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), } }
应该说,性能上真的没有差异。可能对象写法比闭包写法对内存更友好一点点。
如果用两种方式都可以实现时,到底是选对象还是闭包呢?如果纠结的话,我还是更推荐对象写法一些。如果不纠结的话,都可以,无所谓。