Intro
- CPUの用語、アーキテクチャについての解説
- 分析方法(メソドロジ)と、分析ツールについて
6.1 用語
• プロセッサ(processor): システムやプロセッサボードのソケットに装着される物理チップで、コア、ハードウェアスレッドとして実装されたひとつ以上のCPUを含んでいる。
• コア(core): マルチコアプロセッサ(multicore processor)に含まれる独立した CPUインスタンスのこと
• ハードウェアスレッド(hardware thread): ひとつのコアの上で複数のスレッドの並列実行をサポートする CPU アーキテクチャ(Intel のハイパースレッディングテクノロジなど)
• CPU 命令(CPU instruction): CPU の命令セット(instruction set)に含まれる 1 個の
CPU オペレーション。算術演算、メモリ I/O、制御ロジックのための命令がある。
• 論理 CPU(logical CPU): オペレーティングシステムから見えるCPUインスタンス(スケジューリングできる CPU エンティティ)
• スケジューラ(scheduler): 実行のためにスレッドに CPU を与えるカーネルサブシステム。
• ランキュー(run queue): CPU による処理を待っている実行可能スレッドのキュー。Solaris では、ディスパッチャキュー(dispatcher queue)と呼ばれることが多い。
6.2 モデル
6.2.1 CPUのアーキテクチャ
- 例) 4個のコアを搭載し、8個のハードウェアスレッドを持つシングルプロセッサ構成の図
- OSからは8個のCPUのように見える
6.2.2 CPUのメモリキャッシュ
- 図 6-2 は、キャッシュとサイズの関係を示している。小さく、高速 になればなるほど(両者にはトレードオフの関係がある)、キャッシュは CPU の近くになる
6.2.3 CPUのランキュー
- 図 6-3 は、カーネルのスケジューラが管理している CPU ランキューを示している。実行可能と処理中というスレッドのふたつの状態が描かれている
- キューイングされているソフトウェアスレッドの数は、CPU の飽和を示す重要なパフォーマンス指標
- CPU ランキューでの待ち時間: スケジューラレイテンシと呼ぶ (他には、ランキューレイテンシ、ディスパッチャーレイテンシ)
- カーネルは個々の CPU のためにランキューを提供しており、スレッドを同じランキューにキューイングし続けようとする
- CPU はスレッドのデータをキャッ シングしており、スレッドは同じ CPU で実行され続ける可能性が高い
- スレッドデータが格納されているキャッシュを**ウォーム度(warmth)**のあるキャッシュと呼ぶ
- 特定の CPU を選ぼうとするアプローチを **CPU アフィニティ(CPU affinity)**と呼ぶ
6.3 コンセプト
6.3.1 クロックスピード
-
クロック: プロセッサのロジックを動かすデジタル信号
- CPU 命令を実行するのに、ひとつ以上のクロックサイクル(CPU サイクル)が必要
- クロック数を下げて消費電力を下げられる製品もある (Q. NotePC向け製品とか??)
- クロック数をあげても必ずしも速くなるわけではない ← メモリアクセス待ちでストールするワークロード, etc
6.3.2 命令
- CPUは命令を実行する。実行ステップ5つ。それぞれ別の機能ユニット(funcitional unit)で実行される。
- 命令のフェッチ
- 命令のデコード
- 命令の実行
- メモリアクセス (optional)
- レジスタへの書き戻し (optional)
- 各ステップは最低でも1クロックサイクルを消費
- メインメモリへのアクセスには数十サイクルかかり、その間CPUはストールする(止まる)
- ストールしている間のサイクル数をストールサイクル
6.3.3 命令パイプライン
- 複数の命令を並列実行
6.3.4 命令幅
スーパースカラ: 複数のパイプライン
命令幅: 並列数(図では幅2)
今のプロセッサは、幅3または幅4
補足: スーパースケーラーを最初に実装したx86は、インテルのPentiumとCyrixのM1
6.3.5 CPI (Cycles per Instruction), IPC (Instruction per Cycles)
- CPIが高い => どこかでCPUがストールしている
- メモリIOでストールしていた場合、CPUを速くしてもストールサイクルが増えるだけで無駄
- 目安
- CPI高い: 10以上
-
CPI低い: 1未満
- パイプライン処理によって原理的に1未満はおきうる
6.3.6 使用率
- CPU使用率: CPUが仕事をしているビジー時間の割合を%であらわしたもの
- 使用率が高くなっても、カーネルが 優先度、プリエンプション、タイムシェアリングをサポートしているため、パフォーマンスはかならずしも大きく下がるとは限らない
-
IMPORTANT: CPU 使用率の測定値は、メモリストールサイクルを含む !!
- ディスクI/Oは含まれないけどメモリは含まれている...!
- CPU 使用率は、ユーザー時間とカーネル時間に分けられることがよくある
6.3.7 ユーザー時間/カーネル時間
- ユーザー時間: ユーザーレベルアプリケーションコードの実行に使われた CPU 時間
-
カーネル時間: カーネルレベルコードの実行に使われた CPU 時間
- システムコール中、カーネルスレッド、割り込み中の時間
- 比率の雰囲気
- CPUを酷使するアプリケーション => ユーザー時間:カーネル時間 = 99:1
- I/Oを酷使するアプリケーション => ユーザー時間:カーネル時間 = 70:30
6.3.8 飽和
- 飽和(saturated): 使用率100%のCPUは飽和している、という
- スケジューラレイテンシ: スレッドがCPUで実行されるのを待機するために費やす時間
- プリエンプションがあるので、CPU は飽和状態になってもあまり大きな問題と考えなくてよい (ほぅ
6.3.9 プリエンプション
- 3 章「オペレーティングシステム」でも説明
- 優先度の高い スレッドが現在実行されているスレッドから CPU を奪い、自分の実行を開始できること
6.3.10 優先度の逆転
-
優先度の低いスレッドがリソースを抱え込んでいて、優先度の高いスレッドの実行をブロックす
ると、優先度の逆転が起きる - 優先度の継承: Solaris系が実装している優先度の逆転を防ぐ方策
- 例) 優先度: スレッドC > スレッドB > スレッドB
- 優先度の低いスレッドAがリソースをロック
- 優先度の高いスレッドCも同じリソースを使う
- スレッドCを実行したいのにスレッドAがリソースを掴んでいるので実行できない => スレッドBが実行される
- いつまで経ってもスレッドCを実行できない <= 優先度が低いスレッドAに引きづられている。優先度の逆転
- Solaris だと優先度の継承があって、スレッドAの優先度がスレッドCと同じになる
6.3.11 マルチプロセスとマルチスレッディング
- 複数CPUを使う方策。Linuxではマルチプロセスとマルチスレッディングをサポート
- メモリオーバヘッドは、アドレス空間が別々になるマルチプロセスのほうが大きい
- CPUオーバヘッドも、fork()/exit()のコスト、MMUの仕事があるためマルチプロセスのほうが大きい
- 通信も、IPC経由になり、マルプロセスのほうがコストが大きい。
- ただし、スレッド同期などしないといけないので、マルチスレッドのほうがプログラミングは難しい
6.3.12 ワードサイズ
- プロセッサはワードサイズ(32bit or 64bit)をもとに設計されている
- サイズが大きいほうがパフォーマンスは良くなる. ただし、
- データ型によっては未使用bitの分、オーバヘッドが大きくなる
- ポインタサイズも大きくなってより多くのメモリI/Oが
- 小さなワードサイズ用にコンパイルして動かすこともできる
- 64bit CPUで、32bit OSを動かす(4年前のDeNA...)
6.3.13 コンパイラの最適化
- コンパイラのオプションと最適化によってアプリのCPU時間は大幅に短縮できる
- 5章で説明した
6.4 アーキテクチャ
##6.4.1 ハードウェア
- CPU ハードウェアは、プロセッサとそのサブシステムから構成されている
###プロセッサ
-
制御ユニット(図では制御ロジック)がCPUの心臓部
- 命令のフェッチ、デコード、実行管理、結果の格納を実行
- この図では浮動小数点数演算ユニットとL3キャッシュが共有
- 他の部品があることも
- P キャッシュ(P-cache):プリフェッチキャッシュ(CPU ごと)
- W キャッシュ(W-cache):書き込みキャッシュ(CPU ごと)
- クロック(clock):CPU クロックのためのシグナルジェネレータ(外部供給の場合も)
- タイムスタンプカウンタ(timestamp counter):高精度の時刻、クロックによってイン クリメント
- マイクロコード ROM(microcode ROM):命令を回路信号に高速変換する
- 温度センサー(temparature sensor):熱の監視 <= 温度がまだ低ければオーバークロックする: Interl Turbo Boost テクノロジ
- ネットワークインターフェイス:オンチップにある場合がある(ハイパフォーマンスの実現のため)
CPU キャッシュ
- プロセッサに搭載されている場合、オンチップ:on-chip、オンダイ:on-die、組み込み:embedded、統合:integrated などと呼ばれる
- I$: レベル 1 命令キャッシュ
- D$: レベル 1 データキャッシュ
- TLB: Translation lookaside buffer
- E$: レベル 2 キャッシュ
- レベル 3 キャッシュ(オプション)
- マルチコアプロセッサでは、これらのキャッシュの一部はコアやスレッドの間で共有される場合がある
レイテンシ
- L1: 数CPUクロックサイクル
- L2: 10クロックサイクル
- Main Memory: 60n 秒(4GHz プロセッサで約 240 サイクル) + MMU (Memory Management Unit) によるアドレス変換処理のオーバーヘッド
- LMBench を使って計測してみると、キャッシュレベルを超えた時に急激に遅くなっていることがわかる
連想性
キャッシュ内の新しいエントリを探すときの制約を示すキャッシュ特性
直接連想、ダイレクトマップ
cache 番地 = memory 番地 % n (アドレス下位nビット)
完全連想、フルアソシエイティブ(full associative)
キャッシュはどこの新エントリでも見つけられる。たとえば、LRU アルゴリズムは、キャッシュ全体からLRU(最後 に使われてからもっとも時間がたっている)エントリを弾き出せる。
キャッシュ全体を検索しないといけないので遅い
群連想、セットアソシアティブ(set associative)
この2つの中ではLRUアルゴリズムを使う
CPU キャッシュは、直接マッピング方式(ヒット率が悪い)と完全連想方式(コストが高くつく)との間のバランスを取る群連想方式を使うことが多い
キャッシュライン
- CPU がメインメモリからデータを読み出すとき、必ず小さなメモリチャンクをキャッシュ上にロードする
- ロード単位はプロセッサによるけど、だいたい 8 ~ 512 バイト。このロード単位をキャッシュラインと呼ぶ
- x86 プロセッサの典型的なキャッシュラインサイズは 64 バイト
- コンパイラは、パフォーマンスを最適化するときにキャッシュラインサイズを考慮にいれるし、プログラマもそうすることがある
キャッシュコヒーレンシ
- 複数のCPUでキャッシュしていて、特定CPUで書き換えられたら、他のCPUでのキャッシュが陳腐化していることを認識しなければならない
- 次の読み出しでは新しく書き換えられたコピーを取得するようにしなければならない。この処理をキャッシュコヒーレンシと呼ぶ
MMU (Memory Management Unit)
- 仮想アドレスから物理アドレスへの変換を行う
インターコネクト
プロセッサは共有システムバスか専用インターコネクトを使って接続される
初期の Intel プロセッサが使っていたフロントサイドバス(front-side bus)という共有システムバス
Intel の QPI(Quick Path Interconnect)
一般に、インターコネクトは、システム全体のボトルネックにならないように高帯域幅に設計されている
CPU パフォーマンスカウンタ (CPC)
- 別名: PIC(performance instrumentation counter)、
PMU(performance monitoring unit)、ハードウェアイベント(hardware event)、パフォーマン ス監視イベント(performance monitoring event) - 次のものをカウントする
- CPU サイクル:ストールサイクルとストールサイクルタイプを含む。
- CPU 命令:リタイア済み(実行済み)
- レベル1、2、3 キャッシュアクセス:ヒット、ミス
- 浮動小数点数演算ユニット:演算(オペレーション)
- メモリ I/O:読み、書き、ストールサイクル
- リソース I/O:読み、書き、ストールサイクル
- 個々のCPUは、これらのようにイベントを記録するようにプログラムできる少数のレジスタ (通常2個から8個)を持っている
- Intel P6 ファミリのプロセッサは、4 個の MSR(Model-Specific Register、
モデル固有レジスタ)でカウンタを提供 - libcpcを使って取れるっぽい
- Intel P6 ファミリのプロセッサは、4 個の MSR(Model-Specific Register、
こういうのが取れるらしい
perf stat で色々取れる(後述)
6.4.2 ソフトウェア
CPUをサポートするカーネルの機能
スケジューラ
カーネルのスケジューラのもつ機能
- タイムシェアリング:実行可能スレッドの間のマルチタスキング。優先度がもっとも高いものを先に実行する。
- プリエンプション:優先度の高いスレッドが実行可能になったときに、スケジューラは優先度の高いスレッドの実行をすぐに開始するために、現在実行中のスレッドにプリエンプション(一時的な中断)をかけられる。
- ロードバランシング:アイドル状態、またはそれほどビジーではない状態のCPUのランキューに実行可能スレッドを移す。
Linux
Linux では、タイムシェアリングはシステムタイマー割り込みによって駆動され、scheduler_ tick() 呼び出しによって処理される。scheduler_tick() は、優先度、タイムスライスと呼ばれる CPU 時間の単位の経過を管理するためにスケジューリングクラス関数を呼び出す。スレッドの切 り替えは、__schedule()
によって管理される。__schedule()
は、pick_next_task() でもっとも 優先度の高いスレッドを実行の対象として選択する。ロードバランシングは、load_balance() に よって実行される。
Solaris
略
スケジューラ
- スケジューリングクラス: Linux では、リアルタイムか「通常」 (つまり、リアルタイムではない)のどちらか。
- 実行可能スレッドのふるまいを管理
- 優先度
- タイムスライス
- タイムスライスの量(タイムクォンタム)を管理
- ユーザーレベルスレッドの優先度は、ユーザー定義のナイス値(-20 〜 19)の影響を受ける
- Linux では、ナイス値は、スケジューラが計算する動的優先度とは別に、スレッドの静的優先度を設定する
スケジューラポリシー
sched_setscheduler(2) で設定できる
- 通常
- SCHED_OTHER (SCHED_NORMAL): 標準の、ラウンドロビンによる時分割型のスケジューリングポリシー。
- SCHED_BATCH: 「バッチ」形式でのプロセスの実行用。
- SCHED_IDLE: 「非常に」低い優先度で動作するバックグラウンドジョブ用。
- リアルタイム
- SCHED_FIFO: ファーストイン、ファーストアウト型のポリシー
- SCHED_RR: ラウンドロビン型のポリシー
NUMA グループ
-
NUMA(Non-Uniform Memory Access)とは、メモリ共有型のマルチプロセッサシステムの実装方式の一つで、メモリへのアクセス速度が均一にならないような方式
-
カーネルは、自動的に NUMA を検出し、局所化された CPU、メモリリソースのグループを 作り、NUMA アーキテクチャを反映したトポロジに組織する
-
NUMAを意識したスケジューリング
6.5 メソドロジ
6.5.1 ツールメソッド
- 利用できるツールを順に試していく手法
- uptime
- vmstat
- mpstat
- top/prstat
- pidstat/prstat
- perf/dtrace/stap/oprofile
- perf/cpustat
6.5.2 USEメソッド
- USE (Utilization、Saturation、Errors) をチェック
- 使用率:CPU がビジーだった時間(アイドルスレッド以外を実行していた時間)
- 飽和:実行可能スレッドが CPU 待ちでキューイングされている度合い。ロードアベレージ
- エラー:修正可能なものも含む CPU エラー
- エラーはすぐチェックできるので最初に見るとよい
6.5.3 ワークロードの特性の把握
- ワークロードによってチューニングやモニタリングのすべき項目が変わる
- CPU ワークロードを特徴付ける基本属性
- ロードアベレージ(使用率 + 飽和)
- ユーザー時間とシステム時間の比率
- システムコールの頻度
- 自発的なコンテキストスイッチの頻度
- 割り込みの頻度
- 例) システム時間の比率が高い
- カーネル内で使われている時間が長いということ
- I/Oバウンドのワークロード
- システム時間の比率とシステムコールの頻度が高い
- I/O を待ってスレッドがブロックするので、自発的なコンテキストスイッチも多い。
高度なワークロードの特性の把握/チェックリスト
- システム全体で CPU の使用率はどうなっているか。CPU ごとではどうか。
- CPU の負荷はどれくらい並列化されているか。シングルスレッドか、スレッドはいくつか。
- どのアプリケーション、またはユーザーが CPU を使っているか。どのくらいか。
- どのカーネルスレッドが CPU を使っているか。どのくらいか。
- 割り込みの CPU 使用状況はどれくらいか。
- CPU インターコネクトの使用率はどれくらいか。
- CPU はなぜ使われているのか(ユーザー/カーネルレベルのコールパスはどうか)。
- どのようなタイプのストールサイクルが発生しているか。
6.5.4 プロファイリング
- DTrace を含む一部のプロファイリングツールは、キャプチャしたデータをリアルタイムで処理 することができる(自慢)
- CPU プロファイルデータの要素
- ユーザーレベルかカーネルレベルか、その両方か
- 関数とオフセット(プログラムカウンタベース)、関数のみ、部分的なスタックトレース、 完全なスタックトレース
10 秒に渡って 997Hz (100Hz や 1,000Hz で実行されている定期的なタスクと歩調が合ってしまわないようにした)でユーザーレベル関数名をサンプリングする DTrace 1 行プログラム
6.5.5 サイクル分析
- CPC(CPU Performance Counter)を使えば、CPU の使用率がサイクルレベルでわかる
- レベル 1、2、3 キャッシュミス
- メモリ I/O
- リソース I/O でストール しているか
- 浮動小数点数演算
- その他のアクティビティ
- CPI(Cycles Per Instruction)の計測から始める
- 高い(10より上) => ストールしてそう
- 低い(1より下) => 問題はないはずだが、さらにチューニングするなら、実行される命令数を減らす方法をコード内で探すぐらいしか
- ツール
- DTrace の cpc プロバイダ
- 例) レベル 2 キャッシュミスが 1 万回発生するたびに、カーネルに割り込んでスタックのバックトレースを集める
- perf stat
- DTrace の cpc プロバイダ
- サイクル分析は高度な活動で何日もかかる場合がある
- CPU ベンダーのプロセッサマニュアルのために貴重な時間が潰れることも予想しておかなければならない
6.5.6 パフォーマンスモニタリング
- CPU の主要な指標
- 使用率:パーセントビジー
- 飽和:ロードアベレージから推定できるランキューの長さか、スレッドスケジューラレイテンシの計測値
- コアごとにモニタリングすべき
- 1分おきのモニタリングではバーストを見逃す場合がある
6.5.7 静的パフォーマンスチューニング
静的構成をチェック
- 何個のCPUが使える状態になっているか。それはコアか、ハードウェアスレッドか。
- CPU のアーキテクチャはシングルプロセッサかマルチプロセッサか。
- CPU キャッシュのサイズはどれだけか。供給されているか。
- CPU のクロックスピードはどれだけか。動的か(たとえば、Intel Turbo Boost や SpeedStep)。それら動的なスピード変更は BIOS で有効になっているか。
- BIOS で有効、または無効になっている CPU 関連のその他の機能は何か。
- このプロセッサモデルには、パフォーマンス問題(バグ)があるか。それはプロセッサのエラッタシートに書かれているか。
- この BIOS ファームウェアバージョンにはパフォーマンス問題(バグ)があるか。
- ソフトウェアで強制された CPU の使用制限(リソースコントロール)はあるか。あるならどのようなものか
6.5.8 優先度のチューニング
- nice(2)システムコール、nice(1)コマンドで変更
- nice -n 19 command
- 優先度の低い処理を見分けて、nice付きで起動するお仕事
6.5.9 リソースコントロール
- CPU の使用率の制限
- 詳しくは「6.8 チューニング」で説明する
6.5.10 CPUのバインド
- taskset(1)
- プロセスのバインド: プロセスがある特定のCPUでしか実行できないようにする
• 排他的CPUセット: 割り当てられたプロセス(複数可)以外は使えないようにする- プロセスがアイドル状態のときでもほかのプロセスはCPUを使えず、キャッシュのウォーム度が損なわれないため、CPUキャッシュのヒット率も上がる
6.5.11 マイクロベンチマーキング
- CPU のマイクロベンチマーキング
- CPU 命令:整数演算、浮動小数点数演算、メモリのロード、格納、分岐その他の命令
- メモリアクセス:さまざまな CPU キャッシュのレイテンシとメインメモリのスループットの調査
- オペレーティングシステムのオペレーション: getpid() やプロセスの作成など、CPUバウンドなシステムライブラリやシステムコール関数のテスト
- UnixBench
6.5.12 スケーリング
- キャパシティプランニングにもとづくスケーラビリティの計算方法
- ターゲットユーザーの規模やアプリケーションの要求のペースを見積もる。
- ユーザーごと、要求ごとのCPUの使用状況を明らかにする。負荷生成ツールでユーザーをシミュレートするなど
- CPU リソースの使用率が 100% になったときのユーザー数、要求数を外挿法(1次関数とか)で計算する。これがシステムの理論的な限界になる。
6.6 分析
6.6.1 uptime
システムのロードアベレージを表示する
最後の 3 つの数値は、1 分、5 分、15 分のロードアベレージ
ロードアベレージ
- 実行中のスレッド(使用率)とキューイングされているスレッド(飽和)の合計
- ロードアベレージ > CPU数 => CPU足りてない
Linuxのロードアベレージ
-
IMPORTANT: Linux はロードアベレージに割り込み不能状態のタスクを加えている(´・ω・`)
- 典型的には、I/O待ちが割り込み不能状態
- I/O待ちしてるだけなのにロードアベレージが高く見える
- vmstat(1) や mpstat(1) が返す数字などのほかの指標を使った方がよい
- Solaris では大丈夫
6.6.2 vmstat (Virtual Memory STATistics)
- r:ランキューの長さ。実行可能スレッドの総数
- us:ユーザー時間
- sy:システム(カーネル)時間
- id:アイドル時間
- wa:I/O 待ちの時間。スレッドがディスク I/O のためにブロックされていて、CPUがアイドル状態になっている時間を計測する。
- st:盗まれた(STolen)時間(この出力には表示されていない)。仮想環境で、ほかのテ ナントにサービスを提供するために使われた CPU 時間を示す。
IMPORTANT: Linuxではrは本当は待機しているタスクと実行されているタスクの合計
を反映しているらしい。Solaris では待機しているタスクの数。
6.6.3 mpstat (MultiProcessor STATistics)
CPU ごとの統計情報。top の 1 よりは詳しそう
デフォルト の mpstat(1) は、システム全体のサマリ行(all)しか出力しない
- CPU:論理 CPU ID。サマリ情報では all
- %usr:ユーザー時間
- %nice:ナイス値で優先度を操作したプロセスのユーザー時間
- %sys:システム時間(カーネル)
- %iowait:I/O 待ち時間
- %irq:ハードウェア割り込みによる CPU 使用時間
- %soft:ソフトウェア割り込みによる CPU 使用時間
- %steal:ほかのテナントのために使われた CPU 時間
- %guest:ゲスト仮想マシンのために使われた CPU 時間
- %idle:アイドル時間
6.6.4 sar (System Activity Reporter)
アーカイブから見ることもできる
- -P ALL:mpstat -P ALLと同じ。
- -u:mpstat(1) のデフォルト出力と同じで、システム全体の平均のみを表示する。
- -q:runq-sz 欄としてランキューサイズも表示する(待ちスレッド数と実行中スレッド数 の合計。vmstat の r と同じ
$ sar
10:30:01 AM CPU %user %nice %system %iowait %steal %idle
10:40:01 AM all 0.08 0.00 0.17 0.00 0.00 99.75
10:50:01 AM all 0.08 0.00 0.18 0.00 0.00 99.73
11:00:01 AM all 0.08 0.00 0.17 0.00 0.00 99.74
11:10:01 AM all 0.08 0.00 0.17 0.00 0.01 99.74
11:20:01 AM all 0.08 0.00 0.17 0.00 0.00 99.75
$ sar -q
10:30:01 AM runq-sz plist-sz ldavg-1 ldavg-5 ldavg-15
10:40:01 AM 0 249 0.00 0.00 0.00
10:50:01 AM 0 249 0.00 0.00 0.00
11:00:01 AM 0 249 0.00 0.00 0.00
11:10:01 AM 0 249 0.00 0.00 0.00
6.6.5 ps (Process Status)
BSDスタイル
SVR3 スタイル
- TIME 欄は、作成以降、プロセスが消費した CPU 時間の合計(ユーザー+システム)
- Linux では、%CPU 欄は、前の秒のCPU使用状況をすべてのCPUの合計という形で表示する(マルチスレッドなら100%を超えうる)
- Solaris では、%CPU は CPU 数に合わせて正規化(平均)される
IMPORTANT: %CPU は「前の秒」ではなく「生存期間中」のCPU使用状況のはず ... モニタリングには使えない man ps CPU usage is currently expressed as the percentage of time spent running during the entire lifetime of a process
6.6.6 top
-
TIME+: 分解能が 1/100 秒
-
Linux では、%CPU 欄はデフォルトでは CPU 数で正規化されていない
-
top(1) は /proc のスナップショットを撮るので、短命なプロセスについての情報を取りこぼしてしまうことがある
-
Tips
- top -c: コマンドライン表示
- 1: CPUコア全表示
- F n: メモリでソート
6.6.7 prstat
- Solaris ベースシステム用の top として作られた。略
- prstat(1) は、open()、read()、close() のサイクルではなく、開いたままにしたファイル記 述子で /proc の状態を読み出す pread() を使っているので、top(1) よりも CPU リソースを消費 しない
6.6.8 pidstat
- プロセスまたはスレッドごとに CPU の使用状況を表示
- デフォルトではアクティブなスレッドのみ
$ pidstat 1
02:12:14 PM PID %usr %system %guest %CPU CPU Command
02:12:15 PM 22667 1.00 2.00 0.00 3.00 6 pidstat
02:12:15 PM 39322 1.00 0.00 0.00 1.00 8 bundle
02:12:15 PM PID %usr %system %guest %CPU CPU Command
02:12:16 PM 3464 0.00 1.00 0.00 1.00 7 cmasm2d
02:12:16 PM 22667 1.00 2.00 0.00 3.00 6 pidstat
02:12:16 PM 39321 1.00 0.00 0.00 1.00 3 bundle
02:12:16 PM 39323 1.00 0.00 0.00 1.00 0 bundle
02:12:16 PM PID %usr %system %guest %CPU CPU Command
02:12:17 PM 155 0.00 1.00 0.00 1.00 24 events/24
02:12:17 PM 3464 1.00 1.00 0.00 2.00 7 cmasm2d
02:12:17 PM 22667 1.00 2.00 0.00 3.00 6 pidstat
02:12:17 PM PID %usr %system %guest %CPU CPU Command
02:12:18 PM 17844 0.00 1.00 0.00 1.00 8 netstatlog
02:12:18 PM 22667 0.00 2.00 0.00 2.00 6 pidstat
02:12:18 PM 39321 1.00 0.00 0.00 1.00 3 bundle
6.6.9 time
- 最初の実行
- 全体: 5.1秒
- ユーザー時間: 2.8秒(チェクサム計算)
- システム時間: 0.3秒(ファイルを読み出すために必要なシステムコール)
- 失われた 2.0 秒: おそらくディスク I/O(読み出し)
- 2度目が早くなったのはおそらくファイルキャッシュ
/usr/bin/time には -v がある (シェル組み込みにはない)
6.6.10 DTrace
Linuxでまともに動かないので...略
関数のトレーシング
関数呼び出しの実行時間の分布
CPU クロスコール
クロスコールをトレーシングし、そこに至るまでのコードパスを表示することができる
割り込み
割り込みをトレーシングして解析することができる
Solaris には DTrace ベースのツール、intrstat がある
mega_sas ドライバが CPU 5 の 7.1% を消費
スケジューラのトレーシング
"sshd" という名前のプロセスが CPU 上で実行された時間をトレーシングしている
6.6.11 SystemTap
Linux では DTrace の代わりに SystemTap
6.6.12 perf
プロファイリングとトレーシングのためのツールコレクション
システムプロファイリング
- record コマンドを使うと、一定間隔でキャプチャされたサンプルが perf.data ファイルに書き込まれる
- そのあとで report コマンドを使うと、 ファイルの内容が見られる
- 9.43% の時間が dd プロセスで消費されていると言っている。そして、9.43% の時間のうちの 87.5% は、ここに表示されている ext4_file_write() のためのスタックで使われて いる
- debuginfo ファイルがなければ表示されない
プロセスのプロファイリング
# perf record -g command
スケジューラのレイテンシ
perf stat
CPC にもとづいて、CPU サイクルのふるまいについての高水準サマリ
コンテキストスイッチの回数、分岐予測ミスの回数、IPCなどがわかる
perf list で見れるもの一覧。perf stat -e で指定できる
$ perf stat -e instructions,cycles,L1-dcache-load-misses,LLC-load-misses,dTLB-load-misses ls
Performance counter stats for 'ls':
943,438 instructions # 0.52 insns per cycle
1,800,813 cycles # 0.000 GHz
22,958 L1-dcache-load-misses
1,802 LLC-load-misses
4,677 dTLB-load-misses
4-wide スーパースカラなら 4 insns per cycle までいくはず(?)なので、0.52 は遅いといえそう。
LLC-load-misses が最後のレベル、つまりL3キャッシュのロードミス ≒ メインメモリへのアクセスの回数回数(結局、何 cycle になるのかいまいちわからん)
Performance counter stats for 'sysbench --num-threads=8 --test=cpu --cpu-max-prime=1000 run':
527.029020 task-clock # 7.483 CPUs utilized
113 context-switches # 0.214 K/sec
18 cpu-migrations # 0.034 K/sec
750 page-faults # 0.001 M/sec
1,539,575,025 cycles # 2.921 GHz [83.46%]
882,133,872 stalled-cycles-frontend # 57.30% frontend cycles idle [83.59%]
624,548,115 stalled-cycles-backend # 40.57% backend cycles idle [67.16%]
523,014,992 instructions # 0.34 insns per cycle
# 1.69 stalled cycles per insn [83.35%]
142,563,177 branches # 270.503 M/sec [83.36%]
5,987,335 branch-misses # 4.20% of all branches [83.07%]
0.070432542 seconds time elapsed
cycles
は8コア全体(--num-threads=8
)で使用したサイクル数。試しに --num-threads=1
にしても時間は8倍に伸びるが cycles
の値はたいして変わらなかった。
1.69 stalled cycles per insn
がメモリにアクセスするなどしてストールした CPI の値。
ref. What are stalled-cycles-frontend and stalled-cycles-backend in 'perf stat' result?
注意: vm で実行すると以下のように <not supported> だらけになります. vmware だと起動時に有効にできるっぽいのだが、kvm や aws は ... ?
$ perf stat ls
0.528094 task-clock # 0.547 CPUs utilized
0 context-switches # 0.000 K/sec
0 cpu-migrations # 0.000 K/sec
138 page-faults # 0.261 M/sec
<not supported> cycles
<not supported> stalled-cycles-frontend
<not supported> stalled-cycles-backend
<not supported> instructions
<not supported> branches
<not supported> branch-misses
libcpc ないし perf は、CPUが持っているイベントを記録するようにプログラムできる少数のレジスタ (通常2個から8個)の値を参照している。この値はCPUごとに持っているものや持っていないものがあり、例えば stalled-cycles-frontend
および stalled-cycles-backend
は Intel Xeon X5650 では表示できたが、最近のCPUだと表示されなかったりするようだ。
パフォーマンスカウンタについては the Intel® 64 and IA-32 Architectures Optimization Reference Manual の Appendix B に詳細が書いてある。
ソフトウェアトレーシング
例) コンテキストスイッチソフトウェアイベントを使って、アプリケーションが CPU を 手放したときのトレーシングを行い、10 秒間コールスタックを集める
6.6.13 cpustat
Solaris ベースのシステムで CPC を解析するためのツール. 略
6.6.14 その他のツール
- oprofile:John Levon によるオリジナルの CPU プロファイリングツール。
- htop:CPU の使用状況の ASCII バーチャートを含み、オリジナルの top(1) よりも強力な対話的インターフェイスを備える。
- atop:システム規模の統計を top(1) よりもはるかに多く含むほか、プロセスアカウンティングを使って短命なプロセスの存在をキャッチする。
- /proc/cpuinfo:これを読むと、クロックスピードや機能フラグなど、プロセッサの詳細 がわかる。
- getdelays.c:遅延アカウンティング可観測性ツールの例であり、プロセスごとの CPU スケジューラレイテンシが含まれている。これについては、4 章で触れた。
- Valgrind:メモリのデバッグ、プロファイリングのためのツールキット†。関数呼び 出しをトレーシングしてコールグラフを集める callgrind(集めたコールグラフは、 kcachegrind で見ることができる)、指定したプログラムのハードウェアキャッシュ使用 状況を分析する cachegrind が含まれている。
6.6.15 ビジュアライゼーション
折れ線
- 伝統的な xload(1)
- 今日使われている CPU の数には適していない
- 1 万本の折れ 線が描かれたグラフは、絵画のようになってしまう
ヒートマップ
リアルタイムモニタリングソフトウェア(Joyent Cloud Analytics)が生成した画像
最上部の濃い直線は、使用率が 100% の CPU も いくつかあることを示している
秒未満オフセットヒートマップ
CPU は μ秒または m 秒単位のオーダーなので、1 秒間の平均を計算すると重要な情報が消えてしまうので、ヒートマップにしたほうがよい
白いスペース: スレッドが一切 CPU 上にない数百 m 秒の時間である。I/O待ちとか
フレームグラフ
- スタックトレースのプロファイリングは、出力が数千ページになってしまうかも => フレームグラフ
- 個々のボックス: スタック内の関数
- y 軸: スタックの深さ
- x 軸: サンプル数 (時間ではない。左から右への順序には意味はない <- アルファベット順)
- ボックスの幅: CPU時間 (遅いか、呼び出し回数が多いかどちらか)
- 実は色には意味がない
6.7 実験
6.7.1 アドホックテスト
CPU バウンドのシングルスレッドワークロードを作る
# while :; do :; done &
6.7.2 SysBench
素数を計算するシンプルなCPUベンチマークツールがある
# sysbench --num-threads=8 --test=cpu --cpu-max-prime=100000 run
6.8 チューニング
6.8.1 コンパイラオプション
コンパイラとコンパイラがコードの最適化のために提供しているオプションは、CPU のパフォーマンスに劇的な影響を与えることがある
6.8.2 優先度とクラスの操作
- nice(1)で優先度を変更. chrt(1)でスケジューリングポリシーを設定できる。
- もしくは setpriority(2) と sched_setscheduler(2) システムコール
6.8.3 スケジューラオプション
kernel を build するときに make menuconfig
で設定する
6.8.4 プロセスのバインド
taskset(1)
6.8.5 排他的CPUセット
カーネルの cpuset という機能
6.8.6 リソースコントロール
cgroup
- 特定プロセスをCPU使用率50%に制限した特定グループに所属させる
- 特定コアのみ許可を出した(cpuset)特定グループに所属させる
ref. https://oss.sios.com/yorozu-blog/cgroups-20150708
6.8.7 プロセッサオプション(BIOSチューニング)
- BIOSで、プロセッサレベルの機能を有効/無効化できる
- 例えば、Intel Turbo Boost を無効
- 通常はデフォルトで最大限のパフォーマンスを提供できるように設定されている
おわりに
- CPU使用率はメモリストールサイクル(メモリI/O待ち)を含む..!
- Linuxのロードアベレージは割り込み不能(I/O待ち)状態のタスクを含むので、純粋なCPUの負荷を表現していない...!
- psの%CPUは生存期間中のCPU使用率を表現するので監視には役に立たない...!
- perf stat で cpc (cpu performance counter) をプロファイリングできる...!
- LLC-load-misses: 最後のレベルのキャッシュのロードミス。メインメモリからのロードの計測値になる
- IPC(instructions per cycle): 命令幅4のCPUなら最大で4までいくはず(?)
- クロック周波数×IPCがCPUの速さの指標
- Intelはクロック周波数をあげる方向
- AMDはIPCをあげる方向
- 昨今のCPUはHT(ハイパースレッディング)をデフォルトで有効にしているはず、かつ1物理コアで2論理コアになるはずなので、OSからCPUコアが例えば24コアに見えても物理的には12コアしかない
- スレッド数12とスレッド数24でCPUバウンドなアプリを動かした場合、ほとんど速度が分からないはず
- スレッド数24で動かした場合、topでみるとそれぞれのコアが50%程度の使用率になっているかもしれないが、物理的には100%出ているかもしれない
- 最近の linux スケジューラは NUMA アーキテクチャやキャッシュも考慮するので、taskset ないし は cpuset で CPU affinity (特定のプロセスを特定のCPUコアで動かす)の設定をしても、おそらくあまり効果がない