Go语言对象跟闭包性能比较

2015-03-09

上篇文章中提到了一下这个问题,但我之前只是写“直觉上认为”,并且还不解释。因为我了解Go的底层,知道方法和闭包分别是怎么实现的,然后用直觉做的判断。但是事后觉得写博客不能这么不严谨,所以做了个测试。

结论大致是对的,但结果也有一些出乎之间意料的地方。

假设我们要实现下面这个接口:

type I interface { XXX() }

分别用闭包和对象的方式实现。闭包或是对象,在这里都是想封装一些状态。用对象的写法:

type Object struct { A int }

func (obj *Object) XXX() { obj.A++ }

用闭包的写法:

func Closure() func() { var A int return func() { A++ } }

为了实现接口I,闭包写法需要一点辅助:

type IFunc func()

func (f IFunc) XXX() { f() }

现在测试:

var ( obj I clo I )

obj = &Object{} clo = IFunc(Closure())

for i := 0; i < 10000000; i++ { obj.XXX() } for i := 0; i < 10000000; i++ { clo.XXX() }

在我的机器上得到的结果:

object: 30.026664ms closure: 54.920985ms

所以说结论大致是对的。

我又测试了一下直接调用Object.XXX()跟Closure()(),两者性能其实没差异。主要问题是在闭包写法多了一层转换步骤,直觉地认为闭包比方法慢是有偏见的。这是我所说的有点出乎意料的地方。

Go语言中,方法在底层的实现,实际上就是普通函数多加了一个参数,将对象作为这个参数。比如上面的

func (obj *Object) XXX() { obj.A++ }

变换为底层的处理就是

func XXX(obj *Object) { obj.A++ }

至于闭包,则是会将一个函数指针和各个upvalue打包到一起。

func Closure() func() { var A int return func() { A++ } }

转化之后等价于

func fun001(A *int) { (*A)++ } func Closure() struct { return &struct{ fun001, new(int), } }

应该说,性能上真的没有差异。可能对象写法比闭包写法对内存更友好一点点。

如果用两种方式都可以实现时,到底是选对象还是闭包呢?如果纠结的话,我还是更推荐对象写法一些。如果不纠结的话,都可以,无所谓。

golangclosureobject