この記事では、GPU がグラフィックス、ニューラル ネットワーク、ディープラーニングのタスクに優れ、CPU がさまざまなシーケンシャルで複雑な汎用コンピューティング タスクに優れている理由を理解するために、基本的な低レベルの詳細をいくつか説明します。この記事では、調査してもう少し詳しく理解する必要があったトピックがいくつかありましたが、そのうちのいくつかについては簡単に触れるだけにします。CPU と GPU の処理の絶対的な基本だけに焦点を当てるために意図的に行っています。
以前のコンピュータは専用デバイスでした。ハードウェア回路と論理ゲートは、特定の一連の動作を実行するようにプログラムされていました。何か新しいことをしなければならない場合は、回路の配線を変更する必要がありました。「何か新しいこと」は、2 つの異なる方程式の数学的計算を実行するという単純なことかもしれません。第二次世界大戦中、アラン チューリングはエニグマ マシンを破るプログラム可能なマシンの開発に取り組んでおり、後に「チューリング マシン」の論文を発表しました。同じ頃、ジョン フォン ノイマンと他の研究者も、基本的に次のことを提案するアイデアに取り組んでいました。
コンピュータ内のすべてがバイナリであることは周知の事実です。文字列、画像、ビデオ、オーディオ、OS、アプリケーション プログラムなどはすべて 1 と 0 で表されます。CPU アーキテクチャ (RISC、CISC など) の仕様には命令セット (x86、x86-64、ARM など) があり、CPU メーカーはこれに準拠する必要があり、OS がハードウェアとインターフェイスするために使用できます。
データを含む OS およびアプリケーション プログラムは、CPU で処理するために命令セットとバイナリ データに変換されます。チップ レベルでは、処理はトランジスタとロジック ゲートで行われます。2 つの数値を加算するプログラムを実行すると、加算 (「処理」) はプロセッサ内のロジック ゲートで行われます。
フォン ノイマン アーキテクチャの CPU では、2 つの数値を加算する場合、回路内の 2 つの数値に対して 1 つの加算命令が実行されます。そのミリ秒のほんの一部では、処理ユニットの (実行) コアで加算命令のみが実行されます。この詳細が常に私を魅了してきました。
上の図のコンポーネントは一目瞭然です。詳細と詳しい説明については、この優れた記事を参照してください。最新の CPU では、1 つの物理コアに複数の整数 ALU、浮動小数点 ALU などを含めることができます。繰り返しますが、これらのユニットは物理的なロジック ゲートです。
GPU をより深く理解するには、CPU コアの「ハードウェア スレッド」を理解する必要があります。ハードウェア スレッドは、CPU コアの実行ユニットで、CPU クロック サイクルごとに実行できるコンピューティングの単位です。これは、コアで実行できる作業の最小単位を表します。
上の図は、CPU 命令サイクル/マシン サイクルを示しています。これは、CPU が 1 つの命令を実行するために実行する一連のステップです (例: c=a+b)。
フェッチ:プログラム カウンター (CPU コア内の特別なレジスタ) は、どの命令をフェッチする必要があるかを追跡します。命令はフェッチされ、命令レジスタに格納されます。単純な操作の場合、対応するデータもフェッチされます。
デコード:命令をデコードして演算子とオペランドを確認します。
実行:指定された操作に基づいて、適切な処理ユニットが選択され、実行されます。
メモリ アクセス:命令が複雑であるか、追加のデータが必要な場合 (いくつかの要因が考えられます)、実行前にメモリ アクセスが行われます (上の図では、簡略化のため無視されています)。複雑な命令の場合、初期データはコンピューティング ユニットのデータ レジスタで使用できますが、命令を完全に実行するには、L1 および L2 キャッシュからのデータ アクセスが必要です。つまり、コンピューティング ユニットが実行されるまで少し待機時間が発生し、待機時間中もハードウェア スレッドがコンピューティング ユニットを保持したままになります。
書き戻し:実行によって出力が生成される場合 (例: c=a+b)、出力はレジスタ/キャッシュ/メモリに書き戻されます。 (上記の図や記事の後半では、簡潔にするために無視されています)
上の図では、t2 でのみ計算が行われています。残りの時間、コアはアイドル状態です (作業は行われません)。
最新の CPU には、基本的にクロック サイクルごとに (フェッチ、デコード、実行) ステップを同時に実行できるようにする HW コンポーネントがあります。
単一のハードウェア スレッドがクロック サイクルごとに計算を実行できるようになりました。これを命令パイプラインと呼びます。
フェッチ、デコード、メモリ アクセス、およびライト バックは、CPU 内の他のコンポーネントによって実行されます。適切な言葉がないため、これらは「パイプライン スレッド」と呼ばれます。パイプライン スレッドは、命令サイクルの実行段階にあるときにハードウェア スレッドになります。
ご覧のとおり、t2 から毎サイクルで計算出力が得られます。以前は、3 サイクルに 1 回計算出力が得られていました。パイプライン化により、計算スループットが向上します。これは、フォン ノイマン アーキテクチャで処理のボトルネックを管理する手法の 1 つです。アウトオブオーダー実行、分岐予測、投機的実行などの他の最適化もあります。
これは、CPU について最後に議論したい概念です。この概念を最後に、GPU について説明しましょう。クロック速度が上昇するにつれて、プロセッサも高速化され、効率も向上しました。アプリケーション (命令セット) の複雑さが増すにつれて、CPU の計算コアは十分に活用されなくなり、メモリ アクセスの待機に多くの時間を費やすようになりました。
つまり、メモリのボトルネックが発生していることがわかります。コンピューティング ユニットはメモリ アクセスに時間を費やしており、有用な作業は何も行っていません。メモリは CPU よりも数桁遅く、その差はすぐには縮まりそうにありません。アイデアは、単一の CPU コアのいくつかのユニットでメモリ帯域幅を増やし、メモリ アクセスを待機しているときにコンピューティング ユニットを利用できるようにデータを準備しておくというものでした。
ハイパースレッディングは、2002 年に Intel の Xeon および Pentium 4 プロセッサで利用可能になりました。ハイパースレッディング以前は、コアごとに 1 つのハードウェア スレッドしかありませんでした。ハイパースレッディングを使用すると、コアごとに 2 つのハードウェア スレッドが存在します。これは何を意味しますか? 一部のレジスタ、プログラム カウンター、フェッチ ユニット、デコード ユニットなどの処理回路が重複します。
上の図は、ハイパースレッディング機能を備えた CPU コアの新しい回路要素を示しています。これは、1 つの物理コアがオペレーティング システムから 2 つのコアとして見える仕組みです。ハイパースレッディング機能が有効になっている 4 コア プロセッサを使用している場合、OS からは 8 つのコアとして見えます。L1 - L3 キャッシュ サイズは、追加のレジスタに対応するために増加します。実行ユニットは共有されることに注意してください。
プロセス P1 と P2 が a=b+c、d=e+f を実行していると仮定します。これらは、HW スレッド 1 と 2 により、1 クロック サイクルで同時に実行できます。前述のように、単一の HW スレッドでは、これは不可能です。ここでは、ハードウェア スレッドを追加してコア内のメモリ帯域幅を増やし、処理ユニットを効率的に利用できるようにします。これにより、コンピューティングの同時実行性が向上します。
いくつかの興味深いシナリオ:
この記事をチェックして、 Colab ノートブックも試してみてください。行列乗算が並列化可能なタスクであること、並列コンピューティング コアによって計算が高速化される仕組みがわかります。
コンピューティング能力が増加するにつれて、グラフィックス処理の需要も増加しました。UI レンダリングやゲームなどのタスクでは並列処理が必要なため、回路レベルで多数の ALU と FPU が必要になります。シーケンシャル タスク用に設計された CPU では、これらの並列ワークロードを効果的に処理できませんでした。そのため、グラフィックス タスクでの並列処理の需要を満たすために GPU が開発され、後にディープラーニング アルゴリズムの高速化に採用される道が開かれました。
強くお勧めします:
CPU と GPU のコア、ハードウェア スレッド、クロック速度、メモリ帯域幅、オンチップ メモリは大きく異なります。例:
汎用コンピューティングのピーク パフォーマンスを得ることは非常に主観的なため、この数値は GPU との比較に使用されます。この数値は理論上の最大制限であり、FP64 回路が最大限に使用されていることを意味します。
CPU で見た用語は、必ずしも GPU にそのまま当てはまるわけではありません。ここでは、コンポーネントとコア NVIDIA A100 GPU について説明します。この記事の調査中に驚いたことの 1 つは、CPU ベンダーがコアの実行ユニットで使用できる ALU、FPU などの数を公開していないことです。NVIDIA はコアの数について非常に透明性が高く、CUDA フレームワークは回路レベルで完全な柔軟性とアクセスを提供します。
上の GPU の図では、L3 キャッシュがなく、L2 キャッシュが小さく、制御ユニットと L1 キャッシュが小さいながらもかなり多く、処理ユニットも多数あることがわかります。
最初に理解していただくために、上の図の GPU コンポーネントと、それに対応する CPU コンポーネントを示します。私は CUDA プログラミングをしたことがありませんので、CPU コンポーネントと比較すると、最初に理解しやすくなります。CUDA プログラマーはこれをよく理解しています。
グラフィックスおよびディープラーニングのタスクでは、SIM(D/T) [単一命令マルチデータ/スレッド] タイプの実行が必要です。つまり、単一の命令で大量のデータを読み取って処理します。
CPU の命令パイプラインとハイパースレッディングについて説明しましたが、GPU にもその機能があります。実装方法と動作方法は若干異なりますが、原理は同じです。
CPU とは異なり、GPU (CUDA 経由) はパイプライン スレッド (メモリからデータを取得してメモリ帯域幅を利用する) への直接アクセスを提供します。GPU スケジューラは、最初にコンピューティング ユニット (関連する共有 L1 キャッシュとコンピューティング オペランドを格納するレジスタを含む) を埋めようとし、次に「パイプライン スレッド」がレジスタと HBM にデータをフェッチします。ここでも強調しておきたいのは、CPU アプリ プログラマーはこれについて考えておらず、「パイプライン スレッド」とコアあたりのコンピューティング ユニット数に関する仕様は公開されていないということです。Nvidia はこれらを公開するだけでなく、プログラマーに完全な制御を提供します。
これについては、CUDA プログラミング モデルとモデル サービング最適化手法における「バッチ処理」に関する専用の投稿でさらに詳しく説明し、これがどれほど有益であるかを説明します。
上の図は、CPU と GPU コアでのハードウェア スレッドの実行を示しています。CPU パイプラインの前の「メモリ アクセス」セクションを参照してください。この図は、CPU の複雑なメモリ管理により、この待機時間が L1 キャッシュからレジスタにデータをフェッチするのに十分なほど短く (数クロック サイクル) なっていることを示しています。L3 またはメイン メモリからデータをフェッチする必要がある場合、データがすでにレジスタにある他のスレッド (ハイパー スレッディング セクションで説明しました) が実行ユニットを制御します。
GPU では、オーバーサブスクリプション (パイプライン スレッドとレジスタの数が多い) とシンプルな命令セットのため、実行を保留しているレジスタにはすでに大量のデータが存在します。実行を待機しているこれらのパイプライン スレッドはハードウェア スレッドになり、GPU のパイプライン スレッドは軽量であるため、クロック サイクルごとに実行を実行します。
オーバーゴールって何ですか?
これが、小さい行列の行列乗算のレイテンシが CPU と GPU でほぼ同じになる主な理由です。試してみてください。
タスクは十分に並列化されている必要があり、データはコンピューティング FLOP とメモリ帯域幅を飽和させるのに十分な大きさである必要があります。 単一のタスクが十分に大きくない場合は、ハードウェアを完全に活用するために、メモリとコンピューティングを飽和させるには、複数のそのようなタスクをパックする必要があります。
計算強度 = FLOP / 帯域幅。つまり、計算ユニットが 1 秒あたりに実行できる作業量と、メモリが 1 秒あたりに提供できるデータ量の比率です。
上の図では、レイテンシが高くなり、メモリ帯域幅が低くなるにつれて、コンピューティングの強度が増すことがわかります。コンピューティングを最大限に活用するには、この数値をできるだけ小さくする必要があります。そのためには、コンピューティングを迅速に実行できるように、L1 / レジスタにできるだけ多くのデータを保持する必要があります。HBM から単一のデータを取得する場合、単一のデータに対して 100 回の演算を実行しても価値がある操作はほとんどありません。100 回の演算を行わない場合、コンピューティング ユニットはアイドル状態になります。ここで、GPU 内の多数のスレッドとレジスタが役立ちます。L1 / レジスタにできるだけ多くのデータを保持して、コンピューティングの強度を低く抑え、並列コアをビジー状態に維持します。
CUDA コアは 1 つの 1x1 FP64 MMA しか実行できないのに対し、Tensor コアはクロック サイクルごとに 4x4 FP64 MMA 命令を実行できるため、CUDA コアと Tensor コアの計算強度には 4 倍の違いがあります。
多数のコンピューティング ユニット (CUDA および Tensor コア)、多数のスレッドとレジスタ (サブスクリプション以上)、削減された命令セット、L3 キャッシュなし、HBM (SRAM)、シンプルで高スループットのメモリ アクセス パターン (CPU と比較して - コンテキスト スイッチング、マルチ レイヤー キャッシュ、メモリ ページング、TLB など) は、並列コンピューティング (グラフィックス レンダリング、ディープラーニングなど) において GPU が CPU よりはるかに優れている原則です。
GPU は当初、グラフィックス処理タスクを処理するために作成されました。AI 研究者は、CUDA と、CUDA コアを介した強力な並列処理への直接アクセスを活用し始めました。NVIDIA GPU には、テクスチャ処理、レイ トレーシング、ラスター、ポリモーフ エンジンなど (グラフィックス固有の命令セットとしましょう) があります。AI の採用が増えるにつれて、4x4 マトリックス計算 (MMA 命令) に優れたディープラーニング専用の Tensor コアが追加されています。
2017 年以降、NVIDIA は各アーキテクチャの Tensor コアの数を増やしてきました。しかし、これらの GPU はグラフィック処理にも優れています。GPU の命令セットと複雑さははるかに少ないですが、ディープラーニング (特に Transformer アーキテクチャ) に完全に特化しているわけではありません。
FlashAttention 2 は、トランスフォーマー アーキテクチャのソフトウェア レイヤー最適化 (アテンション レイヤーのメモリ アクセス パターンに対する機械的な共感) であり、タスクを 2 倍高速化します。
CPU と GPU に関する基礎原理に基づく深い理解により、トランスフォーマー アクセラレータの必要性が理解できます。トランスフォーマー アクセラレータとは、並列処理のための多数の計算ユニット、削減された命令セット、L1/L2 キャッシュなし、HBM に代わる大容量の DRAM (レジスタ)、トランスフォーマー アーキテクチャのメモリ アクセス パターンに最適化されたメモリ ユニットを備えた専用チップ (トランスフォーマー操作専用の回路) です。結局のところ、LLM は (Web とモバイルに続く) 人間の新しい仲間であり、効率とパフォーマンスのために専用チップが必要です。