Go 從版本 1.0 到 1.22的性能變化
共 2566字,需瀏覽 6分鐘
·
2024-04-18 09:40
兩年前,我在 1.2 到 1.18 的所有 Go 版本上比較了 GoAWK 解釋器的兩個(gè)不同基準(zhǔn)。
在本文中,我重新運(yùn)行這些基準(zhǔn)測試,添加缺少的 Go 版本(1.0 和 1.1)以及新版本(1.19 到 1.22)。我還包含了 Go 1.20 中添加的配置文件引導(dǎo)優(yōu)化 (PGO) 的結(jié)果。我將引用我原來文章中的一些內(nèi)容,這樣您就不必重新閱讀舊文章來理解設(shè)置。
用 Go 編寫的程序可以通過多種方式變得更快:Go 團(tuán)隊(duì)和外部貢獻(xiàn)者改進(jìn)了編譯器,并優(yōu)化了運(yùn)行時(shí)、垃圾收集器和標(biāo)準(zhǔn)庫。在這里,我們比較了使用 Go 的每個(gè)發(fā)布版本(從 1.0 到 1.22)(撰寫本文時(shí)的最新版本)編譯時(shí) GoAWK 的性能。
我通過在兩個(gè) AWK 程序上運(yùn)行 GoAWK 來測試這一點(diǎn),這兩個(gè)程序代表了使用 AWK 可以執(zhí)行的不同極端操作:帶有字符串處理的 I/O 和數(shù)字運(yùn)算。
首先,我們有 countwords ,一個(gè)字符串處理任務(wù),它計(jì)算輸入中單詞的頻率并打印出單詞及其計(jì)數(shù)。這是 AWK 腳本的典型情況。輸入是 King James Bible 的 10 倍串聯(lián)版本(我之前用過它來進(jìn)行性能比較)。代碼如下:
{for (i=1; i<=NF; i++)counts[tolower($i)]++}END {for (k in counts)print k, counts[k]}
第二個(gè)程序是 sumloop ,一個(gè)緊密循環(huán),它將循環(huán)計(jì)數(shù)器多次添加到變量中。這實(shí)際上并不是 AWK 的典型用法,但可以很好地測試 GoAWK 字節(jié)碼解釋器循環(huán):
BEGIN {for (i=0; i<10000000; i++)sum += i+i+i+i+i}
我必須稍微調(diào)整 GoAWK 的代碼才能使其在較舊的 Go 版本上進(jìn)行編譯。特別是對于 Go 1.0,因?yàn)樗鼪]有 bufio.Scanner ,而 GoAWK 大量使用它。我在 1.0 中使用了 bufio.Scanner 的 Go 1.1 實(shí)現(xiàn)。
圖表中的計(jì)時(shí)數(shù)字是我的 x86-64 Linux 筆記本電腦上的時(shí)間(以秒為單位)(三輪運(yùn)行中最好的)。藍(lán)線是 countwords ,紅線是 sumloop (順便說一句,我上次錯(cuò)誤地標(biāo)記了結(jié)果)。請注意,這次 Y 軸是對數(shù)的,以便更清楚地看到最近版本中更微妙的改進(jìn)。
圖表中還包括每個(gè) Go 版本的 GoAWK 二進(jìn)制大小 - 即淺灰色線。
我再次使用 Python 腳本來運(yùn)行它們并測量時(shí)間。這是圖表(如果您愿意,也可以作為表格):
最大的改進(jìn)來自版本 1.3、1.5、1.7 和 1.12。之后,速度會逐漸加快——所有容易實(shí)現(xiàn)的目標(biāo)都早已被采摘了。
這次,Go 1.2 中的 countwords 出現(xiàn)了奇怪的變化:它從 1.1 中的 7.5 秒增加到 1.2 中的 25.5 秒(?。缓笤?1.3 中下降到 2.8 秒。這幾乎肯定是由堆?!盁岱指睢眴栴}引起的,該問題在 1.3 中得到了修復(fù),因?yàn)?Go 團(tuán)隊(duì)將“goroutine 堆棧的實(shí)現(xiàn)從舊的‘分段’模型更改為連續(xù)模型”。
我通過分析找出了 1.2 異常的原因,并注意到運(yùn)行時(shí)堆棧操作占了運(yùn)行時(shí)間的很大一部分。這是 pprof 輸出的前幾行:
go tool pprof --text ./goawk_1.2 go12.profTotal: 1830 samples332 18.1% 18.1% 332 18.1% runtime.newstack296 16.2% 34.3% 296 16.2% runtime.memclr281 15.4% 49.7% 281 15.4% runtime.oldstack222 12.1% 61.8% 619 33.8% github.com/benhoyt/goawk/interp.(*interp).execute91 5.0% 66.8% 91 5.0% runtime.lessstack75 4.1% 70.9% 133 7.3% github.com/benhoyt/goawk/interp.(*interp).callBuiltin57 3.1% 74.0% 57 3.1% runtime.stackfree53 2.9% 76.9% 81 4.4% strings.FieldsFunc...
使用 Go 1.22,PGO 僅提高了幾個(gè)百分點(diǎn)的性能, countwords 大約提高了 2%, sumloop 提高了 7%。我使用 PGO 編譯發(fā)布的 GoAWK 二進(jìn)制文件。
除了 1.2 中的大幅增長之外,多年來二進(jìn)制大小一直保持相當(dāng)穩(wěn)定。即使啟用了 PGO,二進(jìn)制文件也只會大 5% 左右,所以我認(rèn)為這通常是值得的。
總體而言, countwords 現(xiàn)在的速度大約是 Go 1.0 的 8 倍, sumloop 的速度是 Go 1.0 的 24 倍。感謝Go團(tuán)隊(duì)多年來的辛勤付出!
