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,又能充分利用到函数内联呢?

golang

HNS.to is a highly insecure way of browsing Handshake domains and should only be used for demo or educational purposes. Click to see preferable resolutions methods