隠されたボトルネック:GPUメモリ階層があなたのコンピューティング体験に与える影響
紹介
GPUメモリ階層は、深層学習の研究者や実践者にとって、ますます関心のある分野になっています。メモリ階層についての直感を構築することで、開発者はメモリアクセスのレイテンシを最小限に抑え、メモリ帯域幅を最大化し、電力消費を削減することができ、これにより処理時間を短縮し、データ転送を加速し、コスト効率の良い計算資源の利用が実現します。メモリアーキテクチャに関する徹底的な理解は、開発者がスケールでのピークGPU能力を達成するのに役立ちます。
CUDA リフレッシャー
CUDA(統一コンピューティングデバイスアーキテクチャ)は、NVIDIAによってGPUを構成するために開発された並列計算プラットフォームです。
CUDAプログラムの実行は、ホストコード(CPUの直列コード)がカーネル関数を呼び出すと始まります。この関数呼び出しは、デバイス(GPU)上で異なるデータコンポーネントを並行して処理するスレッドのグリッドを起動します。
スレッドは、プログラムのコード、コード内の現在の実行位置、およびその変数やデータ構造の値で構成されています。スレッドのグループはスレッドブロックを形成し、スレッドブロックのグループがCUDAカーネルグリッドを構成します。ソフトウェアコンポーネントであるスレッドとスレッドブロックは、ハードウェアのアナログであるCUDAコアとCUDAストリーミングマルチプロセッサ(SM)に直接対応しています。
これらすべてがGPUの構成要素を構成しています。
スレッドはブロックに整理され、ブロックはグリッドに整理されます。図はNVIDIAの技術ブログから取られています。
NVIDIA H100ホワイトペーパーからの図。
H100は新しいスレッドブロッククラスタアーキテクチャを導入し、GPUの物理プログラミングアーキテクチャを拡張して、スレッド、スレッドブロック、スレッドブロッククラスタ、グリッドを含むようになりました。
CUDAメモリタイプ
CUDAデバイスで使用されるメモリストレージタイプには、さまざまなアクセス性と持続時間の程度があります。CUDAプログラマーが変数を特定のCUDAメモリタイプに割り当てると、変数へのアクセス方法、アクセス速度、およびその可視性の範囲を決定します。
こちらは異なるメモリタイプの概要です。
この図は、教科書「プログラミングマッシブパラレルプロセッサ」の第4版第5章からのものです。
レジスタメモリ は各スレッドに対してプライベートです。これは、その特定のスレッドが終了すると、そのレジスタのデータが失われることを意味します。
ローカルメモリは各スレッドに対してもプライベートですが、レジスタメモリよりも遅くなります。
共有メモリは同じブロック内のすべてのスレッドがアクセス可能で、ブロックの寿命とともに持続します。
グローバルメモリは、グリッド/ホストの間、データを保持します。すべてのスレッドとホストは、グローバルメモリにアクセスできます。
定数メモリ は読み取り専用で、カーネルの実行中に変更されないデータのために設計されています。
テクスチャメモリは、物理的に隣接したデータアクセスに理想的な別の読み取り専用メモリタイプです。その使用はメモリトラフィックを軽減し、グローバルメモリと比べてパフォーマンスを向上させることができます。
GPUメモリ階層
スピードと容量のトレードオフ
メモリアクセス効率に関して理解することが重要です。バンド幅とメモリ容量の間にはトレードオフがあります。 高速は低容量と相関しています。
レジスター
レジスタはGPU上で最も高速なメモリコンポーネントであり、CUDAコアに直接データを供給するレジスタファイルを構成しています。カーネル関数は、スレッドにプライベートな変数を保存し、頻繁にアクセスされるためにレジスタを使用します。
レジスタと共有メモリはどちらもオンチップメモリであり、これらのメモリに存在する変数は非常に高速度で並列にアクセスすることができます。
レジスタを効果的に活用することにより、データの再利用を最大化し、パフォーマンスを最適化することができます。
キャッシュレベル
現代のプロセッサには複数のレベルのキャッシュが存在します。プロセッサへの距離は、これらのキャッシュの番号付けに反映されています。
L1キャッシュ
L1、またはレベル1キャッシュは、プロセッサコアに直接接続されています。これは、アクティブデータの量がSMのレジスタファイルの容量を超えるときに、バックアップストレージエリアとして機能します。
L2キャッシュ
L2またはレベル2キャッシュはサイズが大きく、通常はSM(ストリーミングマルチプロセッサ)間で共有されます。L1キャッシュとは異なり、L2キャッシュは1つだけです。
恒久キャッシュ
定数キャッシュは、各カーネルのために頻繁に使用される変数をキャプチャし、パフォーマンスを向上させます。
大規模並列プロセッサ用にメモリシステムを設計する際、定数メモリ変数が存在します。これらの変数を書き換えることは冗長で無意味です。したがって、定数キャッシュのような専用メモリシステムは、計算コストのかかるハードウェアロジックの必要性を排除します。
H100による新しいメモリ機能
NVIDIAホッパーストリーミングマルチプロセッサ。図はNVIDIA H100ホワイトペーパーからのものです。
ホッパーは、そのH100 GPUラインを通じて、以前のNVIDIAマイクロアーキテクチャに比べて性能を向上させる新機能を導入しました。
スレッドブロッククラスタ
この記事で前述したように、スレッドブロッククラスターはH100とともに登場し、CUDAプログラミング階層を拡張しました。スレッドブロッククラスターは、単一のSM上のスレッドブロックでは許可されるよりも大きなスレッドグループに対して、より大きなプログラム制御を可能にします。
非同期実行
非同期実行における最新の進展は、ホッパーアーキテクチャにテンソルメモリーアクセラレーター(TMA)と非同期トランザクションバリアを導入しました。
テンソルメモリアクセラレーター (TMA) ユニットは、グローバルメモリと共有メモリの間で大容量データブロックの効率的な転送を可能にします。
非同期トランザクションバリアは、CUDAスレッドおよびオンチップアクセラレーターの同期を可能にし、これらが物理的に異なるSMに配置されていても関係ありません。
H100は、Ampere GPUアーキテクチャで導入された非同期バリアと、新しい非同期トランザクションバリアの両方を含んでいます。
結論
特定のCUDAメモリタイプに変数を割り当てることは、プログラマーにその動作を正確に制御する手段を提供します。この指定は、変数がどのようにアクセスされるかだけでなく、そのアクセスが行われる速度にも影響します。レジスタや共有メモリのようにアクセス速度が速いメモリタイプに保存されている変数は、迅速に取得でき、計算を加速させます。それに対して、グローバルメモリのような遅いメモリタイプにある変数は、遅い速度でアクセスされます。さらに、メモリタイプの割り当ては、変数の使用範囲や他のスレッドとの相互作用にも影響します。割り当てられたメモリタイプは、その変数が単一のスレッド、スレッドブロック、またはグリッド内のすべてのスレッドにアクセス可能かどうかを決定します。最後に、AIワークフローにおける現在のSOTA GPUであるH100は、スレッドブロッククラスタ、テンソルメモリアクセラレーター(TMA)ユニット、および非同期トランザクションバリアのようなメモリアクセスに影響を与えるいくつかの新機能を導入しました。
参考文献
プログラミング大規模並列プロセッサ(第4版)
ホッパーのホワイトペーパー
CUDA リフレッシャー: CUDA プログラミングモデル | NVIDIA テクニカルブログ





