2

【译文】Go语言性能从 1.0 版到 1.22 版

 1 month ago
source link: https://www.techug.com/post/go-performance-from-version-1-0-to-1-22/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

【译文】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 脚本来运行它们并测量时序。下面是图表(如果您喜欢,也可以用表格):

goawk-speed-2024-1000x668.png

最大的改进来自 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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK