社内有志で集まって詳解システムパフォーマンスの輪読会をしている。現在11章まで完了。6章 CPUを担当したので要点だけを書いておく。 資料もあるのだけど、公開すると引用の枠を超えているような気がするので自重しておく。

勉強メモなので、なにか間違いがあるかもしれません。見つけたら教えてください。

詳解 システム・パフォーマンス
Brendan Gregg 
オライリージャパン 
売り上げランキング: 36,763


要点

(本に書いてあったものより自分で調べて追記したものが多いかも)

  • top などで見れるCPU使用率はメモリストールサイクル(メモリI/O待ち)を含む
    • 純粋にCPUサイクルだけを見れているわけではない!
  • Linuxのロードアベレージは割り込み不能(I/O待ち)状態のタスクを含む
    • CPU使用率だけでもメモリI/O待ちを含んでいたのに、ロードアベレージはさらにディスク、ネットワークI/O待ちを含むので、純粋なCPUの負荷を表現していない。参考程度にしかならない。
  • psの%CPUは生存期間中のCPU使用率を表現するので監視には役に立たない
    • topのCPU使用率は一定期間中の使用率
  • perf stat で cpc (cpu performance counter, cpuレベルのパフォーマンス計測) をプロファイリングできる
    • LLC-load-misses: 最後のレベルのキャッシュのロードミス。メインメモリからのロードの計測値になる
    • IPC(instructions per cycle): 命令幅4のCPUなら4までいくはず(パイプライン考えるともっと伸びるはず?) ref. https://en.wikipedia.org/wiki/Instructions_per_second
  • クロック周波数×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コアで動かす)の設定をしても、おそらくあまり効果がない


perf stat

個々のCPUは、イベントを記録するようにプログラムできる少数のレジスタ (通常2個から8個)を持っていて、そこに CPU パフォーマンスカウンタ (CPC) の値を保存している。libcpc ないし perf は、この CPU の値を参照できる。

この値はCPUごとに持っているものや持っていないものがあり、例えば stalled-cycles-frontend および stalled-cycles-backend は Intel Xeon X5650 では表示できたが、最近のCPUだと表示されなかったりするようだ。

パフォーマンスカウンタについては the Intel® 64 and IA-32 Architectures Optimization Reference Manual の Appendix B に詳細が書いてある。


使い方

perf list で見れるもの一覧し、perf stat -e で指定する。

$ perf stat -e instructions,cycles,cache-misses,L1-dcache-load-misses,L1-dcache-store-misses,LLC-load-misses,LLC-store-misses,dTLB-load-misses,dTLB-store-misses ls

 Performance counter stats for 'ls':

         1,546,781      instructions              #    0.90  insns per cycle
         1,720,435      cycles
             3,736      cache-misses
            25,534      L1-dcache-load-misses
   <not supported>      L1-dcache-store-misses
             1,806      LLC-load-misses
     <not counted>      LLC-store-misses
     <not counted>      dTLB-load-misses           (0.00%)
     <not counted>      dTLB-store-misses          (0.00%)

       0.002274088 seconds time elapsed

4-wide スーパースカラなら 4 insns per cycle までいくはず(パイプライン考えたらもっといくはず?)なので、0.90 は遅いといえそう。 LLC-load-misses が最後のレベル(LLC: Last Level Cache)、つまりL3キャッシュのロードミス ≒ メインメモリへのアクセスの回数となる(結局、何 cycle になるのかいまいちわからんが)

$ perf stat sysbench --num-threads=8 --test=cpu --cpu-max-prime=1000 run

 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 はマルチコア全体で使用した合計サイクル数。試しに sysbench の並列度を --num-threads=1 に減らしても実行時間は伸びるが cycles の値はたいして変わらなかった。

1.69 stalled cycles per insn がメモリにアクセスするなどしてストールした CPI  (cycles per instructions) の値。 stalled-cycles-backend が主にメモリアクセスでストールしたサイクル数で、stalled-cycles-frontend が主に Fetch 命令でストールしたサイクル数。下図参照。

なお、vm guest で実行すると以下のように <not supported> だらけになるようだ。vmware だと起動時に有効にできるっぽいのだが、kvm や ec2 は無理そう ...? ec2 の場合は vm ではないハードウェア専有インスタンスを使うとかするしかなさそう。

$ 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