Dev Study
システム性能分析のしくみ コース

9. サンプリングとトレーシング — コスト構造が違う

観測ツールのコストがどう決まるのかを、サンプリングとトレーシングの2方式に分けて図で追っていきましょう。

① 2つの方式は「いつ記録するか」が違う

記録のきっかけ: 時計か、イベントか
観測したい対象動き続けているプロセス
記録のきっかけが2通りある
サンプリング一定レートで割り込み、その瞬間のスタックを抜き取る
perf record
トレーシング対象イベントが起きるたびハンドラが走る
strace / トレースポイント
だからコストの決まり方が違う
件数 ≒ レート × 時間対象が何をしていても件数は変わらない
件数 = イベント発生回数対象が忙しいほど件数が増える
  • ・perf は既定で周波数指定のサンプリングを行い、カウンタのオーバーフローを契機にサンプルを記録します(perf wiki)
  • ・strace は ptrace(2) を使い、システムコールのたびに対象を停止させます(strace(1))

サンプリングは時間に対して課金され、トレーシングはイベント回数に対して課金されます。この一点がすべての使い分けの根拠になります。

② サンプリングの件数は事前に見積もれる

perf record -F 1000 を 60 秒、システム全体で回した場合
-F 10001秒あたり1000サンプルを狙う平均レート
× 計測時間 60 秒
1 CPU あたり 約 60000 サンプル対象が何をしていても件数はほぼ同じ
システム全体収集なら × CPU 数(ここでは 4)
合計 約 240000 サンプルperf.data のサイズも概算できる
  • ・perf record の既定は平均レート方式で、1000Hz(1000 samples/sec)と perf wiki に記載があります
  • ・CPU 数を掛けられるのはシステム全体収集の場合です。単一プロセスを追う場合、件数はそのプロセスが走った CPU 時間に対応します
  • ・-F は狙う周波数、-c は「イベント何回につき1サンプル」という周期を指定します(perf-record(1))
  • ・-g を付けるとコールグラフを記録するため、1サンプルあたりの記録量が増えます

レートと時間を掛けるだけで件数の目安が出ます。つまり計測を始める前にオーバーヘッドの上限を見積もれます。

③ 一定レートを保つために周期は自動調整される

周波数指定(-F)のときカーネルがすること
狙い: 平均 1000 サンプル/秒
対象が速く回りイベントが増えた
カウンタのオーバーフローが早く来るこのままでは件数が増えすぎる
カーネルが周期を動的に伸ばす
1オーバーフローに必要なイベント数を増やす結果として毎秒の件数は狙い値に戻る
  • ・周波数指定では「カーネルが目標の平均レートを達成するようサンプリング周期を動的に調整している」と perf wiki に記載があります
  • ・-c を使うと周期は固定になり、件数はイベント発生回数に比例して動きます

「レート×時間で件数が決まる」が成り立つのは、カーネルが周期を調整して平均レートを守っているからです。ここが見積もり可能性の根拠です。

④ 重くなりすぎたときはカーネルが自ら絞る

サンプル処理が長引いたときに起きること
kernel.perf_cpu_time_max_percentサンプル処理に割いてよいCPU時間の割合
1サンプルの処理が想定より長いと判断された
カーネルが上限超過を検知サンプリング側が対象を圧迫している状態
サンプリング周波数を自動で落とす
kernel.perf_event_max_sample_rate が下げられる許容される最大周波数そのものが引き下げられる
  • ・perf_cpu_time_max_percent は「サンプルがこの上限を超えていると通知されると、CPU使用を減らすためサンプリング周波数を落とす」と説明されています(kernel sysctl ドキュメント)
  • ・0 で仕組みを無効化、1〜100 で割合指定です。100 にしても超過すれば絞られることがあります
  • ・-F max は現在の kernel.perf_event_max_sample_rate の値を使う指定です(perf-record(1))
  • ・既定値は環境やカーネル版で異なるため、sysctl で実際の値を確認してください

サンプリングは上限を見積もれるだけでなく、重くなりすぎたときにカーネル側が周波数を落とす仕組みまで持っています。ここがトレーシングとの安心感の差です。

⑤ トレーシングは発生回数にそのまま比例する

同じツールでも対象次第でコストが桁で変わる
1イベントあたりの固定コストハンドラ実行 + 記録
低頻度な対象に当てた場合
毎秒 100 回のイベント追加コストは無視できる範囲
同じツールを高頻度な対象に当てた場合
毎秒 100000 回のイベント固定コストが1000倍に積み上がる
結果
計測が対象を遅くする遅くなった姿を測ってしまう
  • ・件数の上限を指定する仕組みがないため、事前見積もりにはイベント発生回数の見当が必要です
  • ・観測対象が遅くなるとイベント頻度も変わるため、測った値がそのままでは元の姿を表しません

トレーシングには「レート」に相当する歯止めがありません。高頻度な対象では、計測そのものが結果を歪めます。

⑥ strace が特に重い理由は停止回数にある

ptrace はシステムコール1回につき2回止める
対象がシステムコールを1回発行
1回目の停止
syscall-enter-stop実行される直前で停止し、引数を読める
tracer が PTRACE_SYSCALL で再開
システムコール本体が実行される
2回目の停止
syscall-exit-stop完了時に停止し、戻り値を読める
  • ・1回目の停止で引数を、もう一度 PTRACE_SYSCALL してから2回目の停止で戻り値を見る、という使い方が想定されています(ptrace(2))
  • ・1回のシステムコールごとに2回の停止と、そのたびの切り替えが入ります
  • ・strace(1) は「追跡されたプロセスは追跡されないものより遅く動く」と明記しています
  • ・--seccomp-bpf は負荷を軽減できますが、-f を同時に指定しないと効果がありません(strace(1))

重さの正体は記録量ではなく停止回数です。システムコールを多発するプロセスほど、この2回の停止が積み上がります。

⑦ 使い分けは「全体像 → 因果」の順

軽い順に当てて、範囲を絞ってから重い道具へ
perf statカウンタの合計を読むだけ
最も軽い
どのリソースが問題かの当たりを付ける
perf recordレート×時間でコスト上限が決まる
全体像の把握
ホットな関数・対象を絞り込む
トレーシング絞った対象にだけイベント単位で仕掛ける
因果の追跡
  • ・perf stat はイベントを集計して終了時に合計を出す方式で、サンプルは記録しません(perf-stat(1))
  • ・絞り込みが済んでいれば、トレーシングの対象イベント数そのものを減らせます

順序に意味があります。軽い方式で範囲を狭めておくほど、最後に使うトレーシングのイベント発生回数が減り、コストが実用の範囲に収まります。

公式ドキュメントで詳しく ↗