原文在這里,
原文發布于2023年2月8日
在構建Go二進制檔案時,Go編譯器會進行優化,以盡可能生成性能最佳的二進制檔案,例如,常量傳播可以在編譯時對常量運算式進行求值,避免了運行時的計算開銷;逃逸分析可以避免對區域作用域物件進行堆分配,從而減少了垃圾回收的負擔;行內則將簡單函式的代碼體復制到呼叫處,通常能夠進一步優化呼叫處的代碼(例如額外的常量傳播或更好的逃逸分析),
Go在發布的每個版本中都會改進優化,但這并不總是一項容易的任務,某些優化是可調節的,但編譯器不能對每個函式都進行過度激進的優化,因為過于激進的優化實際上可能會損害性能或導致過長的構建時間,其他優化要求編譯器對函式中的“常見”和“不常見”路徑進行判斷,編譯器必須根據靜態啟發式規則進行最佳猜測,因為它無法在運行時知道哪些情況將是常見的,
但現在編譯器可以在運行時知道哪些情況是常見的了,
在沒有關于代碼在生產環境中如何使用的確切資訊的情況下,編譯器只能對包的源代碼進行操作,但是我們確實有一種工具來評估生產行為:性能分析,如果我們向編譯器提供一個性能分析檔案,它就可以做出更明智的決策:對最常用的函式進行更積極的優化,或更準確地選擇常見情況,
使用應用程式行為的性能分析檔案進行編譯器優化的方法被稱為基于性能分析的優化(Profile-Guided Optimization,簡稱PGO,也被稱為反饋導向優化(Feedback-Directed Optimization,簡稱FDO)),
PGO/FDO通過收集和分析運行時的性能資料,使得編譯器能夠更準確地了解代碼的執行特性,從而進行更精細的優化,通過結合靜態分析和動態運行時資料,PGO/FDO可以產生更優化的代碼,提高程式的性能和效率,這種技術在提高大型復雜應用程式的性能方面非常有用,特別是對于高度頻繁執行的代碼路徑進行優化,
Go 1.20中包含了PGO的初步支持,作為預覽版本提供,請參閱profile-guided optimization user guide以獲取完整的檔案,盡管距離在生產環境中使用還有一段距離,但仍希望大家在作業中使用,并反饋遇到的問題或意見,
示例
以Markdown轉HTML服務為例:用戶通過/render上傳Markdown檔案,然后接收轉換后的HTML檔案,這里使用gitlab.com/golang-commonmark/markdown,
創建專案
$ go mod init example.com/markdown
$ go get gitlab.com/golang-commonmark/markdown
main.go內容:
package main
import (
"bytes"
"io"
"log"
"net/http"
_ "net/http/pprof"
"gitlab.com/golang-commonmark/markdown"
)
func render(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Only POST allowed", http.StatusMethodNotAllowed)
return
}
src, err := io.ReadAll(r.Body)
if err != nil {
log.Printf("error reading body: %v", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
md := markdown.New(
markdown.XHTMLOutput(true),
markdown.Typographer(true),
markdown.Linkify(true),
markdown.Tables(true),
)
var buf bytes.Buffer
if err := md.Render(&buf, src); err != nil {
log.Printf("error converting markdown: %v", err)
http.Error(w, "Malformed markdown", http.StatusBadRequest)
return
}
if _, err := io.Copy(w, &buf); err != nil {
log.Printf("error writing response: %v", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
}
func main() {
http.HandleFunc("/render", render)
log.Printf("Serving on port 8080...")
log.Fatal(http.ListenAndServe(":8080", nil))
}
啟動服務:
$ go build -o markdown.nopgo
$ ./markdown.nopgo
2023/06/25 11:27:13 Serving on port 8080...
使用Go專案的README來進行測驗:
$ curl -o README.md -L "https://raw.githubusercontent.com/golang/go/c16c2c49e2fa98ae551fc6335215fadd62d33542/README.md"
$ curl --data-binary @README.md http://localhost:8080/render
<h1>The Go Programming Language</h1>
<p>Go is an open source programming language that makes it easy to build simple,
reliable, and efficient software.</p>
...
<p>Note that the Go project uses the issue tracker for bug reports and
proposals only. See <a href="https://go.dev/wiki/Questions">https://go.dev/wiki/Questions</a> for a list of
places to ask questions about the Go language.</p>
性能分析
現在我們來采集一個profile檔案,再使用PGO來重新構建服務,看看性能能提升多少,
在main.go中,我們匯入了net/http/pprof包,它會自動為服務器添加一個/debug/pprof/profile地址,用于獲取CPU分析資料,
通常情況下,我們都是從生產環境中收集性能分析資料,以便編譯器能夠獲取在實際生產環境中的行為情況,但這個示例沒有一個真實的“生產”環境,我們將創建一個簡單的程式來生成負載,同時收集性能分析資料,將該程式的原始碼復制到load/main.go,并啟動負載生成器(確保服務器仍在運行!),
$ go run example.com/markdown/load
下載性能分析檔案:
$ curl -o cpu.pprof "http://localhost:8080/debug/pprof/profile?seconds=30"
下載完成后,關閉服務,
啟用PGO
我們可以使用go build命令的-pgo標志要求Go工具鏈使用PGO進行構建,-pgo標志可以接受以下兩種引數:
- 指定要使用的性能分析檔案的路徑
- 使用"auto",它將使用主包目錄中的default.pgo檔案
我們建議將default.pgo性能分析檔案提交到你的代碼倉庫中,將性能分析檔案與源代碼放在一起,可以確保用戶只需獲取代碼倉庫(無論是通過版本控制系統還是通過go get命令),就能自動獲得性能分析檔案,并且構建程序仍然可重現,在Go 1.20中,默認的-pgo選項是off,因此用戶仍需要添加-pgo=auto選項,但預計將來的Go版本將把默認值改為-pgo=auto,這樣任何構建該二進制檔案的人都將獲得PGO的好處,
構建:
$ mv cpu.pprof default.pgo
$ go build -pgo=auto -o markdown.withpgo
性能對比
我們將使用一個基于Go的基準測驗版本的負載生成器來評估PGO對性能的影響,將這個基準測驗的代碼復制到load/bench_test.go檔案中,
首先沒有使用PGO的情況下進行測驗:
$ ./markdown.nopgo
進行測驗:
$ go test example.com/markdown/load -bench=. -count=100 -source ../README.md > nopgo.txt
然后啟用PGO:
$ ./markdown.withpgo
進行測驗:
$ go test example.com/markdown/load -bench=. -count=100 -source ../README.md > withpgo.txt
運行結束后進行結果對比:
$ go install golang.org/x/perf/cmd/benchstat@latest
$ benchstat nopgo.txt withpgo.txt
goos: linux
goarch: amd64
pkg: example.com/markdown/load
cpu: Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz
│ nopgo.txt │ withpgo.txt │
│ sec/op │ sec/op vs base │
Load-8 445.1μ ± 4% 408.6μ ± 2% -8.21% (p=0.000 n=100)
新版本大約快了8.2%!在Go 1.20中,通過啟用PGO,可以獲得2%到4%的CPU使用率提升,性能分析檔案包含了關于應用程式行為的豐富資訊,而Go 1.20僅僅開始利用這些資訊進行行內優化,未來的發布版本將繼續改進性能,因為編譯器的更多部分將利用PGO帶來的好處,
原文中效率提升了2.6%
文中的代碼可以在這里找到,
宣告:本作品采用署名-非商業性使用-相同方式共享 4.0 國際 (CC BY-NC-SA 4.0)進行許可,使用時請注明出處,
Author: mengbin
blog: mengbin
Github: mengbin92
cnblogs: 戀水無意
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/555909.html
標籤:其他
上一篇:記錄一個在寫專案中遇到的Maven依賴無法匯入的問題
下一篇:返回列表
