-
Notifications
You must be signed in to change notification settings - Fork 17.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cmd/gc: optimize switch statements with string literal cases #10000
Comments
To expand on my last comment: I think that we should first sort (and binary search) by string length. It is readily available in the string header, registerizable, and cheap to compare. If we wanted to optimize further, instead of sorting (and searching) strings of the same length using cmpstring, we could instead further sort (and binary search) by the indices at which the strings vary--effectively doing something like walking a trie. I think that using the string length is worth doing right way. It should be pretty simple and a quick win. The fancier approach I'm less sure about, at least initially, since cmpstring is written in pretty heavily optimized assembly. |
Started, but waiting for switch code cleanup before mailing to avoid churn. |
The benchmark here has some quirks:
Here is a set of benchmarks that provides more visibility into exactly what is going on: package main
import "testing"
var words = [...]string{"super", "califragi", "listic", "expiali", "docius"}
func benchmarkSwitch(b *testing.B, s string) {
m := 0
for i := 0; i < b.N; i++ {
switch s {
case "super":
m++
case "califragi":
m++
case "listic":
m++
case "expiali":
m++
case "docius":
m++
}
}
}
func BenchmarkSwitch0(b *testing.B) { benchmarkSwitch(b, words[0]) }
func BenchmarkSwitch1(b *testing.B) { benchmarkSwitch(b, words[1]) }
func BenchmarkSwitch2(b *testing.B) { benchmarkSwitch(b, words[2]) }
func BenchmarkSwitch3(b *testing.B) { benchmarkSwitch(b, words[3]) }
func BenchmarkSwitch4(b *testing.B) { benchmarkSwitch(b, words[4]) }
func BenchmarkSwitchNewStr0(b *testing.B) { benchmarkSwitch(b, string([]byte(words[0]))) }
func BenchmarkSwitchNewStr1(b *testing.B) { benchmarkSwitch(b, string([]byte(words[1]))) }
func BenchmarkSwitchNewStr2(b *testing.B) { benchmarkSwitch(b, string([]byte(words[2]))) }
func BenchmarkSwitchNewStr3(b *testing.B) { benchmarkSwitch(b, string([]byte(words[3]))) }
func BenchmarkSwitchNewStr4(b *testing.B) { benchmarkSwitch(b, string([]byte(words[4]))) }
func benchmarkIf(b *testing.B, s string) {
m := 0
for i := 0; i < b.N; i++ {
if s == "super" {
m++
} else if s == "califragi" {
m++
} else if s == "listic" {
m++
} else if s == "expiali" {
m++
} else if s == "docius" {
m++
}
}
}
func BenchmarkIf0(b *testing.B) { benchmarkIf(b, words[0]) }
func BenchmarkIf1(b *testing.B) { benchmarkIf(b, words[1]) }
func BenchmarkIf2(b *testing.B) { benchmarkIf(b, words[2]) }
func BenchmarkIf3(b *testing.B) { benchmarkIf(b, words[3]) }
func BenchmarkIf4(b *testing.B) { benchmarkIf(b, words[4]) }
func BenchmarkIfNewStr0(b *testing.B) { benchmarkIf(b, string([]byte(words[0]))) }
func BenchmarkIfNewStr1(b *testing.B) { benchmarkIf(b, string([]byte(words[1]))) }
func BenchmarkIfNewStr2(b *testing.B) { benchmarkIf(b, string([]byte(words[2]))) }
func BenchmarkIfNewStr3(b *testing.B) { benchmarkIf(b, string([]byte(words[3]))) }
func BenchmarkIfNewStr4(b *testing.B) { benchmarkIf(b, string([]byte(words[4]))) } Here's the result of running these benchmarks:
|
Mailed golang.org/cl/7698 |
Consider the following benchmark:
On my machine, this gives
Despite the switch statement having more opportunity for optimization, it is consistently slower than the if variant. After a brief discussion on golang-nuts, it was pointed out that Go will move to a binary search for switch statements with more than three constant cases. Firstly, this threshold seems too low, as
runtime.eqstring
(because of length checks) is often an order of magnitude faster thanruntime.cmpstring
. Second, other optimizations can probably also be implemented here by the compiler. @josharian mentions a few in the golang-nuts thread:The text was updated successfully, but these errors were encountered: