Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
PGOによるコンパイラ最適化 / Compiler Optimization with PGO
Search
Koya IWAMURA
March 07, 2023
Programming
1.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.27?
ciarana
1
550
What's new in Go 1.26?
ciarana
2
490
What's new in Go 1.25?
ciarana
1
130
What's new in Go 1.24?
ciarana
1
290
What's new in Go 1.21?
ciarana
2
1.8k
Go1.13以後のエラーハンドリングについて語ろう / Let's talk about error handling after Go 1 13
ciarana
10
8.8k
Other Decks in Programming
See All in Programming
20260914 AIエージェント時代のPlatform Engineering LLM基盤とプロダクトの責務境界線
kanfab1
6
2k
大喜利で理解するLLM as a Judge / Understanding LLM-as-a-Judge through Ogiri
rockname
0
110
Java 27新機能 / Java 27 new features
kishida
2
140
Streamlitで実現する自然言語データアプリ開発
ayumu_yamaguchi
0
280
Vibes Containers 〜AIで変わるコンテナ設計と運用〜
tkikuc
3
550
AWS DevOps Agentで インシデント対応をAIに任せたい
honmarkhunt
7
2.9k
Family mrubyの進捗
kishima
1
120
Claude Codeを組織的に動かして月400PRを実現した話
happy_ryo
0
280
XHTMLが残したもの
yosuke_furukawa
PRO
2
800
市販E-Readerを乗っ取れ 〜Embedded Swiftで電子ペーパーガジェットを制御する〜
trickart
0
190
一人だけ、Kiroが静止する日
hideg
0
110
変化を抱擁するドキュメントの作り方 - ビジネスルール駆動開発がもたらす、コードとの新しい関係
ioki
2
200
Featured
See All Featured
4 Signs Your Business is Dying
shpigford
187
23k
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
600
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
3k
Facilitating Awesome Meetings
lara
57
7.1k
svc-hook: hooking system calls on ARM64 by binary rewriting
retrage
2
570
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
3
240
The Invisible Side of Design
smashingmag
301
52k
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
2.2k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
120
120k
The Director’s Chair: Orchestrating AI for Truly Effective Learning
tmiket
1
300
Statistics for Hackers
jakevdp
799
230k
Exploring the relationship between traditional SERPs and Gen AI search
raygrieselhuber
PRO
3
4.3k
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