-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Description
Consider the following benchmark:
package main
import (
"fmt"
"testing"
)
var words = []string{"super", "califragi", "listic", "expiali", "docius"}
var wlen = len(words)
func BenchmarkSwitch(b *testing.B) {
m := 0
for i := 0; i < b.N; i++ {
switch words[i%wlen] {
case "super":
m++
case "califragi":
m++
case "listic":
m++
case "expiali":
m++
case "docius":
m++
}
}
fmt.Println(m)
}
func BenchmarkIf(b *testing.B) {
m := 0
for i := 0; i < b.N; i++ {
w := words[i%wlen]
if w == "super" {
m++
} else if w == "califragi" {
m++
} else if w == "listic" {
m++
} else if w == "expiali" {
m++
} else if w == "docius" {
m++
}
}
fmt.Println(m)
}
On my machine, this gives
$ go test -bench . -benchtime 30s
BenchmarkSwitch
2000000000 29.2 ns/op
BenchmarkIf
2000000000 22.9 ns/op
ok x 109.375s
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 than runtime.cmpstring
. Second, other optimizations can probably also be implemented here by the compiler. @josharian mentions a few in the golang-nuts thread:
I think that there can be significant improvement here, e.g. using diverge indices and lengths for the binary search followed by a final call to
eqstring
.