Commit 4bc9badd authored by Josh Bleecher Snyder's avatar Josh Bleecher Snyder

cmd/internal/gc: speed up large string switches

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/7698Reviewed-by: 's avatarKeith Randall <khr@golang.org>
parent 6ffed302
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment