【golang】Go 性能优化技巧

逃离我推掉我的手 2022-06-16 02:24 442阅读 0赞

一. array与slice

对于一些初学者,自知道 Go 里面的 array 以 pass-by-value 方式传递后,就莫名地引起 “恐慌”。外加诸多文章未作说明,就建议用 slice 代替 array,企图避免数据拷贝,提升性能。实际上,此做法有待商榷。某些时候怕会适得其反,倒造成不必要的性能损失。

用个简单的示例说明。

  1. package main import ( "fmt" ) const capacity = 1024 func array() [capacity]int { var d [capacity]int for i := 0; i < len(d); i++ { d[i] = 1 } return d } func slice() []int { d := make([]int, capacity) for i := 0; i < len(d); i++ { d[i] = 1 } return d } func main() { fmt.Println(array()) fmt.Println(slice()) }

代码很简单,两个函数分别返回 “内容相同” 的 array 和 slice。为避免编译器优化,特填充了全部数据,以模拟 “真实” 数据复制行为。接下来,看看性能测试对比。

  1. package main import ( "testing" ) func BenchmarkArray(b *testing.B) { for i := 0; i < b.N; i++ { _ = array() } } func BenchmarkSlice(b *testing.B) { for i := 0; i < b.N; i++ { _ = slice() } }

184424_XnkS_2741717.jpg

这结果怕是颠覆了最初认知。array 非但拥有更好的性能,还避免了堆内存分配,也就是说减轻了 GC 压力。为什么会这样?

熟悉汇编的,怕是很容易看出来。函数 array 返回值的复制只需用 “CX + REP” 指令就可完成。

184448_F5kw_2741717.jpg

整个 array 函数完全在栈上完成,而 slice 函数则需执行 makeslice,继而在堆上分配内存,这就是问题所在。

184519_IUCU_2741717.jpg

对于一些短小的对象,复制成本远小于在堆上分配和回收操作。

Go Proverbs: A little copying is better than a little dependency.

二. defer

编译器通过 runtime.deferproc “注册” 延迟调用,除目标函数地址外,还会复制相关参数(包括 receiver)。在函数返回前,执行runtime.deferreturn 提取相关信息执行延迟调用。这其中的代价自然不是普通函数调用一条 CALL 指令所能比拟的。

Center

解决方法么,要么去掉 f.close 前的 defer,要么将内层处理逻辑重构为独立函数(比如匿名函数调用)。

发表评论

表情:
评论列表 (有 0 条评论,442人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Elasticsearch性能优化技巧

    0、背景 在当今世界,各行各业每天都有海量数据产生,为了从这些海量数据中获取想要的分析结果,需要对数据进行提取、转换,存储,维护,管理和分析。 这已然远远超出了普通处...

    相关 Java性能优化技巧

    在我们身边是一大批的程序员,层次不一,但是放眼观,我们很容易就可以看到那些是业务型程序员,那些是有层次的程序员。注重细节,注重性能,做一个有深度的程序员吧! 1. 在明确必要

    相关 js性能优化技巧

    1. 关于JS的循环,循环是一种常用的流程控制。JS提供了三种循环:for(;;)、while()、for(in)。在这三种循环中 for(in)的效率最差,因为它需要查询Ha

    相关 Java性能优化技巧

    大多数开发人员认为性能优化是个比较复杂的问题,需要大量的经验和知识。是的,这并不没有错。诚然,优化应用程序以获得最好的性能并不是一件容易的事情,但这并不意味着你在没有获得这些经