この記事の目的
現代ハードウェアの計算性能を評価する尺度であるメモリ律速の概念とルーフラインモデルについて理解を深めること。
対象読者はメモリバンド幅やOPSなどの概念があまりわかっていない人です。例えば本記事を通し、あるアルゴリズムが速度が十分に出ない時、それがハードウェアのどの性能(メモリか演算)に律速されてるかイメージできるようになるのが目標です。
思うままに書いていたら肝心のメモリの話まで行きませんでした。そのため前編はクロックや演算(OPS)についてです。
この図はTPUの論文に出てくるRoofline modelです。この図が意味するところを理解するのが本記事の最終目標となります。
ハードウェアの速度をどう評価すればいいか?
ベンチマークによる評価
ハードウェア速度の評価は昔から大きな問題でした。わかりやすい評価指標にある代表的なアルゴリズム数種類で性能をベンチマークするSPEC)などがありますが、ベンチマークの数値だけ見ても実際何故性能が出てるか紐解くのは難しいです。またベンダーの出しているメモリバンド幅や計算性能なども読み解けないかもしれません。ただSPECは同じアルゴリズムを異なるハード上で行うため、ある程度フェアな比較はできます。
CPUクロックによる評価
昔のハードウェア速度はcpuクロックで測れてました。というのもCPUは大体1クロックで一度の演算をするため、クロック速度から大体の性能を予測することができます。一方で並列演算などの要素が入ってくるとこの方法で性能を見積もるのは難しくなります。
またプログラムの計算時間を見積もる際には十分通用します。例えば競プロでは計算量がO(1e9)のプログラムであれば1GHz CPUで1秒で回ると概算できることを最初に習いますね。一般的に競プロのアルゴリズムはシリアル処理なのでこのような見積もりが当たります。(並列化速度はライブラリの出来なので競技性はないですね。。)
OPS、SIMDによる評価
しかし並列演算を行うと単純にクロック速度だけではハードウェアの性能を正確に評価できなくなります。
例えば近代CPUには並列演算(ベクトル演算とも)が実装されています。並列演算回路は多数の演算機(ALU)を並べ、N個のデータを受け同時に演算を行いN個の出力を出すというものです。
この並列化は全ての入力データに対し同じ計算を行うため(例えばベクトルに対し+5を行うなど)、並列化が可能になります。これはスパコン用語で言うとSIMD(single instruction Multiple Data)という名のデータ並列性であり、良く機械学習、グラフィックス、科学演算などで使われます。グラフィックスなどはこのように全てのピクセルに対し同じ計算(定数の足し算や掛け算)を行うためSIMDによって膨大な速度ブーストが得られます。身近な実例ですとnumpy arrayを使うと中では妖精さんがSIMD機能を使ってくれます。そのため生pythonより遥かに高速にベクトルや行列演算をしてくれます。
またGPUも大雑把に言うとSIMDプロセッサの延長線と理解することができます(GPUはSIMDに加えマルチスレッディング、マルチコア性能がCPUに対し大幅に加えられたヘテロジニアスマルチコアシステムですがGPUアーキテクチャに関してはそれはそれで本が一冊書けるのでそこまで今回は深く立ち入りません。)。
CPUとGPUのアーキテクチャ的な違い、SIMDやマルチスレッディングについて深堀りはCMU大の講義が詳しいです。
http://15418.courses.cs.cmu.edu/spring2017/home(15418.courses.cs.cmu.edu]
また日本語で詳しくSIMDや並列演算について知りたい人はこれとか読んでみて下さい。
https://kaityo256.github.io/sevendayshpc/day7/index.html(https://kaityo256.github.io/sevendayshpc/day7/index.htmlkaityo256.github.io]
A100チップのOPSを計算してみる
とにかく並列演算を持つハードウェアの演算性能は単純にSIMDのデータ数N, clock rate, 演算器コア数の掛け算で求めることができ、OPS(operations per second)といいます。
例えばnvidia GPUのOPsを計算して製品スペックと一致するか見てみましょう。
nVidia A100のスペックは以下です。
https://videocardz.com/newz/nvidia-announces-tesla-a100-specifications
FP32のOPSは
OPS = CUDAコア数 * GPUクロック * 2
で求められます。*2としているのはCUDAコアは一クロックで掛け算と足し算両方を実行できるため(MAC)、そのような回路は1クロックで2OPSの動作ができると慣例的に計算します。
そのため、
OPS = 6912 * 1.41GHz * 2 = 19.49TOPS(テラOPS)
とスペックの19.5TOPSの値が得られます。
同様にTensorCoreのOPSも計算する事も可能です。
ここによると)A100はTensorCoreを432個持ち、一クロックで16*32コの計算を扱えるとのことです。そのため
OPS = 16 * 32 * 432 * 1.41GHz * 2 = 623.7TOPS
とスペック状のBF16の624TOPSの値が得られることがわかります。TensorCoreの設計上16*32の演算をフルに扱えるのはBF16のみで、FP32となると演算器の数は半分となってしまいTOPSも半減します。一方でINT8では実効的に演算器の数を倍に増やしたかのように見せられるため、TOPSは倍増します。