# Benchmark switch, binary search and if-else

Just for curiosity I have done some benchmark for testing the best performance between switch, binary-search, and if-else statement. here is the code :

package switchStatement

score int
}

}

}

//search score using binary search
if index == -1 {
}
}

low := 0
middle := (high + low) / 2

for low <= high {
//checking to avoid index out of range
return middle
}

return middle
} else if badges[middle].score >= score {
high = middle - 1

} else {
low = middle + 1
}

middle = (high + low) / 2
}

return -1
}

switch {
case score >= 500001:
return "diamond-5.gif"
case score >= 200001:
return "diamond-4.gif"
case score >= 150001:
return "diamond-3.gif"
case score >= 100001:
return "diamond-2.gif"
case score >= 50001:
return "diamond-1.gif"
case score >= 45001:
return "gold-5.gif"
case score >= 30001:
return "gold-4.gif"
case score >= 15001:
return "gold-3.gif"
case score >= 10001:
return "gold-2.gif"
case score >= 4501:
return "gold-1.gif"
default:
}
}

if score >= 50001 { // Diamond
if score >= 500001 {
} else if score >= 200001 {
} else if score >= 150001 {
} else if score >= 100001 {
}
} else if score >= 4501 { // Gold
if score >= 45001 {
} else if score >= 30001 {
} else if score >= 15001 {
} else if score >= 10001 {
}
}

}


And I write some test and benchmark :

package switchStatement

import (
"testing"
)

t.Error("Expected badges-off.jpg got = ", result)
}
}

func TestGetReputationImprove(testing *testing.T) {

//using table drive test or tt
var tt = []struct {
input    int
expected string
}{
{4501, "gold-1.gif"},
{4561, "gold-1.gif"},
{13001, "gold-2.gif"},
{104001, "diamond-2.gif"},
{594401, "diamond-5.gif"},
}

for _, t := range tt {
if actual != t.expected {
testing.Errorf("Test failed expected : %s, actual : %s", t.expected, actual)
}
}
}

//binary search
func benchmarkGetReputation(input int, b *testing.B) {
for n := 0; n < b.N; n++ {
}
}

//switch
func benchmarkGetReputationSwitch(input int, b *testing.B) {
for n := 0; n < b.N; n++ {
}
}

//if-else statement
func benchmarkGetReputationIf(input int, b *testing.B) {
for n := 0; n < b.N; n++ {
}
}

//benchmark using binary search
func BenchmarkGetReputation1(b *testing.B) { benchmarkGetReputation(10, b) }
func BenchmarkGetReputation2(b *testing.B) { benchmarkGetReputation(4555, b) }
func BenchmarkGetReputation3(b *testing.B) { benchmarkGetReputation(14555, b) }
func BenchmarkGetReputation4(b *testing.B) { benchmarkGetReputation(504001, b) }

//benchmark using switch
func BenchmarkGetReputation1Switch(b *testing.B) { benchmarkGetReputationSwitch(10, b) }
func BenchmarkGetReputation2Switch(b *testing.B) { benchmarkGetReputationSwitch(4555, b) }
func BenchmarkGetReputation3Switch(b *testing.B) { benchmarkGetReputationSwitch(14555, b) }
func BenchmarkGetReputation4Switch(b *testing.B) { benchmarkGetReputationSwitch(504001, b) }

//benchmark if-else statemen
func BenchmarkGetReputation1If(b *testing.B) { benchmarkGetReputation(10, b) }
func BenchmarkGetReputation2If(b *testing.B) { benchmarkGetReputation(4555, b) }
func BenchmarkGetReputation3If(b *testing.B) { benchmarkGetReputation(14555, b) }
func BenchmarkGetReputation4If(b *testing.B) { benchmarkGetReputation(504001, b) }


This is the result from my benchmark test :

BenchmarkGetReputation1-4           20000000           114 ns/op
BenchmarkGetReputation2-4           10000000           114 ns/op
BenchmarkGetReputation3-4           10000000           113 ns/op
BenchmarkGetReputation4-4           10000000           118 ns/op
BenchmarkGetReputation1Switch-4     2000000000           1.80 ns/op
BenchmarkGetReputation2Switch-4     2000000000           1.80 ns/op
BenchmarkGetReputation3Switch-4     1000000000           2.16 ns/op
BenchmarkGetReputation4Switch-4     2000000000           0.36 ns/op
BenchmarkGetReputation1If-4         10000000           137 ns/op
BenchmarkGetReputation2If-4         10000000           129 ns/op
BenchmarkGetReputation3If-4         10000000           112 ns/op
BenchmarkGetReputation4If-4         10000000           118 ns/op
PASS
ok      github.com/hello/switchStatement    22.554s


from there I can conclude that switch is the winner here. Any improvement for the test or the code to get a better result?

Note : you can find the code here

In GetReputationBadge, you're needlessly allocating strings by modifying the badge variable. It would be a fairer benchmark to write it in the following way:

func GetReputationBadge(score int) string {
if score >= 500001 {
return "diamond-5.gif"
}
if score >= 200001 {
return "diamond-4.gif"
}
// etc.
if score >= 4501 {
return "gold-1.gif"
}
}


(That said, I'm not convinced it matters — the compiler should probably optimize this anyway.)

I tried it and was very surprised to see that the benchmarks were still very poor. Turns out, you called the wrong function in your benchmark :-) BenchmarkGetReputation1If calls benchmarkGetReputation insead of benchmarkGetReputationIf. Retrying it with this fixed leads to the expected result:

BenchmarkGetReputation1-4           10000000       188 ns/op
BenchmarkGetReputation2-4           10000000       182 ns/op
BenchmarkGetReputation3-4           10000000       179 ns/op
BenchmarkGetReputation4-4           10000000       192 ns/op
BenchmarkGetReputation1Switch-4     300000000         4.55 ns/op
BenchmarkGetReputation2Switch-4     300000000         4.32 ns/op
BenchmarkGetReputation3Switch-4     300000000         4.63 ns/op
BenchmarkGetReputation4Switch-4     2000000000         0.84 ns/op
BenchmarkGetReputation1If-4         300000000         4.21 ns/op
BenchmarkGetReputation2If-4         300000000         4.19 ns/op
BenchmarkGetReputation3If-4         300000000         4.74 ns/op
BenchmarkGetReputation4If-4         2000000000         0.88 ns/op


Note that this benchmark is still pretty flawed: you're only testing it with a couple of values, so depending on which they are, the order of the if/else statements are going to matter. You should use much more values, picked among a reasonable distribution instead.

• thank you for correcting the if-else statement. so it turns out the switch and if-else here is the same in terms of performance? – Gujarat Santana Dec 10 '16 at 15:17
• Yes; which I don't find surprising — they're pretty much the same thing with some syntaxic sugar. Ashwini's mentions that the switch is doing a binary search; but I don't expect this to change benchmarks significantly for switch statements with very simple conditions and less than a few dozen cases. – Ted Dec 10 '16 at 15:24
• alright I will give this a try and let you know, I will add some more conditions to it and see the difference. For example above it only mention for gold badges. I will try with silver, bronze, etc – Gujarat Santana Dec 10 '16 at 15:28
• your benchmark and my benchmark is kinda different, I've got the same result as before when using if-else just like your suggestion. I will post the code in my github – Gujarat Santana Dec 10 '16 at 22:40