Go1.20で入ったPGO(Profile-guided Optimization Preview)を試してみた
Table of Contents
概要
PGOはコンパイラ最適化手法の1つ。実世界で利用された統計データをもとに、最適化を実施する手法。RustやChromeでは既に取り入れられている。
参考リンク
- https://go.dev/doc/pgo
- https://go.dev/blog/pgo-preview
- https://rhysd.hatenablog.com/entry/2020/06/01/204423
Go Blogにしたがって試す
- 手元で動かしたコードは https://github.com/task4233/go-pgo-test に
- Goコンパイラは最適化してバイナリを作成する
- 最適化の手法
- Goはリリースごとに最適化を改善するが非常に困難
- 過度な最適化はパフォーマンスの低下やコンパイル時間の増加に繋がる
- 全ての関数を最大限まで最適化することは難しい
- コンパイラはcommonパスとuncommonパスを判断する必要があり、静的ヒューリスティックに基づいて推測を行う必要がある
- 過度な最適化はパフォーマンスの低下やコンパイル時間の増加に繋がる
- コンパイラは、コードがどのように動作するか把握できない
- プロファイラによって本番環境の動作を評価できる
- 最も頻繁に利用される関数をより積極的に最適化することができる
- コンパイラ最適化のために、アプリケーション動作のプロファイルを使用することでこの問題に対処する
- これをPGO(Profile-Guided Optimazation)、もしくはFDO(Feedback-Directed Optimization)と呼ぶ
- サンプルプログラム
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))
}
- profileの収集
go run example.com/markdown/load &
curl -o cpu.pprof "http://localhost:8080/debug/pprof/profile?seconds=30"
- PGOの利用
-pgo
フラッグをbuild時に設定すれば良い- Go1.20では
-pgo=off
がデフォルト設定 - 将来的には
-pgo=auto
がデフォルト設定になる - デフォルトでは、mainパッケージのあるディレクトリの
default.pgo
が利用される
- benchstatを利用した比較
make bench
goos: darwin
goarch: arm64
pkg: github.com/task4233/pgo-test/load
│ ./log/nopgo.txt │ ./log/withpgo.txt │
│ sec/op │ sec/op vs base │
Load-8 85.37µ ± 1% 83.36µ ± 2% -2.35% (p=0.001 n=20)