]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/internal/gc: speed up large string switches
authorJosh Bleecher Snyder <josharian@gmail.com>
Tue, 17 Mar 2015 23:10:31 +0000 (16:10 -0700)
committerJosh Bleecher Snyder <josharian@gmail.com>
Wed, 18 Mar 2015 21:38:40 +0000 (21:38 +0000)
Switch statements do a binary search on long runs of constants.
Doing a less-than comparison on a string is much more expensive
than on (say) an int. Use two part comparison for strings:
First compare length, then the strings themselves.

Benchmarks from issue 10000:

benchmark                  old ns/op     new ns/op     delta
BenchmarkIf0               3.36          3.35          -0.30%
BenchmarkIf1               4.45          4.47          +0.45%
BenchmarkIf2               5.22          5.26          +0.77%
BenchmarkIf3               5.56          5.58          +0.36%
BenchmarkIf4               10.5          10.6          +0.95%
BenchmarkIfNewStr0         5.26          5.30          +0.76%
BenchmarkIfNewStr1         7.19          7.15          -0.56%
BenchmarkIfNewStr2         7.23          7.16          -0.97%
BenchmarkIfNewStr3         7.47          7.43          -0.54%
BenchmarkIfNewStr4         12.4          12.2          -1.61%
BenchmarkSwitch0           9.56          4.24          -55.65%
BenchmarkSwitch1           8.64          5.58          -35.42%
BenchmarkSwitch2           9.38          10.1          +7.68%
BenchmarkSwitch3           8.66          5.00          -42.26%
BenchmarkSwitch4           7.99          8.18          +2.38%
BenchmarkSwitchNewStr0     11.3          6.12          -45.84%
BenchmarkSwitchNewStr1     11.1          8.33          -24.95%
BenchmarkSwitchNewStr2     11.0          11.1          +0.91%
BenchmarkSwitchNewStr3     10.3          6.93          -32.72%
BenchmarkSwitchNewStr4     11.0          11.2          +1.82%

Fixes #10000

Change-Id: Ia2fffc32e9843425374c274064f709ec7ee46d80
Reviewed-on: https://go-review.googlesource.com/7698
Reviewed-by: Keith Randall <khr@golang.org>
src/cmd/internal/gc/swt.go

index c1e0405fdce431e6a79f385c983691f9f60a41a5..991f3ac9042b579b4961c7de6af7dbd267520cff 100644 (file)
@@ -314,7 +314,16 @@ func (s *exprSwitch) walkCases(cc []*caseClause) *Node {
        // find the middle and recur
        half := len(cc) / 2
        a := Nod(OIF, nil, nil)
-       a.Ntest = Nod(OLE, s.exprname, cc[half-1].node.Left)
+       mid := cc[half-1].node.Left
+       le := Nod(OLE, s.exprname, mid)
+       if Isconst(mid, CTSTR) {
+               // Search by length and then by value; see exprcmp.
+               lenlt := Nod(OLT, Nod(OLEN, s.exprname, nil), Nod(OLEN, mid, nil))
+               leneq := Nod(OEQ, Nod(OLEN, s.exprname, nil), Nod(OLEN, mid, nil))
+               a.Ntest = Nod(OOROR, lenlt, Nod(OANDAND, leneq, le))
+       } else {
+               a.Ntest = le
+       }
        typecheck(&a.Ntest, Erv)
        a.Nbody = list1(s.walkCases(cc[:half]))
        a.Nelse = list1(s.walkCases(cc[half:]))
@@ -750,7 +759,19 @@ func exprcmp(c1, c2 *caseClause) int {
        case CTINT, CTRUNE:
                return Mpcmpfixfix(n1.Val.U.Xval, n2.Val.U.Xval)
        case CTSTR:
-               return cmpslit(n1, n2)
+               // Sort strings by length and then by value.
+               // It is much cheaper to compare lengths than values,
+               // and all we need here is consistency.
+               // We respect this sorting in exprSwitch.walkCases.
+               a := n1.Val.U.Sval
+               b := n2.Val.U.Sval
+               if len(a) < len(b) {
+                       return -1
+               }
+               if len(a) > len(b) {
+                       return +1
+               }
+               return stringsCompare(a, b)
        }
 
        return 0