Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
PGOによるコンパイラ最適化 / Compiler Optimization with PGO
Search
Koya IWAMURA
March 07, 2023
Programming
1k
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
PGOによるコンパイラ最適化 / Compiler Optimization with PGO
Koya IWAMURA
March 07, 2023
More Decks by Koya IWAMURA
See All by Koya IWAMURA
What's new in Go 1.26?
ciarana
2
430
What's new in Go 1.25?
ciarana
1
110
What's new in Go 1.24?
ciarana
1
270
What's new in Go 1.21?
ciarana
2
1.7k
Go1.13以後のエラーハンドリングについて語ろう / Let's talk about error handling after Go 1 13
ciarana
10
8.8k
Other Decks in Programming
See All in Programming
メソッドのジェネリクスでGoの夢は広がるか? / Kyoto.go #65
utgwkk
3
840
CSC307 Lecture 17
javiergs
PRO
0
320
ふつうのFeature Flag実践入門
irof
8
4k
AIとASP.NET Coreで雑Webアプリを作った話
mayuki
0
660
生成AI時代にこそ効くGo | Why Go Works in the Age of Generative AI
mom0tomo
8
3.3k
AI時代のUIはどこへ行く?その2!
yusukebe
22
7.4k
エージェンティックRAGにAWSで入門しよう!
har1101
8
1.7k
Vue × Nuxt × Oxc どこまで使える?実運用の現在地
andpad
0
270
RTSPクライアントを自作してみた話
simotin13
0
610
技術記事、AIに書かせるか、自分で書くか? 〜それでも私が自分の手で書く理由〜 / #QiitaConference
jnchito
2
1.4k
Creating Composable Callables in Contemporary C++
rollbear
0
150
ローカルLLMでどこまでコードが書けるか -拡張版 / How much code can be written on a local LLM Extended
kishida
11
4.3k
Featured
See All Featured
The agentic SEO stack - context over prompts
schlessera
0
820
The B2B funnel & how to create a winning content strategy
katarinadahlin
PRO
1
390
Paper Plane
katiecoart
PRO
1
51k
Rebuilding a faster, lazier Slack
samanthasiow
85
9.5k
Taking LLMs out of the black box: A practical guide to human-in-the-loop distillation
inesmontani
PRO
3
2.3k
HU Berlin: Industrial-Strength Natural Language Processing with spaCy and Prodigy
inesmontani
PRO
0
410
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
200
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
850
Have SEOs Ruined the Internet? - User Awareness of SEO in 2025
akashhashmi
0
370
Game over? The fight for quality and originality in the time of robots
wayneb77
1
200
The Director’s Chair: Orchestrating AI for Truly Effective Learning
tmiket
1
200
Learning to Love Humans: Emotional Interface Design
aarron
275
41k
Transcript
PGOによる コンパイラ最適化 シアラナ Go 1.20 Release Party 2023-02-21
自己紹介 • シアラナ ◦ Twitter: cia_rana / Zenn: koya_iwamura •
株式会社アプリボット ◦ 新規ゲームプロジェクトでサーバーサイドエンジニア • Goの記事書いたり、コントリビュートしたり
目次 • 通常のコンパイラ最適化とPGOによるコンパイラ最適化 • PGOの使用例 & ベンチマーク • PGO FAQ
• PGOの今後
通常のコンパイラ最適化 • インライン展開 ◦ 関数の呼び出し元に関数の呼び出し先を展開する • エスケープ解析 ◦ 変数の値をヒープに退避しなくて良いか判定する •
devirtualization ◦ interfaceのメソッド呼び出しを具象型のメソッド呼び出しに変換する • SSA最適化 ◦ SSA: ASTからマシンコードに変換するまでの間の中間コード ◦ $ go tool compile -d ssa/help で詳細が見れます
通常のコンパイラ最適化 • 一見さまざまな最適化が施されているように見える... • ただ、これはあくまでも動作させる前のコードを静的解析し、 決められたルールに従って最適化を行っているに過ぎない
PGOによるコンパイラ最適化 • PGO: Profile-Guided Optimization • ランタイム時のCPUやRAMの利用状況、関数呼び出しの頻度などを収集し、 次回のコンパイラ最適化にフィードバックする • 実際のワークロードにより特化したコンパイラ最適化を行える
• いわば「推測するな、計測せよ」に則った最適化手法
Go 1.20 で導入された PGO • 2~4%の実行速度高速化が見込めるらしい • 今回入ったのはインライン最適化のみ ◦ その他の最適化については最後の方で
• PGO で使用するプロファイルはpprofのプロファイル結果を用いる ◦ pprofのプロファイル結果には PGOに必要な情報が含まれていなかったり、 PGOに不必要な 情報が多くファイルサイズが大きくなる傾向があり、今後別の形式が導入される可能性が あることが示唆されている(確度はまだ低い) ◦ 別の形式が定義される場合は pprofのプロファイル結果から変換するツールを提供する とのこと ◦ https://github.com/golang/go/issues/55022#issuecomment-1244259222 • 今回のリリースではまだパブリックプレビュー版なので、本番環境での利用は避け る
PGO 使用例 & ベンチマーク
実行環境 • OS: macOS Monterey • CPU: Intel Core i9,
8core, 2.4GHz • RAM: 32GB • Go: 1.20.0
使用するコード • GopherCon 2019で行われた「High Performance Go Workshop」の mandelwebを用いて、ベンチマークを取ってみる • mandelwebはマンデルブロ集合を生成するWebサーバー
https://dave.cheney.net/high-performance-go-workshop/dotgo-paris.html
1. プロファイルを取る手段を選択する プロファイルの取り方は大きく分けて3つ • github.com/pkg/profile パッケージを用いる • net/http/pprof パッケージを用いる •
ベンチマークで取得する
1. プロファイルを取る手段を選択する プロファイルの取り方は大きく分けて3つ • github.com/pkg/profile パッケージを用いる • net/http/pprof パッケージを用いる •
ベンチマークで取得する
2. PGOを無効にしてビルドする 現在のディレクトリ構成 $ tree ├── cmd │ └── mandelweb
│ ├── benchmark_test.go │ └── main.go └── go.mod
2. PGOを無効にしてビルドする $ go build -pgo=off -o mandelweb_nopgo ./cmd/mandelweb/ •
Go 1.20から追加されたビルドフラグ • offに設定することでプロファイルを明示的に使わない設定にできる
2. PGOを無効にしてビルドする $ ./mandelweb_nopgo • PGOを無効にしてビルドした Webサーバ • ポート8080で待機中 •
エンドポイント /mandelbrot でマンデルブロ集合を描画して返す
3. ベンチマークを取る(PGO無効) Webサーバーが起動しているターミナルとは別ターミナルでベンチマークを取る $ go test -bench=. -count=30 ./cmd/mandelweb/benchmark_test.go >
benchmark_nopgo.txt • PGOを無効にしてビルドした Webサーバーに対してリクエストを 送るベンチマーク func Benchmark(b *testing.B) { for i := 0; i < b.N; i++ { resp, err := http.Get("http://127.0.0.1:8080/mandelbrot") if err != nil { b.Fatal(err.Error()) } resp.Body.Close() } }
4. プロファイルを取る Apache Bench で負荷をかける $ ab -n 1000 -c
10 http://127.0.0.1:8080/mandelbrot • -n: 最大リクエスト数 • -c: 同時リクエスト数 • つまり10並列で合計1000回リクエストする
4. プロファイルを取る 負荷がかかっている間にプロファイルを取る $ curl -o cmd/mandelweb/default.pgo http://127.0.0.1:8080/debug/pprof/profile?seconds=10 • 保存するファイル名
• main パッケージと同じディレ クトリに保存する • 10秒間プロファイルを取る • 内部で net/http/pprof が 使用されている
4. プロファイルを取る 現在のディレクトリ構成 $ tree ├── benchmark_nopgo.txt ├── cmd │
└── mandelweb │ ├── benchmark_test.go │ ├── default.pgo │ └── main.go ├── go.mod └── mandelweb_nopgo
5. PGOを有効にしてビルドする $ go build -pgo=auto -o mandelweb_pgo ./cmd/mandelweb/ •
無効時はoffを設定していたが、有効時は autoを設定する • autoにするとmainパッケージのdefault.pgoを探索して使用する • -pgo=./cmd/mandelweb/default.pgo のようにファイルパスを 指定することもできる • Go 1.20ではデフォルトでoffだが、将来的にデフォルトで autoに なることが示唆されている
5. PGOを有効にしてビルドする $ ./mandelweb_pgo • PGOを有効にしてビルドした Webサーバを起動 • 挙動はPGOを無効にしてビルドした mandelweb_nopgoと同じ
6. ベンチマークを取る(PGO有効) Webサーバーを起動しているターミナルとは別ターミナルでベンチマークを取る $ go test -bench=. -count=30 ./cmd/mandelweb/benchmark_test.go >
benchmark_pgo.txt • PGOを有効にしてビルドした Webサーバーに対してリクエストを 送るベンチマーク • ベンチマーカーの中身自体は PGOを 無効にしていたときと同じ
7. ベンチマーク結果を比較する PGOを無効・有効にしたWebサーバーに対しそれぞれベンチマークを 行なった結果を benchstat で比較する $ benchstat benchmark_nopgo.txt benchmark_pgo.txt
• go install golang.org/x/perf/cmd/benchstat@latest でインストール
7. ベンチマーク結果を比較する PGOを有効にすると逆に実行時間が増えてしまった😇 benchmark_nopgo.txt benchmark_pgo.txt vs 41.62 msec/op ± 2%
42.63 msec/op ± 1% +2.41% (p=0.000 n=30)
7. ベンチマーク結果を比較する コンパイラ最適化結果を比較してみる $ go build -pgo=off -o mandelbrot_nopgo -gcflags="-m"
./cmd/mandelweb/ 2> opt_nopgo.txt $ go build -pgo=auto -o mandelbrot_pgo -gcflags="-m" ./cmd/mandelweb/ 2> opt_pgo.txt • ビルド時に-gcflagsに-mをつけると、 最適化結果を出力してくれる
7. ベンチマーク結果を比較する コンパイラ最適化結果を比較してみる $ diff opt_nopgo.txt opt_pgo.txt > cmd/mandelweb/main.go:59:15: inlining
call to fillPixel func fillPixel(m *Img, x, y int) { const n = 1000 const Limit = 2.0 const Zoom = 4 Zr, Zi, Tr, Ti := 0.0, 0.0, 0.0, 0.0 Cr := Zoom*float64(x)/float64(n) - 1.5 Ci := Zoom*float64(y)/float64(n) - 1.0 for i := 0; i < n && (Tr+Ti <= Limit*Limit); i++ { Zi = 2*Zr*Zi + Ci Zr = Tr - Ti + Cr Tr = Zr * Zr Ti = Zi * Zi } paint(&m.m[x][y], Tr, Ti) }
8. ベンチマーク結果の考察 なぜパフォーマンスが落ちたのか?(予想) • pprofでプロファイルを取得した時のワークロードとベンチマークコードが 違うから • インライン展開は特に処理サイズ小さく頻繁に呼び出される関数に対しては有効に 働くが、fillPixelはサイズが大きく、CPUの命令キャッシュや レジスタを圧迫している可能性があるから
• mandelwebの他にも3つほどベンチマークケースを作ったが、 どれもパフォーマンスが改善しなかったので、実行環境由来の可能性もある • パブリックプレビュー版なので、まだ完璧に機能するわけではない
PGO FAQ
PGOによる最適化が行われるのはユーザーコードだけか? • ユーザーコードはもちろん、標準パッケージや3rd partyのパッケージに 対してもコンパイラ最適化は行われる
異なる GOARCH/GOOS のプロファイル結果を利用できるか? • プロファイルの形式はCPUアーキテクチャなどによらないため、 基本的には利用できる • そのため、プロファイル結果をmainパッケージのディレクトリに配置して リポジトリにコミットすることが推奨されている •
ただし、ビルドタグによってビルドされるファイルが異なるとホットな部分が見逃され る場合がある
単一バイナリで複数ワークロードを扱う場合は? 次の3つの解決方法が提示されているがトレードオフがあるので、 メリデメを考えて採用する 1. バイナリを単一ではなくワークロードごとに分ける ◦ それぞれのワークロードに対し最高のフォーマンス改善を期待できるが、 複数バイナリを管理する煩雑さはある 2. パフォーマンスを最も重視するワークロードに対してPGOを適用する
◦ そのワークロードについては最高のパフォーマンス改善を期待できるが、 その他のワークロードのパフォーマンスはそこそこの改善に止まる 3. 複数ワークロードのプロファイル結果をマージする ◦ 2つの目の解決方法のその他のワークロードに比べると、 パフォーマンスの改善を期待できるが、最高のパフォーマンスは期待できない ◦ プロファイルのマージは $ go tool pprof -proto a.pprof b.pprof > c.pprof で行える
ビルド時間は長くならないか? • PGOを有効にしない場合に比べて伸びる • 2回目以降のビルドで同じプロファイルを使用する場合は、 ビルドキャッシュが作られるためビルド時間は抑えられる • プロファイルが大きいと解析するのに時間がかかる問題がある ◦ https://github.com/golang/go/issues/58102
PGOはどのように運用するの? • Go PGOはAutoFDOというフローに基づくように設計されている 1. PGOを無効にしてビルドしたバイナリをプロダクションにリリース 2. プロダクションからプロファイルを収集 3. 次回リリース時にPGOを有効にして収集したプロファイルを用いてビルド
&リリース 4. 2. 3. を繰り返す • AutoFDOは単純だが、注意すべき点とそれを防ぐ考えがある ◦ ソース安定性(Source stability) ◦ 反復安定性(Iterative stability)
ソース安定性 • 一般的にリリース後にはソースは改変されるため、プロファイルを取ったソースと次にビル ドするときのソースは違う • ソースが改変されてもプロファイルをなるべく活用できるように最善を尽くすことが ソース安 定性 • ソース安定性が崩れない条件
◦ ホットな関数外への改変 ◦ 同じパッケージの違うファイルへホットな関数を移動 • ソース安定性が崩れる条件 ◦ ホットな関数の改変 ◦ 関数名の改変 ◦ 違うパッケージへホットな関数を移動
反復安定性 • PGOによってホットとみなされ最適化された関数は、次のビルドではホットではなく なっているはずなので最適化が行われない • そして最適化が行われていない関数が、その次のビルド時に再度ホットをみなされ る • つまり、ある関数がPGOを用いてビルドを行うたびに、最適化が行われた状態と行 われていない状態を繰り返す可能性がある
• Goはこの反復を行わないように保守的にPGOを活用している • このことを反復安定性と呼ぶ
今後予定されている最適化項目 • slice/mapのcapサイズを事前に決める • devirtualization • 実行バイナリ内での関数の配置順序を変える • 寿命の近い値を近い場所にメモリ割り当てする •
レジスタ割り当ての効率化 • など https://github.com/golang/go/issues/55022#issuecomment-1245605666
もっと詳しく知りたい人向け • Proposal: profile-guided optimization ◦ ベースとなるProposal ◦ https://go.googlesource.com/proposal/+/master/design/55022-pgo.md •
Proposal: Design and Implementation of Profile-Guided Optimization (PGO) for Go ◦ 実装方針を説明した Proposal ◦ https://go.googlesource.com/proposal/+/master/design/55022-pgo-implementation.md • PGOの初期実装 ◦ https://go-review.googlesource.com/c/go/+/429863 • PGOの課題の中でGo 1.21で解決予定のリスト ◦ https://github.com/golang/go/issues/55022#issuecomment-1409183967