最近在做C++代码到Go代码的翻译中,发现不少Go语言性能上与C++代码的差距。
本文介绍下 Interface接口传参的性能问题。
测试代码
package benchmarks
import "testing"
type ITestObj interface {
GetPropX() int
SetPropX(x int)
}
type Obj struct {
X int
}
func (this *Obj) GetPropX() int {
return this.X
}
func (this *Obj) SetPropX(x int) {
this.X = x
}
func testFunc1(obj *Obj) {
obj.X = obj.X + 1
}
func testFunc2(obj *Obj) {
obj.SetPropX(obj.GetPropX() + 1)
}
func testFunc3(obj ITestObj) {
obj.SetPropX(obj.GetPropX() + 1)
}
var myobj1 Obj
func Benchmark_Ptr(t *testing.B) {
for i := 0; i < t.N; i++ {
testFunc1(&myobj1)
}
}
func Benchmark_PtrAndCallF(t *testing.B) {
for i := 0; i < t.N; i++ {
testFunc2(&myobj1)
}
}
func Benchmark_Interface(t *testing.B) {
for i := 0; i < t.N; i++ {
testFunc3(&myobj1)
}
}
基准测试结果
可以看出,interface传参比指针传参慢4倍。
分析
testFunc1 testFunc2 一个实参直接使用字段、一个实参直接调用函数,结果op一样。说明golang编译器针对实参的函数调用会做一定的优化
查看汇编可以看到,testFunc1 testFunc2的汇编代码完全一样
testFunc2 testFunc3 都是直接调用函数,区别为testFunc3是interface接口。testFunc3的效率差很多。说明golang编译器无法对interface的函数调用做优化
查看汇编可以看到,testFunc3 的传参用了130行-139行,而140行-154行用了2个CALL,即简单函数没有被优化
(总而言之,本人在实作中,仅将interface传参改为指针传参,效率提高1倍,减省0.5ms的情况)
结论与应用
- go语言interface机制,可以实现多态。因此是很有用的
- go语言会对简单函数做C++中类似inline的优化操作
- interface参数传递比普通实参要复杂
- 对于服务器端代码来说,某些关键代码应该避免使用interface机制
- 仅限于那些 每帧都会被反复调用的。即被调用频度最高的那些代码,因尽可能的不要使用interface机制