SODA Engineering Blog
🏂

Go の pprof で ボトルネックを探して ISUCON で優勝する

2023/10/29に公開

はじめに

pprofとは

Goで動くアプリケーションのプロファイリングを行うことができる標準パッケージです。
アプリケーション実行時におけるCPU時間などの計算資源の消費を処理ごとに計測することができます。

runtime/pprofnet/http/pprof があり、後者は前者をHTTPサーバ経由で提供してくれるパッケージです。

ISUCONの練習環境を想定しています

この記事では net/http/pprof を使ってHTTPサーバのプロファイリングを行う手順を説明します。
環境としては ISUCON の練習環境を想定しています。

ISUCONの練習環境としては、負荷をかける側のベンチマーカー用サーバと、負荷を受ける側の競技用サーバが提供されています(例: isucon/isucon12-final )。

外側からボトルネックを探す

ISUCON本

ボトルネックの特定は外側から順番に

藤原 俊一郎,馬場 俊彰,中西 建登,長野 雅広,金子 達哉,草野 翔. 達人が教えるWebパフォーマンスチューニング 〜ISUCONから学ぶ高速化の実践 (Japanese Edition) (p. 49). Kindle Edition.

と言われているように、Webサービスのボトルネックは外側から順番に探すと良いです。

top でWebアプリケーションのCPU利用率がボトルネックになっていそうなら、詳細な原因を特定するためにプロファイリングをしてみると良いかもしれません。

やり方

計測用のコードを仕込む

まずはGoアプリケーションに計測用のコードを仕込みましょう。
6060番ポートでプロファイリングデータが提供されるようになります。
net/http/pprof をBlank Importするのをお忘れなく。

package main

import (
    "log"
    "net/http"
    _ "net/http/pprof"
    "runtime"
)

func main() {
    runtime.SetBlockProfileRate(1)
    runtime.SetMutexProfileFraction(1)
    go func() {
        log.Fatal(http.ListenAndServe("localhost:6060", nil))
    }()

    log.Fatal(http.ListenAndServe("localhost:8080", nil))
}

次のコマンドでシュッと挿入することもできます。

go run github.com/rinchsan/ppprof/cmd/ppprof@latest -fix main.go
go run github.com/rinchsan/gosimports/cmd/gosimports@latest -w main.go

Goの静的解析は色々と便利ですね。

https://github.com/rinchsan/ppprof

graphvizとgvをインストールしておく

6060番ポートで提供されるデータは人間が見やすい構造にはなっていないので、 go tool pprof などで可視化するとよいです。

go tool pprof で可視化するには graphviz と gv が必要になるので次のコマンドでインストールしておきましょう。
go tool pprof を実行するサーバで必要なので、今回は競技用サーバでのインストールが必要です。

sudo apt update
sudo apt install -y gv graphviz

ベンチ開始 & プロファイリング開始

これで準備は整ったので、ベンチマーカーの走行を開始し、 go tool pprof でプロファイリングデータの取得・可視化をしましょう。
go tool pprof は実行開始してから終了するまでのプロファイリングデータを取得するため、ベンチの負荷走行フェーズが開始してから実行すると正確なデータを取得できます。

負荷走行フェーズは約60秒であることが多いため、 -seconds オプションで 60 を指定して実行します。
デフォルトでは30秒になっています。

go tool pprof -seconds 60 -http=localhost:1080 http://localhost:6060/debug/pprof/profile

ローカルにポートフォワードして結果を見る

go tool pprof のデータ取得が完了したら、可視化されたWeb UIが1080番ポートで提供されます。
競技用サーバ上でWeb UIを見るのは大変なので、ローカルマシンにポートフォワードしてWebブラウザで見ましょう。

ssh {username}@{競技用サーバのPublic IP} -fNL 1080:localhost:1080

ブラウザで http://localhost:1080 にアクセスし、例えばFrame Graphを開くと次のようなページを見ることができます。
main.(*Handler).obtainItem あたりがCPU時間の消費としては多そうなことがわかりますね。

image

ファイルをダウンロードすればローカルでも見れる

Web UI上の DOWNLOAD ボタンをクリックするとプロファイリングデータをファイルとしてダウンロードできます。
go tool pprof を終了するとWeb UIが見れなくなってしまいますが、ファイルをダウンロードしておけば次のコマンドで再度Web UIを起動できます。

go tool pprof -http=localhost:1080 profile.pb.gz

おわりに

これで ISUCON 優勝!

※ 優勝できませんでした。

https://zenn.dev/team_soda/articles/20231126195358

GitHubで編集を提案
SODA Engineering Blog
SODA Engineering Blog

Discussion