プログラミング
命令パイプラインハザードの解剖
The Anatomy of an Instruction Pipeline Hazard (hiraditya.github.io)
要約
この記事は、Nvidia B200 GPUにおける命令パイプラインハザード、特にコンパイラがハードウェアの依存関係を誤って処理することによって発生する「アンダースタール」バグに焦点を当てています。著者は、静的解析だけでは不十分であり、実際のシリコン上で動作するマイクロベンチマークを通じてハードウェアの正確なレイテンシを検証することの重要性を強調しています。predicate命令と分岐命令、および固定レイテンシの算術命令における具体的なハザード事例と、それらを検出・回避するためのアプローチについて解説しています。
全文翻訳
命令パイプラインハザードの解剖
目次
命令パイプラインハザードの解剖
B200パイプラインモデルのケーススタディ
方法論に関する注意:この記事のすべては、B200シリコン上で直接実行されたマイクロベンチマークの分析に基づいています。NvidiaはGPUの命令レイテンシ、パイプラインの深さ、またはスコアボードエンコーディングの詳細を公開していません。ここで説明する数値とメカニズムは、私の経験的な最良の理解を表しています。読者は自身の責任でデューデリジェンスを行い、自身のハードウェアで検証すべきです。
Nvidia B200のような最新のディープパイプラインGPUを扱う際、静的解析は命令スケジュールの検証に必要ですが、それだけでは不十分です。スケジューラが依存関係の追跡で100%のカバレッジを報告したにもかかわらず、生成されたコードが実際のシリコン上で静かに失敗するのを見るのは、謙虚な経験です。
なぜこれが起こるのでしょうか?ハードウェアパイプライン自体が、正しさの最終的な裁定者です。
スケジューラが依存関係を過小評価(under-stall)すると、プロデューサの結果がレジスタファイルに確実にコミットされる前に、コンシューマ命令がパイプラインに発行されてしまいます。ハードウェアは例外を発生させません。代わりに、スケジュールを実行し、古い状態を読み取り、計算の残りの部分に不正な値を伝播させます。
これらはシリコンの欠陥ではありません。これらは、ハードウェアがコンパイラの誤った仮定を露呈するスケジュールの違反です。コンパイラバックエンドでは、コンパイラエンジニアは一般的に「過剰なスタールはパフォーマンスバグだが、過小なスタールは静かな正しさのバグである」というルールに従います。
これらの問題を検出するために、私は最小限で再現可能なオンシリコンテストに裏打ちされたハードウェアハザードのレジストリを構築しました。
前提条件と用語
特定のB200ハザードを詳しく見る前に、いくつかの基本的なコンテキストを確立しておくと役立ちます。
命令スケジューリング: コンパイラバックエンドのフェーズで、ハードウェアの利用率を最大化するために命令を並べ替えます。依存関係のある命令間に明示的に遅延(スタール)または同期(スコアボード)をエンコードする必要があります。
パイプラインの深さ: 命令が通過するステージ数(フェッチ、デコード、実行、ライトバック)。パイプラインが深いほど、命令の完了に時間がかかります。
RAW(Read-After-Write)ハザード: 前の命令がレジスタへの書き込みを完了する前に、別の命令がそのレジスタを読み取ろうとするシナリオ。
可変レイテンシ操作: 実行時間が固定されていない操作。これには、グローバルメモリロード(LDG)、共有メモリ操作(LDS)、アトミック操作(ATOM)、およびマルチファンクションユニット(MUFU)が含まれます。
シリコンは嘘をつかない
現代のGPUストリーミングマルチプロセッサ(SM)は、極端なスループットのために設計されています。これを達成するために、パイプラインは深くなっています。ハードウェアは、コンパイラが依存関係情報を明示的にエンコードすることに依存しています。
命令Aが値Aを生成し、命令Bがその値Aを消費する単純なデータフローパスを考えてみましょう。
(フローチャートの図の説明)
命令Bが早すぎると、ライトバックが完了する前に、そのレジスタ読み取りフェーズでレジスタの古い内容がフェッチされてしまいます。
CPUでは、洗練されたアウトオブオーダー実行エンジンがこれらのレイテンシを動的にマスクします。GPUでは、ALUのためにダイ面積を最大化するという哲学があります。これにより、命令スケジューリングの複雑さがコンパイラに押し付けられます。これは、同じ哲学(スケジューリングの決定をハードウェアからコンパイラにオフロードする)を共有するVLIWアーキテクチャを彷彿とさせます。
このアーキテクチャ上のトレードオフは、コンパイラエンジニアがパイプラインの深さやバリアエンコーディングのような低レベルの制約について、細心の注意を払う必要があることを意味します。
述語コンシューマのアンダースタール
厳格な静的チェックをすり抜ける最も難しいバグがあります。最近、B200で作業中に、命令スケジューラにおける述語評価に関する重大なバグを発見しました。これは、テストスイート全体でRAWカバレッジが完全であると主張する静的メトリクスがあったにもかかわらず発生しました。
このパターンは、条件を計算する整数セット述語命令(ISETP)を含みます。これは結果を述語レジスタに書き込み、その後、分岐命令によって読み取られます。これは、ループを定義するバックエッジ分岐で古典的に見られます。
// 1. いくつかの条件に基づいて述語P1を生成します。
// R0とR1が比較され、ブール結果がP1に書き込まれます。
ISETP.GE.AND P1, PT, R0, R1, PT;
// 2. 分岐ターゲット条件としてP1を消費します。
// P0は実行ガード(スレッドはアクティブか?)、P1は分岐条件です。
@!P0 BRA P1, target;
失敗のメカニズム
このバグは、B200の述語処理で作業しているときに表面化しました。コンパイラは、分岐のガード述語P0を正しく使用として記録しましたが、分岐条件オペランドP1を見落としていました。
その結果、ISETP → BRA RAW依存関係は完全に失われました。スケジューラは必要な述語レイテンシのスタールを挿入できませんでした。
(シーケンス図の説明)
分岐は、ISETPの約4サイクル後に発行されました。これは、述語の実際のモデル化されたレイテンシである13サイクルが経過するずっと前でした。分岐命令は古い値を読み取り、間違った実行パスを取り、その結果、静かな誤計算が発生しました。
グラウンドトゥルースの緩和策
これを防ぐために、スケジューラのオペランド分析は、@!P0 BRA P1 の両方の使用を正しく識別する必要があります。しかし、真の防御はオンシリコンプローブです。
私はISETPと分岐の間のスタールサイクルをスイープし、正しい実行に必要な最小レイテンシを検証します。
ベースラインの述語レイテンシは、アーキテクチャモデルがしばしば近似であるため、オンデバイスで動的にプローブする必要があります。B200では、マイクロベンチマークプローブは、モデル化された13サイクルと実際のパイプラインの深さとの乖離を確認しました。物理的な述語レイテンシのフロアは約4サイクルです。
固定レイテンシRAWアンダースタール
固定レイテンシ算術命令は、行列乗算とテンソルコアワークロードのバックボーンを形成します。それらは、宛先レジスタが安全に読み取られる前に、正確な固定サイクル遅延を必要とします。例としては以下が挙げられます。
FFMA(単精度融合乗算加算)
DFMA(倍精度融合乗算加算)
スケジューラがハードウェアの固定レイテンシよりも厳密に少ないサイクル数でスタールを発行すると、コンシューマは宛先レジスタを早期に読み取ります。
レイテンシ測定とトレードオフ
B200での直接ハードウェアプローブを通じて、実行が不正(古い読み取り)から正しい(有効な読み取り)に移行する正確なレイテンシフロアを測定しました。
操作 | 精度 | 測定サイクルフロア | 検証シグナル
FFMA | FP32 | 4サイクル | スタール3は誤った結果を生成。スタール4は正しい結果を生成。
DFMA | FP64 | 8サイクル | スタール7は誤った結果を生成。スタール8は正しい結果を生成。
ここでトレードオフに注目してください。より高い精度の算術演算は、自然に深いパイプラインを必要とします。FP64ユニットは、FP32ユニットのちょうど2倍のレイテンシを必要とします。
レイテンシ検証テストを構築する際には、整数線形チェーンではなく、浮動小数点再帰チェーンを構築することが重要です。整数チェーンは、ハードウェアのパイプラインフォワーディングネットワークによって折りたたまれたりバイパスされたりする可能性があり、アンダースタールをマスクします。浮動小数点チェーンは、厳密な実行パイプラインステージと丸めのため、依存関係レイテンシを可視化します。
これらのレイテンシを検証するには、