2 ポイント 投稿者 GN⁺ 2025-02-27 | 1件のコメント | WhatsAppで共有
  • DeepGEMMは、現代のLLMにおける中核計算プリミティブであるGEMM、fused MoE、MQA scoring、HyperConnectionなどを1つのCUDAコードベースにまとめた高性能tensor coreカーネルライブラリ
  • すべてのカーネルは軽量なJITモジュールとして実行時にコンパイルされ、インストール時のCUDAコンパイルは不要で、C++20、CUDA Toolkit、PyTorch、CUTLASS 4.0以上を必要とする
  • CUTLASSとCuTeの一部概念を活用しているが、テンプレートや代数構造に重く依存せず、限られた数の中核カーネル関数によりNVIDIA GPUカーネル最適化の学習に取り組みやすいよう設計されている
  • 対応範囲にはFP8、FP4、BF16 GEMM、grouped GEMM、DeepSeek v3.2向けのMQA logitsカーネル、通信と計算を重ねるMega MoEまでが含まれ、SM90とSM100ではメモリレイアウトの制約が異なる
  • 軽量設計でありながら、さまざまな行列shapeで専門家チューニング済みライブラリと同等以上の性能を目指しており、H800で最大1550 TFLOPSを達成した更新が含まれる

DeepGEMMの目的と設計

  • DeepGEMMは、最新の大規模言語モデルで使われる主要な計算プリミティブを1つのCUDAコードベースに統合したtensor coreカーネルライブラリ
    • GEMM: FP8、FP4、BF16
    • 通信を重ねたfused MoE: Mega MoE
    • lightning indexer向けMQA scoring
    • HyperConnection(HC)
  • すべてのカーネルは軽量な**Just-In-Time(JIT)**モジュールとして実行時にコンパイルされる
    • インストール時にCUDAコンパイルは不要
  • CUTLASSCuTeの一部概念を活用
    • ただし重いテンプレートや代数構造には大きく依存しない
    • 中核カーネル関数の数を限定し、コードベースをシンプルに保っている
  • 軽量設計でありながら、複数の行列shapeで専門家チューニング済みライブラリと同等以上の性能を出すとしている

主な更新

  • 2026年4月16日の更新では、Mega MoE、FP8xFP4 GEMM、FP4 Indexer、PDL、より高速なJITコンパイルなどを含む
    • 詳細は#304
    • Mega MoEベンチマークは#316
  • 2025年9月28日の更新では、DeepSeek v3.2 lightning indexer向けのweighted ReLU MQA logits scoringカーネルを追加
  • 2025年7月20日の更新では、SM90とSM100の両方をサポートし、低CPUオーバーヘッドのJIT CPPモジュールへ全面リファクタリングされた
    • NVRTCとコンパイル後のSASS最適化は無効化された
    • NVRTCは今後対応予定とされている
    • NVCC 12.9がFFMA interleavingを自動で行うため、コンパイル後最適化はもはやサポートされない
    • 詳細は#112
  • 2025年5月14日の更新では、denseおよびMoE backward向けのweight gradientカーネルを追加
  • 2025年5月7日の更新では、NVRTC対応により最大10倍のコンパイル高速化を提供
    • DG_JIT_USE_NVRTC=1で有効化可能
    • 一部のケースでは性能低下の可能性がある
    • 詳細は#94
  • 2025年4月18日の更新では、H800で最大1550 TFLOPSを達成

要件とインストール手順

  • 実行環境にはNVIDIA SM90またはSM100アーキテクチャGPUが必要
  • ソフトウェア要件は以下の通り
    • Python 3.8以上
    • C++20対応コンパイラ
    • CUDA Toolkit
      • SM90: CUDA 12.3以上
      • 最高性能のためにはCUDA 12.9以上を強く推奨
      • SM100: CUDA 12.9以上
    • PyTorch 2.1以上
    • CUTLASS 4.0以上
    • {fmt}ライブラリ
  • 開発環境では、submoduleを含めてリポジトリをcloneした後、develop.shで必要なincludeの接続とCPP JITモジュールのビルドを行う
  • インストールはinstall.shを実行し、その後Pythonプロジェクトでdeep_gemmをimportする方式

GEMMインターフェースとレイアウト制約

  • DeepGEMMのGEMMカーネル命名規則はD = C + A @ B
  • 入力shapeレイアウトはNTを基準とする
    • fp8_gemm_ntD = C + A @ B.Tを実行する
  • SM90実装はNTメモリレイアウトのみをサポート
    • row-major、col-majorの組み合わせに相当する
  • SM100実装はNT、TN、NN、TTすべてのメモリレイアウトをサポート
  • 両アーキテクチャとも、LHS scaling factorはTMA整列済みかつ転置されたレイアウトである必要がある
    • SM90はscaling factorをFP32形式で要求する
    • SM100はpacked UE8M0形式を要求し、4つのUE8M0を1つのtorch.intにパックする
  • 入力転置やFP8キャストのような処理はユーザーが別途行う必要がある
    • ライブラリは簡単なPyTorchユーティリティ関数を提供するが、性能は低い場合がある
    • 主な焦点はGEMMカーネル最適化にある

DenseとGrouped GEMM

  • 基本のnon-grouped FP8 GEMMはfp8_gemm_{nt, nn, tn, tt}関数を使う
  • contiguous layoutのgrouped GEMMは、CUTLASSの従来のgrouped GEMMと異なりM軸のみをグループ化する
    • NとKは固定である必要がある
    • MoEモデルでexpertsが同じshapeを共有する状況に合わせた設計
  • 学習のforward passや推論prefillingでは、expertごとに処理するtoken数が異なる場合がある
    • こうしたtokenを1つのtensorに連結した形をcontiguous layoutと呼ぶ
    • 各expert segmentはGEMMのMブロックサイズに整列している必要がある
    • 整列条件はget_mk_alignment_for_contiguous_layout()で確認できる
  • MoE weight backward向けにK軸grouped APIも提供される
    • MとNは固定である必要がある
    • 関連関数はk_grouped_fp8_gemm_tn_contiguous
  • 推論decoding段階でCUDA graphが有効で、CPUがexpertごとのtoken数を把握できない場合には、masked grouped GEMMをサポート
    • mask tensorを与えると、カーネルが有効領域のみを計算する
    • 関数はm_grouped_fp8_gemm_nt_masked
    • DeepEPのlow-latencyカーネル出力を入力に使う例がある

DeepSeek v3.2 Indexer向けMQAカーネル

  • V3.2 MQAカーネル群はnon-paged版とpaged版を提供
    • non-pagedはprefilling用
    • pagedはdecoding用
  • fp8_mqa_logitsは6つの入力を受け取る
    • q: E4M3 tensor、shape [seq_len, num_heads, head_dim]
    • kv: E4M3 tensorとfloat scaling factor
      • tensor shapeは[seq_len_kv, head_dim]
      • scaling factor shapeは[seq_len_kv]
    • weights: float tensor、shape [seq_len, num_heads]
    • cu_seq_len_k_startcu_seq_len_k_end: int tensor、shape [seq_len]
    • clean_logits: 埋まっていないlogitsを-infで整理するかどうか
  • 出力tensor shapeは[seq_len, seq_len_kv]で、token-to-token logitsを表す
  • q token icu_seq_len_k_start[i]からcu_seq_len_k_end[i]の手前までのkv token jを走査する
    • kv_jにscaling factorを掛ける
    • q[i, :, :] @ kv_jでheadごとの値を計算する
    • ReLU適用後にweights[i, :]を掛けて合計し、スカラーlogitを作る
  • paged版の関数はfp8_paged_mqa_logits

Mega MoE

  • Mega MoEは複数のMoEステップを1つのmega-kernelにfuseする
    • EP dispatch
    • linear 1, FP8xFP4
    • SwiGLU
    • linear 2, FP8xFP4
    • EP combine
  • Mega MoEはNVLink通信とtensor core計算をoverlapする
  • 実行にはsymmetric memoryを用いるmulti-process launchが必要
  • 使用手順は以下の通り
    • deep_gemm.get_symm_buffer_for_mega_moeでsymmetric memory bufferを確保する
      • PyTorch 2.9以上が必要
    • deep_gemm.transform_weights_for_mega_moeで、FP4とUE8M0 SFを含むweightを必要なレイアウトへ変換する
    • 呼び出し前に入力、scaling factor、top-k index、top-k weightをbufferへコピーする
    • deep_gemm.fp8_fp4_mega_moeでfused mega MoEカーネルを実行する
  • 完全なmulti-process設定とベンチマーク例はtests/test_mega_moe.pyにある

ユーティリティと環境変数

  • 主なユーティリティ関数は、実行リソース、整列、JITコンパイル、scaling factor変換を制御する
    • deep_gemm.set_num_sms / get_num_sms: 使用する最大SM数の設定と取得
    • deep_gemm.set_tc_util / get_tc_util: おおよそのtensor core utilization ratioの設定と取得
    • deep_gemm.set_pdl / get_pdl: Programmatic Dependent Launch(PDL)の有効化と無効化
    • deep_gemm.set_mk_alignment_for_contiguous_layout / get_mk_alignment_for_contiguous_layout: contiguous layoutにおけるgroup-level M/K alignmentの設定と取得
    • deep_gemm.transform_sf_into_required_layout: scaling factorを必要なレイアウトへ変換
    • deep_gemm.get_tma_aligned_size: 必要なTMA alignment sizeを取得
  • JIT関連の環境変数は、デバッグ出力、キャッシュ位置、コンパイラ選択、プロファイリングオプションを制御する
    • DG_JIT_DEBUG: JITデバッグ情報を出力
    • DG_PRINT_CONFIGS: shapeごとに選択されたconfigを出力
    • DG_JIT_CACHE_DIR: コンパイル済みカーネルのキャッシュディレクトリ。デフォルトは$HOME/.deep_gemm
    • DG_JIT_USE_NVRTC: NVCCの代わりにNVRTCを使用。高速コンパイルが可能だが、一部ケースでは性能が低下する可能性がある
    • DG_JIT_NVCC_COMPILER: NVCCコンパイラのパス
    • DG_JIT_CPP_STANDARD: C++標準バージョン。デフォルトは20
  • デバッグおよびプロファイリング用の環境変数も提供される
    • DG_JIT_DUMP_ASMDG_JIT_DUMP_PTXDG_JIT_DUMP_SASS: PTXとSASSの出力ダンプ
    • DG_JIT_WITH_LINEINFO: プロファイリングツール向けにsource line情報を含める
    • DG_COMM_KERNEL_DEBUG: Mega MoE呼び出し前にsymmetric bufferを0で初期化
    • DG_USE_NVIDIA_TOOLS: 外部NVIDIAツール実行時に内部profilingをスキップ
  • ビルドオプションは、インストール方法とカーネル読み込み方式を制御する
    • DG_SKIP_CUDA_BUILD: インストール時のCUDA extension buildをスキップ
    • DG_FORCE_BUILD: pre-built wheelのダウンロードの代わりにローカルビルドを強制
    • DG_JIT_USE_RUNTIME_API: カーネル読み込みにCUDA Runtime APIを使用。CUDA runtime 12.8以上が必要

ライセンスと引用

  • DeepGEMMリポジトリはMIT Licenseで公開されている
  • プロジェクトはCUTLASSから着想を得たとしている
  • 引用項目のタイトルはDeepGEMM: clean and efficient BLAS kernel library on GPU

1件のコメント

 
GN⁺ 2025-02-27
Hacker Newsのコメント
  • FFMA SASSのインターリーブは本当に驚くべきレベルに見える
    NVCC 12.2と12.3の間でCUTLASS FP8カーネルの性能が向上したのを見て、コンパイル済みSASSを比較したところ、複数のFADD命令で1ビットがインターリーブパターンで反転しており、オープンソースのCUDAアセンブラ実装を参考に、そのビットが現在のワープを譲って別のワープを実行させるyieldビットだと突き止めたようだ
    これを活用してコンパイル済みバイナリのFFMA命令を修正するスクリプトを作り、yieldビットだけでなく、ワープが譲られるとレジスタ再利用ができないためreuseビットもあわせて反転させ、細粒度スケーリングFP8 GEMMでMMA命令と昇格FFMA命令をよりうまく重ね、一部のケースで性能を10%以上引き上げたという点が印象的

    • 他で読んだところでは、性能が重要な行列演算の最適化では、このような手法は典型的に使われるものらしい
      ただ、この特定の問題については他のAI企業がまだ必要性を感じておらず、適用していなかったようで、結局みな似た地点に到達する可能性が高い
    • Scott Grayは2015年にMaxwellでまさにこれと、それ以上のことをすでに見つけており、その後も多くの人がかなり扱ってきた内容だ
  • こうした事例は、高水準コードだけでハードウェア性能を引き出すには、既存のコンパイラがどれほど遠い位置にあるかをよく示している
    伝統的なコンパイラ技術やAIベースの最適化エージェントがこうした結果を出すには、何が必要なのか気になる

    • 強化学習のフィードバックループの中で、膨大な試行錯誤が必要になりそうだ
  • 報告されている速度向上の数値は、独自のCUTLASSベースのベースラインとの比較だ
    cuBLASと直接性能比較をした人がいるのか気になる
    これまで見たCUTLASS GEMMの結果はcuBLAS比でおおむね10%以内だったので、論文で述べられている2倍〜2.5倍の向上が維持されるなら本当に印象的だと思う

    • 普段はFP8を避けてI8を好むが、この質問を見てcuBLASがどれくらい出るのか気になった
      まずcuBLASは、FP8のような混合精度処理を扱うにはcuBLASLt拡張APIが必要になる
      また、A x BでE5M2 x E5M2のような妥当に見える型の組み合わせはサポートしていない一方、E5M2 x E4M3はサポートしており、Ampere、Hopper、Blackwellでは行列Aが常に転置レイアウトでなければならないなど、制約が続く
      FP8 cuBLASLtベンチマークを自分の「Less Slow C++」リポジトリ<https://github.com/ashvardanian/less_slow.cpp>に統合し、既存のcuBLASおよび自作のCUDA/PTXベンチマーク一覧に追加した
      H100と同等の性能であるはずのH200 GPUで実行しており、正方入力ではスループットがおよそ1.35 Peta-opsでピークに達する
      256は2.68T/s、512は20.49T/s、1024は144.23T/s、2048は665.68T/s、4096は1.26P/s、8192は1.34P/s、16384は1.23P/s程度で、これは密GEMMについてNVIDIAが宣伝している数値<https://resources.nvidia.com/en-us-data-center-overview-mc/e...>の約67%だ
    • CUTLASSならcuBLASより良い性能を出せると聞いた
      ベースラインはcuBLASとCUTLASSのうち良い方を選んだものだと思っていた
  • こういうオープンソースは、業界の効率性達成という目標を本当によく示している
    ただし、このソフトウェアの恩恵は、学習や実験をしたり、消費者向けハードウェアでモデルをサーブしたりする一般のオープンソースコミュニティよりも、大規模にモデルをサーブする大手企業、つまりDeepSeekの潜在的な競合により大きく及びそうだ

    • 効率が良くなれば、最終的には誰にとっても、DeepSeek自身にとってもより安価なハードウェアにつながり得る
  • ますます低い精度へ最適化していく方向が長期的に良いのかは、よく分からない
    これはモデルが実際にはかなりであることを意味するが、今はそうであり得るとしても、本質的にそれほど疎でなければならないからというより、学習方法に悪いアイデアが混ざっているためである可能性が高いと思う

    • タダで得られる疎性が通用している間は楽しめばいい
      より高い精度でしか非常に良いモデルを学習できないようにするのは研究課題で、低精度の学習と推論は工学の問題だ
      CNNの時代から、少なくとも9年前からこういうことをやってきたし、今後もまだ数年は続くと思う
    • 活性化関数は浮動小数点数の動的範囲をかなり捨てるため、すでに飽和している活性化区間に広い範囲を割くのは、有用でない可能性がかなり明らかだ
  • これはBlackwellのネイティブなマイクロスケーリング対応であるMXFPのせいで無意味になるかもしれない
    Hopperではそれをより粗い単位で、代わりにFP32のスケーリング係数を使って手動実装しているようなものだ

    • その通り
      こういう高品質な公開デモは、$NVDAの堀がどこにあるのかをよく示している
      汎用GPUは非常に柔軟なので、ハードウェアベンダーが最初から思いつかなかったが十分に筋の通ったさまざまな処理を、プログラミングで実行できる
      ただし、未来がますます専用ハードウェア対応へ収束し、このようなソフトウェア最適化の余地が消えていくと予測するなら、いわゆるCUDAの堀は崩れる
      このゲームに残るために、NVIDIAは自らの堀を自分で壊しているようなものだ :p
  • わあ、MITライセンス
    大手企業がこういうオープンソース協業のやり方を受け入れてくれるといい

  • なぜ文書化されていない命令が存在するのか、ずっと気になっている
    完全に安定していなくても、ユーザーに提供するほうが正しいのではないかと思う
    こうした内容は社内では文書化されているはずなのに、なぜ公開しないのか分からない
    曖昧さに依存したセキュリティは通用しないし、競合他社はいずれにせよ全部リバースエンジニアリングする

    • おそらく、私たちが作るものにも文書化されていない部分が生まれる理由と似ているのだろう
      時間不足だったり、不安定または実験的な機能についてサポートを示唆したくなかったりするからかもしれない
      影響が隣のチーム程度にとどまるなら、変更するのもはるかに簡単になる
    • 「こうした内容は社内で文書化されているはず」という前提からして違うのかもしれない
      おそらくアーキテクチャ設計文書や仕様のような場所にだけ入っている可能性が高く、そういう文書は当然共有したくないはずだ
  • 正直、自分の利用範囲と理解を超える内容だ
    それでも、こうした発見と改善を共有し、誰もが恩恵を受けられるようにしてくれる点は本当にありがたく、新鮮に感じる

    • FFMAは Fused Floating-point Multiply-Add の略で、D = A*B + C を一度に実行する基本的なGPU命令だ
      行列乗算やディープラーニングのワークロードで非常に重要である
      NVIDIA の SASS では、FFMA 命令は64ビットまたは128ビット命令としてエンコードされ、正確な動作を決める複数の制御ビットを持つ
      yield ビットが設定されると、ワープスケジューラに対して、この命令の後に現在のワープが実行を譲れることを知らせ、ハードウェアは別のワープを実行してレイテンシを隠蔽できる
      GPU は大規模な並列性によって高いスループットを得ており、あるワープがメモリ待ちなどで停止すると、別のワープが進行できる
      reuse ビットはソースレジスタを直後の演算で再利用できるかを示し、yield ビットが設定されている場合は必ずオフにしなければならない
      ワープが譲ると、次に実行されるのがそのワープではない可能性があり、別のワープがレジスタファイルの状態を変える可能性もあるため、ハードウェアは yield をまたいでレジスタ値が維持されることを保証できないからだ
      FFMA 命令群に yield ビットを交差パターンで設定すると、コンパイラは別のワープが進行できる明示的なスケジューリングポイントを作り、正確性を保つために該当命令の reuse ビットも同時にクリアする必要がある
      この変更は、行列乗算の中核であるMMA命令と、FP8 をより高い精度で累算するために変換する昇格 FFMA 命令を重ね合わせるうえで特に役立つ
      FP8 GEMM は通常、累算のためにより高い精度へ変換し、再び戻す過程が必要になるため追加の FFMA が発生し、メモリ帯域幅の要求は減らす一方で、昇格/降格演算が混在する複雑な計算パターンを生む
      「細かなスケーリング」は、計算の複数の時点で精度を慎重に管理する作業を指しているようだ
      yield ビットの操作は、計算演算と形式変換がよりうまくインターリーブされるようにし、GPU の実行ユニットをより効率的に使えるようにする。この最適化がないと、ワープスケジューラが自然な切り替え機会を見つけられず、計算リソースの活用度が下がる可能性がある