TL;DR
- Go 標準の pprof でプロファイルを取り、Web UI でボトルネックを探して改善する流れを学ぶワークショップ。
- 実際にはベンチマークテストと一緒にプロファイルを取ることが多く、本番環境では継続的にプロファイルを取るのが理想である。
内容
ワークショップの枠から外れてしまったので、資料とリポジトリを見ながら一人で進めた。
プロファイルとは
Go でプロファイルといえば、通常はパフォーマンスプロファイルを指す。プログラムのどこが実行速度を律速しているかを調べる手法で、CPU 使用率やメモリ使用量、スレッド数などを、統計的にコールスタック(関数)単位で集める。
プロファイルを取る標準パッケージ
CLI やバッチツールでは runtime/pprof を使う。HTTP サーバでは net/http/pprof を使い、/debug/pprof/profile(CPU)、/debug/pprof/heap(ヒープ)、/debug/pprof/block(ゴルーチンのブロック)、/debug/pprof/mutex(Mutex の保持)、/debug/pprof/trace(トレース)といったエンドポイントから取得する。どちらの場合も、コードに計装しないとプロファイルは取れない。ワークショップでは CLI アプリケーションを改善するので、runtime/pprof を使う。
Web UI での見方
go tool pprof -http :<ポート番号> で Web UI が立ち上がる。-http を付けないと対話モードになる。Web UI には次のビューがある。
- Flame Graph: 横に長いほどリソースを使っている。
- Top: Linux の
topと同じく、関数をリソースの消費順に表で並べる。 - Graph: 箱が大きいほど消費が多い。差分を比べるときは、緑(改善)から灰色、赤(悪化)のグラデーションで表示される。
- Source: 行単位でリソースの消費を表示する。Go のアセンブリにも対応している。
ほかに Peek や Disassemble もある。
結果の読み方
標準パッケージや有名なパッケージはベンチマークテストを実行しているので、まずは自分が書いたコードに原因があると考える。Flat(その関数自体が直接使った時間やメモリ)が大きければアルゴリズムに問題がある。Cum(その関数が呼び出す関数も含めた累計)が大きければ、外部の関数の呼び出し方に問題がある。Flame Graph や Graph でボトルネックの見当を付け、Source で掘り下げる。
改善の結果は計測でしか判断できないので、プロファイルは常に取る。改善前後の差分は次のように比べる。
go tool pprof -http :9999 -base cpu.prof cpu.prof.2実際の取り方
上のようにプロファイルを取ることはあまりない。実際にはベンチマークテストのときに一緒に出力することが多い。ヒープを取るときは -memprofile を付ける。
go test -bench=. -cpuprofile=cpu.prof
go test -bench=. -memprofile=mem.prof継続的なプロファイリング
常時稼働しているサービスでは、パフォーマンスの問題は突然やってくる。そのため、本当は本番環境で継続的にプロファイルを取りたい。CNCF のオブザーバビリティホワイトペーパーでも、ログ、メトリクス、トレースなどと並ぶ重要なテレメトリとして、継続的なプロファイルが挙げられている。自作は現実的ではないので、Pyroscope などの専用サービスを使う。
キーワード
自分の理解
いい加減、効率的な Go を読まないと…。