switch在Go语言的性能
2018-01-13
switch 在 Go 的编译器里面几乎没做过什么优化,性能还是挺低下的。
写成这样子:
switch c {
case 0:
case 1:
case 2:
case 3:
...
}
跟直接写成
if c == 1 {
} else if c == 2 {
} else if c == 3 {
}
就没啥太大区别。
也不能责怪编译器蠢,毕竟 Go 语言里面还要支持像 string 类型,支持表达式,以及像这种
switch c.(type)
C 语言里面 switch 语句是会优化成跳转表的,比较高效。
可以考虑换种写法,
var jumpTable []func(){} // 把各个case要做的事情做成函数放到一张表里
f := jumpTable[c] // 直接通过case id找到对应的函数
f() // 并跳转去执行
我测试了一下 Go 的 switch 到底有多么低效,case 稍多,放了32个。
https://gist.github.com/tiancaiamao/28fd53ff9b29eaac30f84b4ba04e2e7e
对比的结果是:
switch cost: 70.476µs jumt table cost: 9.065µs
大部分时候没有写那么多switch case的需求,但是有一个场景还是会用到,就是 interpreter。在 C 里面可以做 threaded code,在 Go 里面做不了。我还是希望做出 Go 能达到的最佳优化,于是把 shen-go 的指令分发从 switch方式切换到了 jump table。
结果很今人失望,性能反而下降了。分析原因,主要是 swtich 的写法可以充分利用到函数内联,比如
switch c {
case opPrimCall:
primCall()
case opPop:
stackPop()
}
这里面的primCall和stackPop这些都是可以inline的,而jump table由于函数地址是动态获取的,利用不到编译器的inline优化。
vm.code[vm.pc](vm)
相当于节省了switch定位具体哪一条操作的开销,却引入了额外的函数调用开销。
有没有什么方法能够既不使用低效的 switch,又能充分利用到函数内联呢?