当前位置:首页 > 技术分析 > 正文内容

Go 从版本 1.0 到 1.22的性能变化

ruisui883个月前 (02-07)技术分析13

两年前,我在 1.2 到 1.18 的所有 Go 版本上比较了 GoAWK 解释器的两个不同基准。

在本文中,我重新运行这些基准测试,添加缺少的 Go 版本(1.0 和 1.1)以及新版本(1.19 到 1.22)。我还包含了 Go 1.20 中添加的配置文件引导优化 (PGO) 的结果。我将引用我原来文章中的一些内容,这样您就不必重新阅读旧文章来理解设置。

用 Go 编写的程序可以通过多种方式变得更快:Go 团队和外部贡献者改进了编译器,并优化了运行时、垃圾收集器和标准库。在这里,我们比较了使用 Go 的每个发布版本(从 1.0 到 1.22)(撰写本文时的最新版本)编译时 GoAWK 的性能。

我通过在两个 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 中使用了 bufio.Scanner 的 Go 1.1 实现。

图表中的计时数字是我的 x86-64 Linux 笔记本电脑上的时间(以秒为单位)(三轮运行中最好的)。蓝线是 countwords ,红线是 sumloop (顺便说一句,我上次错误地标记了结果)。请注意,这次 Y 轴是对数的,以便更清楚地看到最近版本中更微妙的改进。

图表中还包括每个 Go 版本的 GoAWK 二进制大小 - 即浅灰色线。

我再次使用 Python 脚本来运行它们并测量时间。这是图表(如果您愿意,也可以作为表格):

最大的改进来自版本 1.3、1.5、1.7 和 1.12。之后,速度会逐渐加快——所有容易实现的目标都早已被采摘了。

这次,Go 1.2 中的 countwords 出现了奇怪的变化:它从 1.1 中的 7.5 秒增加到 1.2 中的 25.5 秒(!),然后在 1.3 中下降到 2.8 秒。这几乎肯定是由堆栈“热分割”问题引起的,该问题在 1.3 中得到了修复,因为 Go 团队将“goroutine 堆栈的实现从旧的‘分段’模型更改为连续模型”。

我通过分析找出了 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 的速度是 Go 1.0 的 24 倍。感谢Go团队多年来的辛勤付出!

扫描二维码推送至手机访问。

版权声明:本文由ruisui88发布,如需转载请注明出处。

本文链接:http://www.ruisui88.com/post/1773.html

标签: pprof
分享给朋友:

“Go 从版本 1.0 到 1.22的性能变化” 的相关文章

vue3中父子组件之间传值的详解

首先我们回顾一下vue2中父子组件是怎么传值的,然后对比vue3进行详解。一、vue2中父子组件传值<!-- 父组件 --> <template> <div> // name:父组件把值传给子组件test-child // childFn:...

gitlab常用命令大全

GitLab常用命令大全GitLab是一个基于Git的Web平台,它不仅提供代码托管,还集成了持续集成/持续交付(CI/CD)、代码审查、问题追踪等功能。在日常使用GitLab的过程中,我们常常需要使用一系列命令来管理代码仓库、处理分支和标签等。以下是GitLab常用的Git命令大全,并附上详细解释...

Git 分支管理策略与工作流程

(预警:因为详细,所以行文有些长,新手边看边操作效果出乎你的预料)团队开发中,遵循一个合理、清晰的Git使用流程,是非常重要的。否则,每个人都提交一堆杂乱无章的commit,项目很快就会变得难以协调和维护。看完这篇文章后,涉及GIT的工作中就会减少因为规范问题导致工作出错,当然如果你现在暂时还未有合...

抖音 Android 性能优化系列:启动优化实践

启动性能是 APP 使用体验的门面,启动过程耗时较长很可能使用户削减使用 APP 的兴趣,抖音通过对启动性能做劣化实验也验证了其对于业务指标有显著影响。抖音有数亿的日活,启动耗时几百毫秒的增长就可能带来成千上万用户的留存缩减,因此,启动性能的优化成为了抖音 Android 基础技术团队在体验优化方向...

el-table内容\n换行解决办法

问题请求到的数据带有换行符 '\n'但页面展示时不换行statusRemark: "\"1、按期完成计划且准确率100%,得100分;\n2、各项目每延误1天,扣1分;每失误1次或者员工投诉1次,扣3分,失误层面达到公司级影响较大的,该项绩效分数为0\"\n&...

学前端,这30个CSS选择器,你必须熟记

你学会了基本的id,class类选择器和descendant后代选择器,然后就觉得完事了吗?如果这样,你就会错过许多灵活运用CSS的机会。虽然本文提到的许多选择器都属于CSS3,并且只能在现代的浏览器中使用,但学会这些是大有好处的。什么是CSS选择器呢?每一条css样式定义由两部分组成,形式如下:[...