『詳解 システム・パフォーマンス 第2版』を読む:第6章 CPUの全容

Progress 6 / 13
目次

はじめに

[!NOTE] この記事は、筆者が鈍器本を読みながら書き殴った個人的な読書メモをAIに食べさせ、ブログ用に読みやすく再構成させたものです。

これまでOSの基礎やアプリケーションの分析メソドロジを整理してきましたが、今回はすべてのソフトウェアの原動力であり、本書の中でも屈指のボリュームと重要度を誇る「6章 CPU」です。

今回も、ハードウェアの細かすぎる話は思い切って省き、「CPUが遅い!」と感じたときに現場で使うべきサバイバル術やプロファイリングの罠を、サクッと見返せるようにチートシート化しました。


1. 使用率100%の裏に潜む「飽和」

CPU使用率が100%に達したあとの本当のボトルネックは、「CPUを使いたくても順番待ちをしているスレッドの数(ランキューの長さ)」を見ないとわかりません。

  • スケジューラレイテンシ: CPUの数以上に実行可能なスレッドが存在すると、スレッドは「ランキュー」という行列に並ばされます。この行列で待たされている時間が「スケジューラレイテンシ」であり、アプリケーションの遅延の直接的な原因になります。OSレベルのレイテンシがどう変わるかは、非常に重要な評価ポイントですね。
  • PSI(Pressure Stall Information): 最近のLinux(4.20以降)では、スレッドがCPU待ちでストール(待機)した時間の割合をズバリ教えてくれる「PSI」という機能があり、CPUの飽和度を正確に把握するのに非常に役立ちます。

2. 待ち時間の犯人「優先度の逆転」と「マイグレーション」

CPUの順番待ちは、単に負荷が高いから起きるとは限りません。OSのスケジューラとロックの仕組みが絡む複雑な罠が存在します。

  • 優先度の逆転: 優先度の低いスレッドがロックを保持したままCPUを奪われ、優先度の高いスレッドがそのロックを待って永遠にブロックされてしまう現象です。これを防ぐため、OSは「優先度の継承」という仕組みを持っています。高度な並行処理環境では、この種のロック競合がレイテンシの致命傷になり得ます。
  • マイグレーションとキャッシュの冷え: スレッドが別のCPUコアに移動(マイグレーション)させられると、元のCPU内にあったL1/L2キャッシュが使えなくなり、メモリアクセスが劇的に遅くなります。Linuxのスケジューラはこれを防ぐため、できるだけ同じCPUでスレッドを動かし続けようと努力(CPUアフィニティの維持)しています。

3. アプリケーションスレッド評価の要「カーネル時間」と「コンテキストスイッチ」

OSスレッドからアプリケーションスレッドに移行した場合、OSレベルでの「重い切り替え作業」が減るはずです。これを証明するために2つの指標を監視します。

  • ユーザー時間とカーネル時間の比率: mpstatpidstat を使うと、CPUが「アプリケーションの実行(%usr)」と「OSカーネルの処理(%sys)」のどちらに時間を使っているかが分かります。スレッドモデルの改善が成功していれば、カーネルの管理オーバーヘッドが減り、%sysが減少して%usrの割合が増えるはずです。
  • コンテキストスイッチの回数: OSスレッドがI/Oなどで待たされると「自発的コンテキストスイッチ」が発生します。vmstatcs 欄や、perf stat -e context-switches コマンドでこの回数を測ることができます。OSレベルのコンテキストスイッチが劇的に減少しているかどうかが、アーキテクチャ変更が正しく機能しているかの決定的な証拠になります。

4. CPUの「ストール」と「IPC」

CPUが100%ビジーに見えても、実は「メモリからデータが届くのをひたすら待っているだけの時間(ストールサイクル)」が大半を占めていることがあります。

  • PMC(ハードウェアカウンタ): CPUの内部には、実行した命令数やキャッシュミス、ストールサイクルなどをカウントする専用のハードウェアレジスタ(PMC)が備わっています。
  • IPC(サイクルあたりの命令数): perf stat などのコマンドでIPCを調べると、CPUが本当に効率よく働いているかが分かります。IPCが低い(たとえば0.2以下など)場合、CPUはメモリを待ってストールしており、いくらCPUのクロックスピードを上げてもパフォーマンスは上がりません。

5. プロファイリングの罠(99Hzとフレームグラフ)

「どのコードがCPUを食い潰しているのか」を特定するには perf コマンドでサンプリングをしますが、サンプリング間隔と結果の読み方にコツがあります。

  • 99Hzの罠: サンプリングの頻度を100Hzなどのキリの良い数字にすると、システム側の定期的な処理と偶然同期(ロックステップ)してしまい、プロファイル結果に偏り(ダマシ)が出てしまう危険性があります。そのため、 perf record -F 99 のように、あえて「99Hz」などの半端な数字を指定するのが定石みたいです。
  • フレームグラフで「高原」と「塔」を探す: プロファイラが吐き出した膨大なスタックトレースのテキストを人間が読むのは不可能です。これを可視化した「フレームグラフ」を使います。上から下へ見て、横幅が広く平らになっている「高原」(直接CPUを占有している関数)を探し、下から上へ見て「塔」(どこから呼び出されたか)を追うことで、手っ取り早く解決策が見えてきます。

6. 平均の嘘を暴く「秒未満」の分析と「FlameScope」

1秒間の平均使用率だけを見ていると、一瞬だけ発生する致命的な遅延(スパイク)を見落とします。

  • 秒未満オフセットヒートマップ: 1秒未満のアクティビティをヒートマップ化することで、「数百ミリ秒の間、データベース全体が完全にブロックされていた(白い空白ができている)」といった、ロックやI/Oによる一瞬の詰まりを視覚的に発見できます。
  • FlameScope: Netflixが開発したツールで、上記のヒートマップから「怪しい一瞬の遅延(摂動)」だけをドラッグして選択し、その部分だけのフレームグラフを生成してくれます。平均値に埋もれていた「一瞬の遅延の犯人」を正確に特定できる強力な武器です。

おわりに

この章を読むことで、単に「CPUが100%に張り付いているからサーバーを増やそう」と短絡的に考えるのではなく、「ランキューでスレッドが渋滞しているのか」「メモリ待ちでストールしているのか」「ロックで優先度が逆転しているのか」といった、一歩踏み込んだ分析ができるようになります。