Go プロファイリングとベンチマーク 2026年版: pprof、traceと面接質問

pprofとruntime/traceを使ったGoプロファイリングの完全ガイド。CPU、メモリ、ゴルーチン分析テクニックをパフォーマンス最適化と技術面接対策の観点から解説します。

Go プロファイリングとベンチマーク 2026年版: pprof、traceと面接質問

Goのプロファイリングは、pprofとruntime/traceパッケージを使用することで、推測ではなくデータに基づいた最適化を可能にします。パフォーマンスに関する質問はほとんどのGo面接で出題され、フレームグラフを読み取れたり、-inuse_space-allocsの使い分けを説明できる候補者は高く評価されます。これらのツールは標準ライブラリに含まれており、外部依存なしでベンチマークと直接統合できます。

知っておくべきプロファイルタイプ

Go 1.24以降では、CPU、heap、allocs、goroutine、threadcreate、block、mutexの7つの組み込みプロファイルが提供されています。Go 1.26では、到達不能なブロック状態のゴルーチンを検出する実験的なゴルーチンリークプロファイルが追加されました。

pprofによるCPUプロファイリング: 基本から始める

CPUプロファイリングは、一定間隔(デフォルトは100Hz)でコールスタックをサンプリングし、どの関数がプロセッサ時間を消費しているかを記録します。runtime/pprofパッケージが低レベルの収集を処理し、go tool pprofが結果を分析します。30秒のプロファイルで3000サンプルが収集され、ほとんどのアプリケーションで統計的に有意な結果が得られます。

スタンドアロンプログラムでは、起動時にpprof.StartCPUProfileを呼び出すことでプロファイリングを有効にできます。プロファイルはファイルに書き込まれ、後でgo tool pprofで読み取ります:

main.gogo
package main

import (
	"flag"
	"log"
	"os"
	"runtime/pprof"
)

var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")

func main() {
	flag.Parse()
	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		defer f.Close()
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}
	// アプリケーションロジック
}

HTTPサーバーの場合、副作用としてnet/http/pprofをインポートします。このパッケージは/debug/pprof/にハンドラを自動的に登録するため、インポート以外のコード変更は不要です:

server.gogo
package main

import (
	"net/http"
	_ "net/http/pprof" // /debug/pprof/* ハンドラを登録
)

func main() {
	http.HandleFunc("/", handler)
	http.ListenAndServe(":8080", nil)
}

実行中のサーバーから30秒のCPUプロファイルを取得するには、go tool pprof http://localhost:8080/debug/pprof/profile?seconds=30を使用します。ツールがプロファイルをダウンロードし、対話型シェルを開きます。secondsパラメータで収集時間を制御できます。

プロファイル分析: top、list、フレームグラフ

pprofシェルはボトルネックを特定するためのコマンドを提供します。topは最もCPU時間を消費している関数をflat時間でソートして表示します。listコマンドは行ごとのタイミング注釈付きでソースコードを表示し、実行を支配している正確な行を特定できます。

bash
# go tool pprofのターミナルセッション
$ go tool pprof cpu.prof
(pprof) top 10
Showing nodes accounting for 4.2s, 85% of 4.9s total
      flat  flat%   sum%        cum   cum%
     1.8s 36.73% 36.73%      1.8s 36.73%  runtime.memmove
     0.9s 18.37% 55.10%      0.9s 18.37%  encoding/json.(*decodeState).scanWhile
     0.5s 10.20% 65.30%      2.3s 46.94%  main.processRecords
     ...

(pprof) list processRecords
Total: 4.9s
     0.5s      2.3s (flat, cum) 46.94% of Total
      20:   for _, r := range records {
      21:       0.3s    0.3s    data := json.Marshal(r)
      22:       0.2s    2.0s    result := transform(data)
      ...

Webインターフェースは視覚的な分析を追加します。go tool pprof -http=:6060 cpu.profを実行すると、フレームグラフ、有向グラフ、ソースビューを含むブラウザが開きます。Go 1.26以降、WebUIではフレームグラフがデフォルトビューとして表示されます。フレームグラフは呼び出し階層を水平に表示し、幅の広いバーはその関数とその呼び出し先でより多くの時間が費やされたことを示します。

フレームグラフの読み方

フレームグラフでは、x軸はサンプルの母集団を表し、時間ではありません。各ボックスは関数であり、その幅はその関数がサンプルに出現した頻度を示します。親関数は子の下に位置します。上部の広いプラトーを探してください - それらの関数が実際の作業を行っています。

メモリプロファイリング: HeapとAllocs

メモリプロファイリングは2つの異なる質問に答えます。heapプロファイル(-inuse_space)はキャプチャ時点でメモリを保持しているものを示します。allocsプロファイルは、そのメモリがその後解放されていても、時間の経過とともにどこでアロケーションが発生したかを示します。

現在のメモリ使用量を削減するには、heapプロファイルを調べます。アロケーション率とGCプレッシャーを削減するには、allocsプロファイルを調べます。高いアロケーション率は頻繁なガベージコレクションをトリガーし、ゴルーチンを一時停止させ、CPU使用率を増加させます。

bash
# 実行中のサーバーからheapプロファイルをキャプチャ
$ curl -o heap.prof http://localhost:8080/debug/pprof/heap
$ go tool pprof -inuse_space heap.prof

# allocsプロファイルをキャプチャ(30秒間のアロケーション数)
$ curl -o allocs.prof "http://localhost:8080/debug/pprof/allocs?seconds=30"
$ go tool pprof -alloc_objects allocs.prof

-inuse_objectsフラグはバイト数ではなくライブオブジェクト数をカウントし、メモリフラグメンテーションの特定に役立ちます。-alloc_spaceフラグはプロファイル期間中に割り当てられた総バイト数を示し、メモリをすぐに解放しても大量のメモリを消費する関数を明らかにします。

一般的なアロケーションホットスポットには、ループ内での文字列連結(strings.Builderを使用)、ヒープにエスケープするインターフェース変換、事前割り当てなしのスライス成長などがあります。Goコンパイラのドキュメントでエスケープ分析について詳しく説明されています。

testing.Bによるベンチマークプロファイリング

testingパッケージはプロファイリングをベンチマークに直接統合します。この組み合わせにより、完全なアプリケーションのノイズなしに特定のコードパスを分離できます。ベンチマークプロファイリングは「この関数は分離された状態でどのように動作するか」という質問に答えます。

parser_test.gogo
package parser

import "testing"

func BenchmarkParseJSON(b *testing.B) {
	data := []byte(`{"id":1,"name":"test","values":[1,2,3]}`)
	b.ReportAllocs() // アロケーション統計を含める
	b.ResetTimer()   // セットアップをタイミングから除外
	for i := 0; i < b.N; i++ {
		_, _ = Parse(data)
	}
}

フラグを使用してベンチマーク実行中にプロファイルを生成します。-cpuprofile-memprofileフラグは後で分析するためにプロファイルをファイルに書き込みます:

bash
# ベンチマーク中のCPUプロファイル
$ go test -bench=BenchmarkParseJSON -cpuprofile=cpu.prof -benchtime=5s

# ベンチマーク中のメモリプロファイル
$ go test -bench=BenchmarkParseJSON -memprofile=mem.prof -benchtime=5s

# 結果を分析
$ go tool pprof -http=:6060 cpu.prof

-benchtimeフラグはベンチマークの実行時間を制御します。長い実行はより正確なプロファイルを生成しますが、より多くの時間がかかります。5秒の実行で通常は安定した結果が得られます。マイクロベンチマークの場合、-count=10を使用して複数回の反復を実行し、分散をチェックします。

Goの面接対策はできていますか?

インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。

runtime/traceによる実行トレース

pprofは時間がどこで費やされているかを示しますが、runtime/traceはイベントがいつ発生するかを示します。トレースはゴルーチンのスケジューリング、システムコール、GCイベント、ネットワークアクティビティをタイムラインでキャプチャします。この並行性動作への可視性は統計的プロファイリングを補完します。

trace_example.gogo
package main

import (
	"os"
	"runtime/trace"
)

func main() {
	f, _ := os.Create("trace.out")
	defer f.Close()
	trace.Start(f)
	defer trace.Stop()
	
	// アプリケーションロジック
	runConcurrentTasks()
}

トレースビューアーはゴルーチンのライフタイム、ブロッキングイベント、プロセッサ使用率を表示します。各ゴルーチンは水平バーとして表示され、色は実行中、ブロック中、またはスケジューリング待ちを示します:

bash
$ go tool trace trace.out
# ブラウザが http://127.0.0.1:port で開きます

HTTPサーバーの場合、/debug/pprof/trace?seconds=5からトレースを取得します。トレースビューアーはどのゴルーチンが何でブロックしたかを示し、CPUプロファイルでは見逃す競合パターンを明らかにします。「Goroutine analysis」ビューは作成サイトごとにゴルーチンをグループ化し、リークや予期しないファンアウトの特定に役立ちます。

トレースはプロファイルよりも重いです。ビジーなサーバーの5秒のトレースは数百メガバイトのデータを生成する可能性があります。短い期間とターゲットを絞った収集を使用してください。実行トレースに関するGoブログでは高度な分析テクニックについて説明されています。

競合のためのBlockおよびMutexプロファイリング

Blockプロファイリングは、同期プリミティブ(チャネル、ミューテックス、条件変数)で待機しているゴルーチンを記録します。Mutexプロファイリングはミューテックスの競合に特化しています。これらのプロファイルはCPUプロファイリングでは見えない並行性のボトルネックを明らかにします。

競合が発生する前にランタイムパラメータを設定することでこれらのプロファイルを有効にします:

go
// ブロックプロファイリングを有効化(1 = すべてのブロッキングイベントをサンプル)
runtime.SetBlockProfileRate(1)

// ミューテックスプロファイリングを有効化(1 = すべてのミューテックス競合をサンプル)
runtime.SetMutexProfileFraction(1)

本番環境では、オーバーヘッドを削減するためにより高い値を設定します。ブロックプロファイルレート1000000(1マイクロ秒)またはミューテックスフラクション100は、最小限の影響で有用なデータを提供します。これらの値を低く設定しすぎると、すべてのイベントがキャプチャされ、アプリケーションが遅くなる可能性があります。

標準エンドポイントからこれらのプロファイルを取得します:

bash
$ curl -o block.prof http://localhost:8080/debug/pprof/block
$ curl -o mutex.prof http://localhost:8080/debug/pprof/mutex
$ go tool pprof block.prof

Blockプロファイルはブロッキングイベントの数ではなく、待機に費やされた合計時間を示します。1秒間に1回ブロックする関数は、1ミリ秒間に1000回ブロックする関数と同じに見えます。これらのケースを区別するには実行トレースを使用します。

一般的なプロファイリングの落とし穴

プロファイリングは結果を歪める可能性のあるオーバーヘッドを導入します。CPUプロファイリングは約5%のオーバーヘッドを追加します。メモリプロファイリングはアロケーションをサンプリング(デフォルトで512KBごとに1回)するため、小さなアロケーションは表示されない場合があります。トレースはすべてのイベントをキャプチャし、10-30%のオーバーヘッドを追加する可能性があります。

いくつかの間違いが誤解を招くプロファイルにつながります:

最適化されたビルドを異なる方法でプロファイリングする。 常に本番環境で使用されるのと同じビルドフラグでプロファイルを取得してください。デバッグビルドはインライン化と最適化を無効にし、ホットスポットが異なる場所に表示されます。

人工的な負荷でプロファイリングする。 アイドル状態のサーバーのプロファイルはアイドルループを示し、実際のボトルネックを示しません。現実的なトラフィックパターンでプロファイルを取得してください。

GCオーバーヘッドを無視する。 CPUプロファイルにはガベージコレクションに費やされた時間が含まれます。runtime.gc*の存在が高い場合、CPUの問題ではなくメモリアロケーションの問題を示しています。メモリプロファイリングでそれらに対処してください。

プロファイリング期間が短い。 1秒のプロファイルは100サンプルしかキャプチャしません。統計的ノイズが支配します。安定した負荷の下で少なくとも30秒間プロファイルを取得してください。

プロファイリングに関するGo面接質問

パフォーマンスに関する質問は、候補者が実際の問題を診断できるかどうかをテストします。面接官はツールへの習熟度と各プロファイルが何を明らかにするかの理解を求めています。

Q: heapプロファイリングがallocsプロファイリングと異なる結果を示すのはいつですか?

Heapはキャプチャ時に保持されているメモリを示します。Allocsは解放されたメモリを含むすべてのアロケーションを示します。ループで一時バッファを割り当てる関数は、スナップショット前にバッファが収集される場合、allocsには表示されますがheapには表示されません。GCプレッシャーを削減するにはallocsを使用し、リークを見つけるにはheapを使用します。

Q: ゴルーチンがスタックしているように見えます。どのプロファイルが役立ちますか?

ゴルーチンプロファイルはすべてのゴルーチンのスタックトレースを示します。ブロックプロファイルはゴルーチンが待機している場所を示します。Go 1.26以降では、実験的なゴルーチンリークプロファイルがチャネルまたはミューテックスでブロックされている到達不能なゴルーチンを検出します。実行トレースはブロッキングイベントのタイムラインを示します。

Q: pprofのflatパーセンテージとcumulativeパーセンテージは何を意味しますか?

Flatは関数自体での時間を測定します。Cumulativeは呼び出す関数での時間を含みます。cumulative が高くflatが低い関数は、作業を委任するコーディネーターです。flatが高い関数は実際の計算を行います。まずflatが高い関数を最適化してください。

Q: テストセットアップをプロファイリングせずにベンチマークをプロファイリングするにはどうすればよいですか?

セットアップが完了した後にb.ResetTimer()を呼び出します。反復ごとのセットアップがあるベンチマークの場合、セットアップコードの周りにb.StopTimer()b.StartTimer()を使用します。タイマー呼び出しにはナノ秒のオーバーヘッドがあるため、タイトなループでは避けてください。

Q: 遅いにもかかわらず、関数がCPUプロファイルに表示されないのはなぜですか?

CPUプロファイリングはCPUをアクティブに使用している関数のみをキャプチャします。I/Oバウンドの関数(ネットワーク、ディスク、またはチャネルで待機中)はCPUプロファイルではなく、ブロックプロファイルまたはトレースに表示されます。サンプリングは合計で10ms未満しか実行されない関数を見逃すこともあります。

Q: Go 1.24のSwiss Tablesマップ実装はプロファイリングにどのように影響しますか?

Go 1.24ではバケットベースのマップがSwiss Tablesに置き換えられ、マップを多用するワークロードでCPUオーバーヘッドが2-3%削減されました。アップグレード前後に取得されたプロファイルは異なるマップ関連のコールスタックを示します。GOEXPERIMENT=noswissmapフラグは比較のために古い実装に戻します。

本番環境での継続的プロファイリング

ポイントインタイムのプロファイルは一時的な問題を見逃します。PyroscopeParcaなどの継続的プロファイリングツールは、低オーバーヘッドのサンプルを継続的に収集し、デプロイメント間での比較を可能にします。これらのツールはプロファイルをメトリクスおよびトレースと相関させます。

Goの組み込みプロファイルはpprofフォーマットを通じてこれらのツールと連携します。pprof.meサービスは2026年に比較機能を追加し、コード変更前後の最適化の影響を定量化するためにプロファイルをアップロードおよび比較できるようになりました。

Go面接準備では、ツールと基本的な概念の両方を理解することが重要です。contextパッケージ並行性パターンはプロファイリングの質問と一緒に頻繁に出題されます。パフォーマンス最適化には、プロファイリングデータとGoのランタイム動作の知識を組み合わせる必要があることが多いです。

Goプロファイリングが明らかにするアプリケーション動作

  • CPUプロファイルはホット関数を特定しますが、I/Oバウンドの作業を見逃します。完全な画像を得るにはトレースと組み合わせてください。
  • メモリプロファイルは保持の問題(heap)とアロケーションチャーン(allocs)を区別します。リークには-inuse_spaceを、GCプレッシャーには-alloc_spaceを使用してください。
  • ブロックおよびミューテックスプロファイルはCPUプロファイルでは見えない競合を露出します。負荷時にレイテンシスパイクが発生した場合に有効にしてください。
  • ベンチマークプロファイリングは特定のコードパスを分離します。正確な測定のために常にb.ReportAllocs()b.ResetTimer()を呼び出してください。
  • トレースビューアーはゴルーチンのスケジューリングとブロッキングイベントをタイムラインで表示し、並行性バグの診断に不可欠です。
  • Go 1.24のランタイム改善により、Swiss TablesマップとミューテックスのSWAPベース実装によりCPUオーバーヘッドが2-3%削減されました。
  • /debug/pprof/エンドポイントによる本番プロファイリングには認証が必要です。これらのエンドポイントを公開しないでください。内部アプリケーション状態が漏洩し、高価なプロファイル収集によるサービス拒否が可能になります。

今すぐ練習を始めましょう!

面接シミュレーターと技術テストで知識をテストしましょう。

今日のチャレンジ

Go のバグを見つけられますか

実際のコード、隠れたバグ、1日1回。アカウントなしで試せます。

Anthony Fillion-Maillet

執筆

Anthony Fillion-Maillet

SharpSkill 創業者

10 年以上フルスタック開発に携わっています。SharpSkill を運営し、ここで公開される内容に責任を負っています。

2026年9月19日 更新

共有

関連記事