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.codevm.pc

相当于节省了switch定位具体哪一条操作的开销,却引入了额外的函数调用开销。

有没有什么方法能够既不使用低效的 switch,又能充分利用到函数内联呢?

golang