PGO 是啥,咋就讓 Go 更快更猛了?

大家好,我是煎魚。

Go1.20 即將發佈,近期很多大佬提到一個關鍵詞 PGO,說是有很大的提高,很猛... 稱其爲本迭代(Go1.20)最大的功能特色,這個餅讓我一愣一愣,不禁思考是什麼,咋就很快了。

今天就由煎魚和大家一起學習這個新特性。

快速瞭解

PGO 是什麼

Profile-guided optimization (PGO),翻譯過來是使用配置文件引導的優化。也被稱爲:

PGO 是計算機編程中的一種編譯器優化技術藉助配置文件來引導編譯,達到****提高程序運行時性能的目的。

該項優化是一個通用技術,不侷限於某一門語言。像是我們常見的:

這個優化成績,一聽就很振奮人心。

PGO 怎麼優化

《Intel Developer Guide and Reference》[1] 中對 PGO 的優化和流程有一個基本介紹,如下內容,分享給大家。

PGO 通過縮小代碼大小、減少代碼分支錯誤預測和重新組織代碼佈局以減少指令緩存問題來提高應用程序性能。也可以向編譯器提供有關應用程序中最常執行的區域的信息。

編譯器再通過分析這些信息,編譯器能夠在優化應用程序時更具選擇性和針對性,做出最優的選擇。

PGO 由三個階段組成。如下圖:

這就是 PGO 這項優化的基本過程了。

新提案

背景

提案作者(Cherry Mui、Austin Clements、Michael Pratt)建議向 Go GC 工具鏈增加對配置文件引導優化 (PGO) 的支持。

可以使得 Go tool(工具鏈)能根據運行時信息執行特定於應用程序和工作負載的優化。說明了就是想提高性能,不改業務代碼。

用什麼來做

PGO 需要用戶參與來收集配置文件並將其反饋到構建過程中才能優化,這個前置條件是一個大問題。最符合這個要求的,就是 pprof。

一番討論後,Go 團隊最終也敲定將基於 runtime/pprof 來得到所需 profile,以此來完成 PGO。因爲它符合:採集樣本開銷低、多系統兼容性強、Go 標準且被廣泛使用的基準。

也就是有 runtime/pprof 生成的 profile,就能搞 PGO 了!

支持到什麼程度

PGO 第一個版本將會先支持 pprof CPU,直接讀取 pprof CPU profile 文件來完成優化。預計將在 Go1.20 發佈預覽版本

在 Go 工具鏈上,將在 go build 子命令增加 -pgo=<path>,用於顯式指定用於 PGO 構建的 profile 文件位置。

可能會有同學說,還得顯式指定,太麻煩了?這 Go 團隊也考慮到了...

只需要你將其設置爲:-pgo=auto,就會自動去讀取主目錄下的 profile 文件,非常香!

如果不需要,那就直接 -pgo=off 就能完全關閉 PGO。

Go1.20 實現 PGO 的預覽版本,配置默認爲 off,成熟後會默認爲 auto。

從哪裏先動手

Go 團隊先會專注於 Go 編譯器的開發,畢竟這是萬物的開始,後續會在 cmd/go 做一些簡單的支持。PGO 第一個動手的方向是:函數內聯。這項被認爲性價比是最高的。

未來展望上,還會包含:devirtualization(去虛擬化,一種編譯器優化策略)、特定泛型函數的模板化、基本塊排序和函數佈局。

甚至後續會用於改進內存行爲,例如:改進逃逸行爲和內存分配。

看看這個 PGO 的未來展望 [2],這個餅,我感覺畫的又大又圓(遠)...

超前實踐

以下來自 @Frederic Branczyk 在《Exploring Go's Profile-Guided Optimizations[3]》一文中,提前使用 PGO 對 Go 官方已經開發的函數內聯進行了提前嚐鮮。

步驟如下:

首先拉取已實現的 Go 源碼並進行編譯和導入。如下代碼:

git clone https://go.googlesource.com/go
cd go
git fetch https://go.googlesource.com/go refs/changes/63/429863/3 && git checkout -b change-429863 FETCH_HEAD
cd src
./all.bash
cd ..
export PATH="$(pwd)/bin:$PATH" # or add the path to your bashrc/zshrc

進入到 PGO 的內聯測試代碼:

cd src/cmd/compile/internal/test/testdata/pgo/inline

做提前準備,生成 pprof cpu profile 文件:

go test -o inline_hot.test -bench=. -cpuprofile inline_hot.pprof

完成準備動作後。我們進行兩次測試:一次不用 PGO,一次用 PGO,來進行對比。

不使用 PGO 的情況:

go test -run=none -tags='' -timeout=9m0s -gcflags="-m -m" 2>&| grep "can inline"
./inline_hot.go:15:6: can inline D with cost 7 as: func(uint) int { return int((i + (wSize - 1)) >> lWSize) }
./inline_hot.go:19:6: can inline N with cost 20 as: func(uint) *BS { bs = &BS{...}; return bs }
...

使用 PGO 的情況:

go test -run=none -tags='' -timeout=9m0s -gcflags="-m -m -pgoprofile inline_hot.pprof"

用於如下對比:

go test -o inline_hot.test -bench=. -cpuprofile inline_hot.pprof -count=100 > without_pgo.txt
go test -o inline_hot.test -bench=. -gcflags="-pgoprofile inline_hot.pprof" -count=100 > with_pgo.txt
benchstat without_pgo.txt with_pgo.txt
name  old time/op  new time/op  delta
A-10   960µs ± 2%   950µs ± 1%  -1.05%  (p=0.000 n=98+83)

從結論來看,引入 PGO 後有了 1% 的性能改進。當然,這只是一小段測試代碼。不同的程序結果會不一樣。

總結

PGO 是一門編譯器優化技術,能夠在不改業務代碼的情況下,給你的應用程序帶來一定的性能提升。在 Go PGO 中將會依託 runtime/pprof 所生成的 profile 來完成(需改造),也算是做了一個不錯的串聯。

另外從需求出發點來看,這項優化感覺更多的來自開發同學的興趣優化,官方 issues 中並沒有指出是由於什麼用戶痛點導致的要去開發這項功能。

不過後續如果遇到一些需要進一步優化的 Go 程序,PGO 將會是一個不錯的選擇。畢竟不用改業務代碼。

參考資料

[1]

Intel Developer Guide and Reference: https://www.intel.com/content/www/us/en/develop/documentation/cpp-compiler-developer-guide-and-reference/top/optimization-and-programming/profile-guided-optimization-pgo.html

[2]

未來展望: https://github.com/golang/go/issues/55022#issuecomment-1245605666

[3]

Exploring Go's Profile-Guided Optimizations: https://www.polarsignals.com/blog/posts/2022/09/exploring-go-profile-guided-optimizations/

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/7uobN6DmpIYqG34pOpvvlA