Performance difference between switch and if

2,135 views
Skip to first unread message

j...@tsp.io

unread,
Feb 25, 2015, 11:52:59 AM2/25/15
to golan...@googlegroups.com
After profiling a project I've been working on for a while, I noticed that I was seeing a number of calls to runtime.cmpstring from the following function:

func (po PtrObject) Get(key string) (v TypedValue, ok bool) {
switch key {
case ".ptr":
return NewPointer(po.Ptr), true
case ".owner":
return NewPrincipal(po.Owner), true
case ".ltime":
return NewInt(int(po.Ltime)), true
case ".anonymity":
return NewInt(int(po.Anonymity)), true
}
v, ok = po.Data[key]
return
}

This struck me as odd, because as far as I can tell, it should only be using runtime.eqstring and runtime.mapaccess2_faststr (both of which also show up in the profile).
After seeing the (admittedly old) issue adding runtime.eqstring, https://code.google.com/p/go/issues/detail?id=1161, I decided to do a small benchmark of if versus switch on strings using the following suite:

$ cat main_test.go 
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)
}

I would think the performance of these two should be nearly identical, but I consistently see the if version outperform the switch version:

$ go test -bench . -benchtime 30s
BenchmarkSwitch 1
100
10000
1000000
100000000
2000000000
2000000000        29.2 ns/op
BenchmarkIf 1
100
10000
1000000
100000000
2000000000
2000000000        22.9 ns/op
ok   x 109.375s

Does anyone know why the if would outperform the switch in this case? Could it be that switch uses cmpstring instead of eqstring? I was unable to get a non-empty profile from these benchmarks, so I can't verify..

Cheers,
Jon

Josh Bleecher Snyder

unread,
Feb 25, 2015, 12:44:27 PM2/25/15
to j...@tsp.io, golang-nuts
> Could it be that switch uses cmpstring instead of eqstring? I was unable to get a non-empty profile from these benchmarks, so I can't verify..

Above three constant cases, the current gc compiler converts a switch
statement from a linear search to a binary search. The calls to
cmpstring are to execute the binary search. (Code: see e.g. exprbsw in
swt.go: https://github.com/golang/go/blob/master/src/cmd/internal/gc/swt.go#L460.)

This could probably be further fine-tuned, although I'm not sure that
it would be considered a priority. Feel free to file an issue:
golang.org/issues/new.

Hope that helps,
Josh

Josh Bleecher Snyder

unread,
Feb 25, 2015, 7:23:47 PM2/25/15
to j...@tsp.io, golang-nuts
On further thought, please do file an issue. I can't promise it'll get
worked on soon, but I think that there can be significant improvement
here, e.g. using divergence indices and/or lengths for the binary
search followed by a final call to eqstring.

-josh

Jon Gjengset

unread,
Feb 25, 2015, 8:40:29 PM2/25/15
to Josh Bleecher Snyder, golang-nuts
> On further thought, please do file an issue.

Filed as https://github.com/golang/go/issues/10000.

Jon

Caleb Spare

unread,
Feb 25, 2015, 8:42:50 PM2/25/15
to Jon Gjengset, Josh Bleecher Snyder, golang-nuts
An auspicious issue number indeed :)


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages