最近在做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)
    }
}

基准测试结果

图1

可以看出,interface传参比指针传参慢4倍。

分析

  1. testFunc1 testFunc2 一个实参直接使用字段、一个实参直接调用函数,结果op一样。说明golang编译器针对实参的函数调用会做一定的优化

    查看汇编可以看到,testFunc1 testFunc2的汇编代码完全一样
    这里写图片描述

  2. testFunc2 testFunc3 都是直接调用函数,区别为testFunc3是interface接口。testFunc3的效率差很多。说明golang编译器无法对interface的函数调用做优化

    查看汇编可以看到,testFunc3 的传参用了130行-139行,而140行-154行用了2个CALL,即简单函数没有被优化
    这里写图片描述

(总而言之,本人在实作中,仅将interface传参改为指针传参,效率提高1倍,减省0.5ms的情况)

结论与应用

  1. go语言interface机制,可以实现多态。因此是很有用的
  2. go语言会对简单函数做C++中类似inline的优化操作
  3. interface参数传递比普通实参要复杂
  4. 对于服务器端代码来说,某些关键代码应该避免使用interface机制
  5. 仅限于那些 每帧都会被反复调用的。即被调用频度最高的那些代码,因尽可能的不要使用interface机制