【译文】Go语言性能从 1.0 版到 1.22 版
两年前,我比较了我的 GoAWK 解释器在 Go 1.2 到 1.18 所有版本上的两个不同基准测试。
在本文中,我重新运行了这些基准测试,并添加了缺失的 Go 版本(1.0 和 1.1)以及新版本(1.19 至 1.22)。我还加入了在 Go 1.20 中加入的配置文件引导优化(PGO)的结果。我将引用我原来文章中的相当一部分内容,这样您就不必重新阅读旧文章来理解设置了。
用 Go 编写的程序在很多方面都变得更快:Go 团队和外部贡献者改进了编译器,优化了运行时、垃圾回收器和标准库。在此,我们比较了 GoAWK 在使用 Go 1.0 至 1.22(本文撰写时的最新版本)各发布版本编译时的性能。
我在两个 AWK 程序上运行 GoAWK 进行了测试,这两个程序代表了使用 AWK 所能做的不同极端事情:I/O 与字符串处理,以及数字计算。
首先是 countwords,它是一个字符串处理任务,用于计算输入中单词的频率,并打印出单词及其计数。这是 AWK 脚本的典型任务。输入是《King James Bible》的 10 倍串联版本(我曾用它进行过性能比较)。代码如下
{
for (i=1; i<=NF; i++)
counts[tolower($i)]++
}
END {
for (k in counts)
print k, counts[k]
}
第二个程序是 sumloop,这是一个紧凑的循环,它将循环计数器加到一个变量上多次。这并不是 AWK 的典型用法,但却是 GoAWK 字节码解释器循环的良好测试:
BEGIN {
for (i=0; i<10000000; i++)
sum += i+i+i+i+i
}
我不得不对 GoAWK 的代码稍作调整,以使其能在较旧的 Go 版本上编译。特别是 Go 1.0,因为它没有 bufio.Scanner
,而 GoAWK 大量使用了它。我在 1.0 版中使用了 Go 1.1 版的 bufio.Scanner
实现。
图表中的计时数字是在我的 x86-64 Linux 笔记本电脑上以秒为单位计算的时间(三次运行中最好的一次)。蓝线为 countwords,红线为 sumloop(顺便说一下,我上次把结果标错了)。请注意,这次的 Y 轴是对数轴,以便更清楚地看到最近版本中更细微的改进。
图表中还包括每个golang 版本的 GoAWK 二进制大小–即浅灰色线。
我再次使用 Python 脚本来运行它们并测量时序。下面是图表(如果您喜欢,也可以用表格):
最大的改进来自 1.3、1.5、1.7 和 1.12 版本。在此之后,速度将逐步提升,所有低垂的果实早已被摘取。
这次在go 1.2 中,数词的速度出现了奇怪的提升:从 1.1 版的 7.5 秒提升到 1.2 版的 25.5 秒(!),然后又下降到 1.3 版的 2.8 秒。这几乎可以肯定是堆栈 “热分割 “问题造成的,由于 Go 团队改变了 “goroutine 堆栈的实现,从旧的’分段’模型改为连续模型”,堆栈 “热分割 “问题在 1.3 中得到了解决。
我通过剖析找出了 1.2 版异常的原因,并注意到运行时堆栈操作占了运行时间的很大比例。下面是 pprof 输出的前几行:
$ go tool pprof --text ./goawk_1.2 go12.prof
Total: 1830 samples
332 18.1% 18.1% 332 18.1% runtime.newstack
296 16.2% 34.3% 296 16.2% runtime.memclr
281 15.4% 49.7% 281 15.4% runtime.oldstack
222 12.1% 61.8% 619 33.8% github.com/benhoyt/goawk/interp.(*interp).execute
91 5.0% 66.8% 91 5.0% runtime.lessstack
75 4.1% 70.9% 133 7.3% github.com/benhoyt/goawk/interp.(*interp).callBuiltin
57 3.1% 74.0% 57 3.1% runtime.stackfree
53 2.9% 76.9% 81 4.4% strings.FieldsFunc
...
在使用 Go 1.22 的情况下,PGO 只提高了几个百分点的性能,对 countwords 而言约为 2%,对 sumloop 而言约为 7%。我用 PGO 编译已发布的 GoAWK 二进制文件。
多年来,二进制文件的大小一直保持相当稳定,除了在 1.2 中出现了较大的增长。即使启用了 PGO,二进制文件也只增大了 5%,所以我认为这通常是值得的。
总的来说,countwords 现在的速度是 Go 1.0 的 8 倍,sumloop 是它的 24 倍。感谢 Go 团队多年来的辛勤工作!
本文文字及图片出自 Go performance from version 1.0 to 1.22
你也许感兴趣的:
- Go语言有个“好爹”反而被程序员讨厌?
- 【外评】为什么人们对 Go 1.23 的迭代器设计感到愤怒?
- Go 语言程序员的进化
- 【译文】面试时,有人问我喜欢Go语言什么?
- 4 秒处理 10 亿行数据! Go 语言的 9 大代码方案,一个比一个快
- 【译文】Go语言设计:我们做对了什么,做错了什么
- 最好的 Go 框架就是不用框架?
- 吵翻了!到底该选 Rust 还是 Go,成 2023 年最大技术分歧
- “Go 语言的优点、缺点和平淡无奇之处”的十年
- 为什么不用Go开发操作系统?
你对本文的反应是: