Anthropic、NVL72 向け MoE メガカーネル「Mixture-of-Kittens」公開
本文の状態
日本語全文を表示中
詳細モードで約38分の本文を読めます。
Cursor は NVL72 環境向けの MoE トレーニング用メガカーネル「Mixture-of-Kittens」をオープンソース化し、通信と計算を融合させることでトレーニング効率を大幅に向上させた。
AI深層分析を開く2026年8月5日 23:52
AI深層分析
キーポイント
MoE ボトルネックの解消
Cursor は Composer モデルの学習において MoE レイヤーがボトルネックとなっている問題を解決するため、通信と計算を単一のカーネルに融合させる Mixture-of-Kittens (MoK) を開発した。
NVL72 環境への最適化
GB300 NVL72 の特性である広範な NVLink ドメインと、GPU に比べて相対的に遅い Grace CPU の課題に対応し、CPU-GPU 同期を最小化する設計となっている。
実証された性能向上
実際の生産環境でのトレーニングスタックにおいて、MoK を導入することでエンドツーエンドのトークン処理速度が 1.41 倍に向上したと発表している。
MoE層の構成と計算フロー
DeepSeek-V3スタイルのMoE層は共有エクスパート多数とルーティングされた数百のエクスパートを組み合わせており、各トークンはルーターでトップkのエクスパートを選択してFFN計算を実行する。
エキスパート並列化における通信ボトルネック
EP度に応じてエクスパート重みをGPU間で分割すると、トークン転送が必須となるため、従来の逐次実行では通信時間が計算時間と同程度になり非効率になる。
重要な引用
Mixture-of-Kittens (MoK), our production MoE training megakernel for NVL72s.
MoK fuses all MoE communication and computation into a single, fully deterministic kernel.
In our production training stack across several NVL72 racks, MoK increased end-to-end tokens per second by 1.41x.
But because the communication can take as long as the computation itself, running the two sequentially is inefficient.
編集コメントを表示
編集コメント
NVL72 という最新ハードウェアの特性を深く理解し、CPU のボトルネックまで考慮したカーネル設計は、大規模モデル学習の実務において極めて参考になる。通信と計算の融合というアプローチは、将来的な大規模 MoE トレーニングの標準的な最適化手法の一つとして注目されるだろう。
Source Article
元記事を日本語で読む
本文に関係しない購読案内、埋め込み通知、サイト内プロモーションは除いています。
本日、私たちは NVL72 向けの生産環境対応 MoE 訓練用メガカーネル「Mixture-of-Kittens (MoK)」をオープンソース化しました。
エージェント型コーディングモデルである Composer の訓練と推論の規模拡大に伴い、エキスパート混合(mixture-of-experts)層が常に最大のボトルネックとなってきました。ワークロードや訓練設定によっては、エンドツーエンドの訓練時間の半分超をこの層が消費することもあります。
MoK は、MoE における通信と計算をすべて単一の完全な決定性カーネルに統合することで、このボトルネックを解消します。現在、MoK は数万基の GPU を用いた Composer の訓練を支えています。
GitHub で MoK を試し、コードを検索できます。皆様からのフィードバックや貢献をお待ちしています。
MoK は、MoE 層を高速化するためのいくつかの先行試行から発展しました。過去一年間で、私たちは独自に MXFP8 や NVFP4 の訓練カーネルを実装し、MoE 推論のための「warp decode」アプローチを開発しました。
しかし、それらの手法は層の計算部分のみを最適化するものであり、GPU 間の通信は別途処理されると想定していました。実際の生産環境では、通信がボトルネックとなっていました。そこで私たちは、通信機能をカーネルに直接組み込む形で、MoE 層を第一原理から再設計しました。
さらに、GB300 NVL72 への移行により、解決すべき課題は重要な点で二つ変化しました。第一に、NVL72 は単一の NVLink ドメイン内に収まるマルチノードラックであり、72 枚の GPU にわたって計算と通信を高速かつ微細な粒度で重畳実行できます。
第二に、統合された Grace CPU(GB300 の「G」)は GPU に比べて相対的に低速です。GPU ストリームが CPU 側の処理をすぐに追いついてしまい、その間 GPU が完全にアイドル状態になることが判明しました。そのため、CPU での作業量と CPU-GPU 間の同期を積極的に最小化する必要があります。
これらの課題に対する私たちの解決策が Mixture-of-Kittens(MoK)です。これは NVL72 向けに第一原理から構築された、極めて最適化された MoE 学習用メガカーネルです。MoK はすべての MoE 通信と計算を単一のカーネルに融合し、完全に決定論的でありながら、公開されている実装と比較して最良の性能を発揮します。
複数の NVL72 ラックにわたる本番環境でのトレーニングスタックにおいて、MoK の導入によりエンドツーエンドのトークン処理速度は 1.41 倍向上しました。
この投稿の後半では、MoK の核心となるアイデアを解説します。通信方向の選定方法や、計算と通信の重畳構造、リングトークンバッファを用いた CPU-GPU 同期の排除などです。さらに、メガカーネルの設計思想、決定論性の確保、MXFP8 サポート、その他の実装詳細についても取り上げます。
MoE における計算と通信の重畳
MoK は、GLM や Qwen、Kimi(K2.7 まで)、そして DeepSeek-V3 (DSV) そのものなど、オープンウェイトモデル全体で広く採用されている DeepSeek-V3 (DSV3) スタイルの MoE レイヤーを対象としています。これらのレイヤーは、1 つの共有エキスパートと数百に及ぶルーティングされた多数のエキスパートを組み合わせる構造です。
MoE レイヤーに入力される各トークンに対して、ルーター投影が上位 k 個のルーティングされたエキスパートを選択し、それぞれにルーター重みを割り当てます。選択された各エキスパートは、アップとゲートの投影、SwiGLU 活性化関数、そしてダウン投影からなる標準的なフィードフォワードネットワーク計算を実行します。その後、レイヤーはルーター重みを用いて、共有エキスパートとルーティングされたエキスパートの出力を結合します。
以下に記す表記を使用します:
- d: モデル次元
- f: エキスパート中間次元
- E_k: 上位 k 個のルーティングされたエキスパートの集合
- x_i: 入力トークン
- w_r: ルーター重み
- W_e, b_e: エキスパートの重みとバイアス
MoE レイヤーは以下のように計算されます:
y = Σ_{j∈E_k} (w_j * Expert_j(x_i)) + SharedExpert(x_i)
ここで、
E_k は上位 k 個のエキスパートの集合です。
エキスパート並列化(EP)では、ルーティングされたエキスパートを分割し、その重みを多数の GPU またはランクに分散します。すべてのエキスパート重みを集団的に保持するランク数を「EP degree」と呼びます。例えば、256 個のルーティングされたエキスパートと EP degree が 64 の場合、各ランクは 4 つのルーティングされたエキスパートと 1 つの共有エキスパートを保持することになります。その結果、ルーター投影に応じて、トークンは MoE レイヤーの前と後に GPU を跨いで転送される必要があります。
分散型 MoE の最も単純な実装では、各トークンをその割り当てられたエキスパートを持つランクへ送信(dispatch all-to-all)し、FFN を実行した後に結果を元のランクへ返還(combine all-to-all)、最後にエキスパート出力の加重和を取ります。しかし、通信に計算と同じ時間がかかる場合、これらを順次実行するのは非効率的です。
標準的な対策は、パイプライン処理によって dispatch/combine1 の通信と各エキスパートごとの FFN 演算を重畳させることです。具体的には、トークンの断片を転送しながら、その断片に対して FFN を計算し、同時に次の断片の転送も重ねて行うという手法です。MoK はこのスキームの一種であり、既存のベースラインよりも高速化を実現する、ターゲットに特化した一連の革新的な技術を採用しています。

適切な通信方向の選択
GPU 間でトークンを転送する際、トークン所有元の GPU が遠隔先の GPU に能動的に書き込む「プッシュ型」のメカニズムか、必要なトークンを遠隔元の GPU から読み取る「プル型」のメカニズムかを自由に選べます。既存のアプローチでは、GPU 間でのトークンの散乱と集約にプッシュ型の通信を多用する傾向があります(例:DeepEP)。
一般的には、プッシュ方式がプロトコル通信を減らすため GPU 間のリンク飽和に優れ、デフォルトの選択肢だと考えられています。しかし私たちの観察では、各メカニズムにはそれぞれトレードオフがあり、パフォーマンス最大化のためには通信演算子ごとに適切な方を選ぶことが重要です。その理由は以下の 3 点です。
スケジューリング
トークンをできるだけ速く分散・結合するためには、以下の条件が満たされる必要があります。
- ラック内の 72 台の GPU を相互接続するすべての NVLink レーンを常に飽和状態に保つ必要があります。一部の (送信元→宛先) レーンのみでトークンが移動しているような時間的空白は許されません。そのため、どの瞬間においても各送信元ランクからの送信が、すべての宛先ランクに均等に分散されるようにトークンを選択する必要があります。
- 宛先ランクへ送られるトークンは、そのランクのローカルエキスパート順に到着するべきです。到着順序がバラバラだと、テンソルコアによる行列乗算を開始するために、グループ化された GEMM が完全なタイル分のトークンを待つ時間が長くなってしまいます。
- ローカルでのコピーはゼロであるべきです。エキスパート用のトークンは連続したメモリ領域に直接配置され、グループ化された GEMM がローカルで並べ替えを行うことなく開始できるようにする必要があります。
上記 3 つの条件を満たすためのオーバーヘッドを最小限に抑える必要があります。時間を多く割くのは実際のトークン転送であり、スケジューリングや送信対象トークンの探索に費やす時間は極力少なくすべきです。
プッシュベースのディスパッチでは、{src_index, dst_rank, dst_index} の列を持つスケジュールテーブルを作成する必要があります。ここで src_index はローカルの着信アクティベーションバッファ内のインデックスを指し、dst_index はトークンが到着する先ランのメモリ上の場所を示します。このテーブルの行インデックスは、NVLINK を介してトークンを送信する順序を決定します。
このテーブルの行が dst_rank をラウンドロビン方式で巡回し、接続されたすべてのレーンが常に稼働するようにします。
特定の dst_rank を対象とする行群において、dst_index の値はすべてのソース GPU にわたって均等にインターリーブされなければなりません。また、その宛先ランク上ではローカルエキスパートの順序に従って増加している必要があります。このテーブルを構築するには複数のソート処理が必要となり、各ランクのスケジューリングは他のすべてのランクの状況を考慮する必要があります。なぜなら、2 つ以上のソースランクが同じ dst_index に書き込むことはできないからです。
| スケジュール ID | ソース ID | 宛先ランク | 宛先 ID |
|---|---|---|---|
| 0 | 17 | 0 | 0 |
| 1 | 0 | 1 | 0 |
| 2 | 29 | 2 | 0 |
| 3 | 3 | 3 | 0 |
| … | … | … | … |
プッシュベースのスケジューリングでは、ネットワーク全体を最大限に活用するためには、連続するエントリ間で宛先ランクを交互に配置する必要があります。
| スケジュール ID | 送信元ランク | 送信元 ID |
|---|---|---|
| 0 | 0 | 0 |
| 1 | 1 | 5 |
| 2 | 2 | 13 |
| 3 | 3 | 7 |
| … | … | … |
プルベースのスケジューリングでは、ネットワーク全体を飽和させるために連続するエントリはソースランクを交互に配置する必要があります。
プルベースのディスパッチを採用すると、スケジュールテーブルは {src_rank, src_index} の 2 カラムに簡素化され、テーブルの行インデックスがローカル宛先バッファ内のトークンインデックスと直接対応します。
理論上、テーブルの行数と宛先バッファの行数は一致するはずです(これには大量のメモリ割り当てが必要になります。詳細は後述します)。ここではソート処理は不要です。ルーターの投影結果を走査し、現在のランクに配置すべきトークンが見つかったら、そのソースランクとインデックスをスケジュールに書き込みます。アルゴリズムは以下の通りです。
入力: ルーティングテンソル R(R[r, t] は、ソースランク r のトークン t のルート i に割り当てられた専門家のグローバルインデックス)、ローカルランク rank、ランクあたりの専門家数 E
出力: ローカル専門家ごとのトークン数 count_local、ローカル専門家およびソースランクごとのトークン数 count_src、領域オフセット offsets、スケジュールテーブル schedule
count_local = zeros(num_experts)
count_src = zeros(num_experts, num_ranks)
for r in range(num_ranks):
for t in range(batch_size):
expert_id = R[r, t]
count_local[expert_id] += 1
count_src[expert_id][r] += 1offsets = cumsum(count_local)
schedule = []
for r in range(num_ranks):
for t in range(batch_size):
expert_id = R[r, t]
if rank == get_rank_for_expert(expert_id):
schedule.append((r, t))実用上、このアルゴリズムを実装したスケジューリングカーネルは MoE の実行時間の 3% 未満しか占めず、CPU と GPU の通信を一切行わずにデバイス側で完全に動作します。
プッシュベースの結合(combine)を選択し、{src_rank, src_index} を {dst_rank, dst_index} として読み取ることで、このスケジュールをそのまま再利用することも可能です。実際には、フォワードとバックワードの全 4 つの通信操作に対して一度スケジュールを作成すれば、それを流用できます。具体的には以下の組み合わせです。
- プルベースのフォワードディスパッチ
- プッシュベースのフォワード結合
- プルベースのバックワード逆結合
- プッシュベースのバックワード逆ディスパッチ
最悪の場合でもスケジュールは数メガバイト程度なので、メモリ圧力をかけることなく保持して再利用できます。さらに大きな利点として、後述する通り、この手法によって GPU 間やマルチレーン間の信号伝送が完全に不要になります。
NVLink の帯域幅利用率
あらゆるネットワークシステムと同様、NVLink を介して転送されるすべてのユーザデータ(ペイロード)には、ソースと宛先、確認応答など、使用するネットワークプロトコルに依存する追加のプロトコルメタデータが付加されて送信されます。NVLink 通信プロトコルの詳細は非公開ですが、リンク上で送られるデータを観測し、慎重に推論することは可能です。
その結果、プッシュベースの NVLink 通信では転送される総バイト数(プロトコルメタデータとペイロードの合計)が少なくなり、ほぼすべてのデータが一方向に送信されるため、すべてのレーンがフル稼働している状態において高い帯域幅利用率を実現できることがわかりました。
一方、プルベースの転送では総データ量は多くなりますが、プロトコルのメタデータはリンクの双方向に分散されます。プラー側がまず片方向へメタデータを送信し、その後、もう片方向から追加のメタデータとペイロードを受信します。
これを検証するには、シンプルなクロス GPU 転送カーネルを作成し、NCU でプロファイルすればよいでしょう。例えば、NVLINK を介して 1 つの 256x256 BF16 タイル(131,072 バイト)を送信するマイクロベンチマークでは、以下のような結果が観測されます:
| 合計 | 合計 RX | 合計 TX | プロトコル RX | プロトコル TX | ペイロード RX | ペイロード TX | |
|---|---|---|---|---|---|---|---|
| プッシュ | 159.6 KB | 2.9 KB (1.84%) | 155.6 KB (99.16%) | 2.7 KB | 24.6 KB | 0 KB | 131.1 KB |
| プル | 172.0 KB | 147.5 KB (85.71%) | 24.6 KB (14.29%) | 16.4 KB | 24.6 KB | 131.1 KB | 0 KB |
上記の表において、RX は受信方向を、TX は送信方向を表します。プッシュ方式では、トータルで約 12.4 KB バイト少ないことがわかりますが、これは NVLink の飽和率向上に理想的です。ただし、NVLink は各方向ごとに独立したレーンを持っています。GB300 における第 5 世代 NVLink の仕様書に記載されている帯域幅(1.8 TB/s)を達成するには、双方向への通信が完全に成立している必要があります。MoE のディスパッチやコンバインドのようなワークロードでは、各転送は数 KB に過ぎず、かつ非対称になりがちです。その結果、リンク上で実行の待ち時間(バブル)が生じてしまいます。
経験則として、エキスパートの不均衡がある状態でトークンをディスパッチする際、プルベースの通信方式はプッシュベースに比べて NVLink の帯域利用率を最大 29% 向上させます。このため、プル方式がより優れた選択肢となります。
シグナリング
シグナリングが必要です。なぜなら、GPU はディスパッチやコンバインドの通信が完了したことを知る必要があるからです。受信したトークンに対して GPU が処理を開始できるのは、完了信号を受信した後だけです。このシグナリングオーバーヘッドを最小限に抑えることが重要であり、適切な通信方向を選択することが大きな影響を与えます。
プッシュベースのディスパッチまたはプルベースのコンバインドの場合、完了信号は GPU 間をやり取りする必要があります。エキスパートグループ化された GEMM を開始する前に、あるランクは最大 71 のピアからの信号を待機し、メモリフェンスを発行してラック全体にわたってメモリをフラッシュしなければなりません。さらに、シグナリングが転送されている間にデータはすでに宛先に存在しています。
プルベースのディスパッチとプッシュベースのコンバインを採用することで、GPU 間のシグナリングは不要になります。各ランクがデータロードを要求し、到着を待ってから即座に使用を開始できるため、マルチノード同期も必要ありません。また、シグナリングを行うのは単一のエンティティのみであるため、エクスパート並列度の増加に伴ってオーバーヘッドが増大することもありません。
私たちはこのシグナリングのオーバーヘッドが非常に大きいことを発見しました。マルチノードのマイクロベンチマークでは、プッシュベースのディスパッチによるシグナリングはプルベースのものよりも遅延が約 5.8 倍高く、それぞれ 103 µs と 18 µs という結果でした。このコストはメガカーネル内ですぐに蓄積していきます。
これらの結果に基づき、MoK ではプルベースのフォワードディスパッチ、プッシュベースのフォワードコンバイン、プルベースのバックワードリバースコンバイン、そしてプッシュベースのバックワードリバースディスパッチを採用しています。

計算と通信のオーバーラップを適切に構造化する
計算・通信の粒度
計算と通信の粒度とは、計算タスクと通信タスクが完了を互いにシグナリングする際の粒度のことです。ここには興味深いトレードオフが存在します。
通信の粒度は極端な二つの方向に振れる可能性があります。一方では、非常に微細な粒度で通信を行うアプローチです。例えば Blackwell アーキテクチャでは、テンソルコアが行列乗算累積(MMA)命令を起動するために必要な最小限のトークン数である 256 を送信し、転送と行列演算を常時重畳させて実行します。この手法の好例として Comet が挙げられます。
他方では、粗い粒度で通信を行うアプローチです。ここでは数千から数万のトークンを一度に送信し、すべてのバッチが揃うのを待ってからテンソルコアでの演算を開始します。この手法の代表例として DeepEP が挙げられます。
私たちが観察したところ、どちらかの極端なアプローチが最適解ではありません。最適な点は両者の中間に位置し、ワークロードの内容によって変化します。
通信が細かすぎると、テンソルコアは十分に飽和しません。Nvidia のテンソルコアは高度にパイプライン化され、帯域幅を最適化したアクセラレーターです。数回の MMA 命令ごとにバリア待ちが発生するのではなく、絶えずデータを送り込んで稼働させ続けることが重要です。
逆に通信が粗すぎると、最初のトークン群の到着や最終的な出力の結合まで、テンソルコアが長時間待たされることになります。

各ラウンドで送信するトークンの集合を、私たちはミニバッチと呼びます。MoK ではこのミニバッチサイズを調整可能なパラメータとして扱い、実際のワークロードを実行する前にさまざまな構成を簡単に試せるようにしています。
最適なミニバッチサイズを選ぶための大まかなヒューリスティックは、各オペレーター(例えば、エキスパートグループ化された GEMM など)に必要となる「ウェーブ」の数を考慮することです。
ここでいう「ウェーブ」とは、GPU 上のすべての SM(ストリーミングマルチプロセッサ)で並列実行が行われる一ラウンドのことです。もし処理対象が小さすぎると(例えば、エキスパートグループ化された GEMM に割り当てられるトークン数が少なすぎる場合)、一部の SM がアイドル状態になる「部分的なウェーブ」が発生します。あるいは逆に、「テール効果」と呼ばれる現象が見られることもあります。これは最終的なウェーブで利用可能な SM の一部しか使われないケースです。
私たちは、FFN(フィードフォワードネットワーク)内の各エキスパートグループ化 GEMM オペレーターに対して、少なくとも 2 つの完全なウェーブが得られるようなミニバッチサイズを選ぶのが有効だと判断しています。このアプローチには 2 つの理由があります。(1) 1 つの完全なウェーブで十分 tensor cores を最大限に活用できること。(2) 2 つのウェーブを設けることで、2 つ目のウェーブにおける行列乗算(MMA)が、1 つ目のウェーブのエピローグや従属オペレーター(SwiGLU アクティベーション関数や次のグループ化 GEMM など)と重なり合い、テール効果によるオーバーヘッドを分散できることです。
具体的には Blackwell GPU において、GEMM で tensor cores を完全に活用する場合、削減次元を とすると、1-SM または 2-SM の MMA(行列乗算アレイ)のいずれの場合でも、各 SM は 出力タイルを担当することになります。
したがって、以下の定義に基づきます:
- :転送あたりのトークン数(つまりミニバッチサイズ)
- :隠れ次元
- :エキスパート中間次元
- :SM の数
各 GEMM が少なくとも 2 つのウェーブを埋める十分なタスク数を生成し、かつ通信粒度は最大限のオーバーラップを実現するために可能な限り小さくなるようにする必要があります。up プロジェクションと gate プロジェクションは互いに依存しないため同時に実行可能であるため、この要件は以下のようになります。
down プロジェクションは他の演算器と並列実行できないため、以下の条件となります。
これらを組み合わせると、求める要件は以下の式で表されます。
例えば、Composer 2.5 のベースモデルである Kimi 2.5 を考えてみましょう。ここで、K=1024、H=8192 です。Blackwell GPU では T=32 なので、計算すると以下のようになります。
Kimi 2.5 の形状で MoK を前方伝播させるマイクロベンチマークでは、以下のような結果が得られました。これは概ね上記のヒューリスティックと一致しています。
| ms | |
|---|---|
| 512 | 5.981 |
| 1024 | 4.669 |
| 1536 | 3.981 |
| 2048 | 3.666 |
| 2560 | 3.425 |
| 3072 | 3.447 |
| 3584 | 3.524 |
| 4096 | 3.473 |
オーバーラップのスケジューリング
通信粒度を決定した後、次の課題は、GPU 上でディスパッチ(分配)、コンバイン(集約)、FFN(フィードフォワードネットワーク)といった異なるタスクをどのようにスケジューリングして効率的にオーバーラップさせるかです。これを実現するために採用しているのが「SM 間オーバーラップ」手法です。これは、一部の SM を専門家の FFN 処理用(計算用 SM)に割り当て、残りをディスパッチやコンバインの通信処理用(通信用 SM)に割り当て、両グループがローカルカウンタを介して互いにシグナルを送り合う仕組みです。
この手法が可能なのは、Tensor Memory Accelerator (TMA) を用いたロード・ストア操作により、NVL72 の NVLink バンド幅を完全に飽和させるのに、必要な SM の 3 分の 1 も満たないリソースで済むからです。
私たちがベース戦略として採用しているのはこの SM 間オーバーラップです。MoE の順伝播フェーズでは、通信用 SM がプルベースのディスパッチ(all-to-all)を開始し、トークンのミニバッチがすべて到着して計算準備が整うたびに、計算用 SM にシグナルを送ります。計算用 SM はそのトークンに対して MLP-SwiGLU を原子演算として実行します。そして、下層への投影処理がすべて完了すると、計算用 SM が通信用 SM に対し、「トークンをコンバインの準備ができている」というシグナルを送ります。
通信用 SM は、すべてのディスパッチタスクを完了するまで、いかなるコンバインタスク(all-to-all)も実行しません。そのため、通信用 SM がディスパッチ処理を終える頃には、計算用 SM からの完了シグナルが多数届いています。その後、通信用 SM はプッシュベースのコンバイン処理に移行し、その間、計算用 SM は最後に到着したトークンに対する FFN 処理を完了させます。さらに、計算用 SM が最初のディスパッチ完了を待っている間に、共有専門家の FFN 処理も組み込むことが可能です。

逆伝播も同様ですが、フォワードリプレイの影響でやや複雑になります(詳細は次節で解説)。通信用 SM は「逆結合」を実行し、計算用 SM に対して FFN の逆伝播を開始させます。ただし重みの勾配(wgrads)は除外されます。具体的には、3 つの専門家グループ化された GEMM が 3 つの dgrad と SwiGLU 活性化関数の逆伝播に対応します。FFN の計算が完了し、トークンが「逆分散」の準備ができ次第、計算用 SM は通信用 SM に通知を送ります。
すべてのミニバッチにおける dgrads の処理が完了した後、残りの 3 つの wgrads を実行します。wgrads を遅らせる理由は二つあります。(1) 逆分散はこれらに依存しないため、最後の逆分散と並列で wgrads を実行できること。(2) トークン軸が wgrads の縮小次元であるため、トークン軸全体を一度に集約することで数値的不安定性を最小限に抑えられるからです。フォワードパスと同様に、共有専門家の全逆伝播を最初の「逆結合」ラウンドと重畳させて実行します。

リングトークンバッファによるCPUとGPUの同期を完全排除
最後に残る課題は、MoE(Mixture of Experts)の動的な挙動への対応です。どのラベルにどれだけのトークンが到着するかを事前に知ることはできません。
これに対処する既存の方法には 2 つあります。1 つ目はトークンドロップで、固定サイズのバッファを超えたトークンは単純に無視され、専門家の FFN(Feed-Forward Network)に入ることはありません。2 つ目は CPU と GPU の同期です。ルーターの結果から計算された各ランクごとのトークン数を CPU に送り、CPU がそれを使って正確なサイズのバッファを割り当てます。
両者には明らかな欠点があります。トレーニングの品質のためにトークンを捨てるわけにはいきませんし、CPU-GPU 間の同期は GPU ストリームが CPU スレッドより先に進めなくなる原因となります。
特に GB300 NVL72 では、統合された Grace CPU の性能が非常に低いため、この同期のコストは甚大です。実際のトレースでは、Intel CPU を搭載した DGX マシンでは問題にならなかった同じトレーニングスタックでも、GPU カーネルがログ出力やリモートサーバーへのトレーニングメトリクス送信といった CPU 側の処理によってボトルネックに陥っている様子が頻繁に確認されています。そこで私たちは、CPU と GPU の同期を可能な限り最小限に抑えることを目指しました。
MoK ではこれを「マクロバッチ」と呼ぶリングトークンバッファで解決します。通信用 SM(Streaming Multiprocessor)がトークンを転送先へ格納するために、大きくてほとんど使われないバッファを割り当てるのではなく、数百メガバイト程度の固定サイズのリングバッファを作成し、ミニバッチ単位でこれを循環使用します。
マクロバッチ処理を効率的に実装するには、計算と通信の適切な重なり合わせが不可欠です。バッファの空きスロット待ちやデータ到着待ちによってどちらかが停止してはなりません。理想的な状態では、リングバッファの境界を超えて重なりを持たせ、バッファの後端部分で計算を行う一方で、その先頭部分への転送(ディスパッチ)を並行して実行します。
肝心な考え方は、パフォーマンスが許す限り各スロットを早期に解放し、できるだけ早く再利用できるようにすることです。順伝播(フォワードパス)では、ディスパッチでスロットを埋め込み、コンバインでそれらを空にして、次のディスパッチのために解放します。
しかし、すべてのディスパッチを実行した後にすべてのコンバインを実行するスケジュール(前節の手法)を採用すると、次のマクロバッチは最後のコンバインが完了するまで開始できず、その最初のディスパッチが終了するまで計算と通信の重なり合わせは再び始まりません。
これを回避するため、順伝播ではディスパッチとコンバインを交互に実行します。前のマクロバッチのミニバッチに対するコンバイン処理を、同じリングバッファ領域を使用する次のマクロバッチの対応するミニバッチに対するディスパッチ処理と交互に行います。これにより、バッファは空になった瞬間にすぐに再充填されます。なお、最初のマクロバッチのディスパッチと最後のマクロバッチのコンバインにはそれぞれ対応する相手がないため、これらは交互実行されません。

バックワード時のフォワード再計算を最小化する逆方向リング
バックワードパスは、フォワード活性化の再計算(リプレイ)に対応する必要があるため、やや複雑になります。マクロバッチバッファはリング構造を採用しており、複数のマクロバッチが存在する場合、フォワード実行中に一部の保存された活性化が上書きされるため、バックワード時に再計算が必要となります。
ただし、再計算が必要な範囲は SwiGLU 活性化までで十分です。バックワードでは、下層投影(down projection)の入力活性化と出力勾配のみが必要であり、そのフォワード出力自体は不要だからです。
ここで重要な最適化が「逆方向リング」の採用です。マクロバッチを昇順に処理すると、最後のマクロバッチでリングバッファが部分的にしか埋まらず、バックワード時に再計算すべき活性化が増加してしまいます。MoK ではフォワードパス中にマクロバッチを降順に処理することで、保存されるリングバッファが常に完全に満たされているか、あるいはすべてのトークンを含む状態を保ちます。これにより、バックワード時のフォワード再計算量を最小化しています。
その他の機能と実装の詳細
Megakernel
MoK は、megakernel 技術を用いて構築されました。この手法では、カーネル起動の境界で分断されるのではなく、SM(ストリーミングマルチプロセッサ)タスクレベルで命令が重なり合います。
通常、megakernel は前方計算または後方計算のステップ全体を融合させるのに適していますが、MoK においても以下の 2 つの理由から有用であることがわかりました。1 つ目は、megakernel の融合なしにミニバッチ処理やマクロバッチ処理を実装すると、複数のカーネル起動が必要となり、起動境界によるオーバーヘッドが増大することです。2 つ目は、SM 間の重なり合わせにおいて、緑色のコンテキストを持つ複数のストリームでは意図した通りに SM を正確に分割できないことが判明し、ソフトウェアによるパーティショニングであれば正確な割り当て保証が得られるからです。
Determinism(決定性)
内部でのアブレーション実験やオンポリシー RL のポストトレーニングにおいて、MoK は完全に決定性を持つように設計されました。浮動小数点演算の順序は固定されているため、ハードウェアのスケジューリングや命令発行順に関わらず、同じ入力に対してビット単位で同一の結果が出力されます。
Cluster Launch Control (CLC) for RDMA overlap
MoK メガカーネルは、RDMA のオーバーラップを可能にするため、CLC を通じてタスクスケジューリングを行います。CLC は Blackwell で導入されたハードウェアネイティブのワーク・スティーリング機能であり、永続的なグリッドカーネルを実装する新たな方式です。
CLC ベースのカーネルは、現在のカーネルが完全に完了するのを待たずに、より優先度の高いストリームのカーネルに対して自然に譲渡できます。これは重要です。なぜなら、トレーニング中、私たちはしばしばラック内の通信と計算を、独自の SM(Streaming Multiprocessor)を必要とする FSDP all-gather などのラック間の InfiniBand/RoCE 通信とオーバーラップさせる必要があるからです。
CLC がなければ、ラック間通信はメガカーネルの後ろに直列化されてしまい、著しいパフォーマンス低下を招きます。
MXFP8 サポート
MoK は BF16 と MXFP8 の両方の精度モードをサポートしています。数値的な問題を引き起こさないため、より高速な MXFP8 モードでトレーニングを行っています。MXFP8 モードでは、トレーニングの安定性に影響を与えることが判明したため、共有エクスパートは BF16 で維持されます。
MXFP8 の追加コストとして、テンソルをテンサーコアに供給する前に量子化する必要があります。このオーバーヘッドを最小化するため、私たちは以下の 2 つの対策を講じています。
(1) 重みの事前量子化のための最適化された MXFP8 量子化カーネルを提供します。
(2) アクティベーションの量子化を、dispatch all-to-all、エクスパートグループ化された GEMM、および SwiGLU に統合します。
ルーター重み勾配計算
MoK の逆伝播では、SonicMoE 方式の計算を用いてルーター重みの勾配も算出します。下向き投影(down-projection)の全出力を保存するのではなく、SwiGLU の活性化値と下向き投影の逆伝播勾配との内積からこれを計算します。この処理は SwiGLU の逆伝播に統合されており、追加的なメモリアクセスを防いでいます。
計算/通信 SM のチューニング
計算用 SM と通信用 SM の最適な比率はワークロードによって異なります。ローカルのトークン数やモデルの形状、ハードウェアの演算能力とネットワーク帯域幅が、ディスパッチ・コンバインタスクに要する時間をエキスパート FFN タスクと比較してどう変化させるかを決めます。同じワークロード内でも、順伝播と逆伝播では最適な比率が異なります。そこで MoK では、順伝播用と逆伝播用の通信 SM 数をそれぞれ設定できるチューニングパラメータを公開しています。
高速化効果
MoK の評価は、2 つ種類のベンチマークを用いて行いました。1 つ目は個別の MoE レイヤー向けベンチマークで、関連するすべてのベンチマークコードを公開しています。2 つ目は、複数の NVL72 ラックにわたる社内生産用トレーニングスタック上でのエンドツーエンドベンチマークです。
MoE レイヤー ベンチマーク
個別の MoE レイヤー実行についてベンチマークを行いました。対象は、グローバルトークンスケジューリング、トークンの全対全ディスパッチ、エキスパート FFN、トークンの全対全コンバイン、そして最終的な加重和です。順伝播と逆伝播をそれぞれ別々に、BF16 モードと MXFP8 モードの両方で評価しました。すべてのテストは単一の NVL72 ラック内で行い、ルーティング前の GPU あたり 2,048 トークン、EP degree は 64 と設定しました。
比較対象として選定したベースラインは、(1) MoE の順伝播と逆伝播の両方をサポートし、(2) NVL72 上で動作するものに限りました。
- NCCL + PyTorch
- DeepEP + PyTorch
- DeepEP + TransformerEngine
- HybridEP + Megatron(Nvidia が NVL72 で推奨している DeepEP + Megatron の代替オプション)
評価には、広く利用されているオープンウェイトモデルの形状を採用しました。
- Kimi K2.7 Code shape (: 384, : 7168, : 2048, top-k: 8)
- GLM-5.2 shape (: 256, : 6144, : 2048, top-k: 8)
- Qwen3.5-397B-A17B shape (: 512, : 4096, : 1024, top-k: 10)
- DeepSeek-V4-Pro shape (: 384, : 7168, : 3072, top-k: 6)
以下のグラフは、これらすべての形状における各ベースラインの結果を示しています。
全体として、最速のベースラインと比較すると、MoK は MXFP8 の順伝播で最大 2.37 倍、MXFP8 の逆伝播で 1.78 倍、BF16 の順伝播で 1.92 倍、BF16 の逆伝播で 1.58 倍高速であることが確認できました。
エンドツーエンドベンチマーク
公開されている実装との比較に加え、社内生産環境のトレーニングスタックにおける速度向上も報告します。従来、当社のトレーニングスタックは、DeepEP ベースの実装にカスタム MXFP8 MoE 計算カーネルを組み合わせて使用し、エキスパート並列化を実現していました。
ここでは、MoE レイヤーの実装方式(DeepEP と MoK)を変更しながら、エンドツーエンドのトークン毎秒数を比較しました。ベンチマークは、複数の GB300 NVL72 ラックにまたがる 512 GPU で実施されました。
| DeepEP ベースの | MoK | |
|---|---|---|
| GPU 当たり 1 秒あたりのトークン数 | 760.9 | 1,070.2 (1.41x) |
全体として、MoK は以前の DeepEP ベースの運用環境と比較してトークンあたりの処理速度を約 41% 向上させました。これにより、GB300 NVL72 インフラストラクチャ上でモデルのトレーニングをより効率的に行えるようになりました。
エージェント時代のメガカーネル
Mixture-of-Kittens(MoK)は本日、完全にオープンソース化されました。今後も継続的なメンテナンスを行い、コミュニティからのフィードバックや貢献を歓迎します。この取り組みを通じて AI 研究への参入障壁を下げ、より多くの研究者やラボが効率的にモデルをトレーニングできることを目指しています。また、MoK は高い柔軟性も重視しています。エージェントの支援を受けながら、既存のプラットフォーム以外でも素早く適応できるよう、修正しやすい設計に注力しました。
参考までに、高性能なカーネルを生成可能な AI モデルが登場したことで、カーネル最適化の領域はさらに面白くなっています。従来は手作業で記述されていた単一演算子のカーネルも、現在はほぼすべて AI が担当するようになりました。適切な設計指針(例えば、ワープをどう特化させるか、どの PTX 命令を使用するか)さえあれば、エージェントはワンショットで最先端のカーネルを生成できるようになります。
しかし、さらに興味深いのは、これによって得られる新たな能力です。昨年はメガカーネルを書くために、このような簡略化された抽象化レイヤーを作成する必要がありました。今年はフレームワークは不要でした。代わりに、追加の抽象化レイヤーを排除し、エージェントの支援を受けながら、複雑さを自ら解決してゼロから作り上げました。
エージェントが単純なタスク(例えば単一演算子カーネルなど)を自動化し、難しい作業(例えば分散 MoE メガカーネルの記述など)も、非常に少人数のチームで、はるかに速く完了させることができました。
とはいえ、数十万基の GPU にわたって実行されるハイリスクな ML ワークロードの最適化には、まだ多くの課題が残っています。この記事や同様の取り組みに興味を持っていただけた場合は、ぜひご連絡ください。hiring@cursor.com までお問い合わせください。
引用
本稿を利用する際は、以下のように引用してください:
Stuart H. Sul, Nash Brown, Henry Wildermuth, William Lin, and Federico Cassano. "Mixture-of-Kittens: MoE Megakernel for NVL72s." Cursor Research, Aug 2026. https://github.com/cursor/mixture-of-kittens
または BibTeX 形式では:
@misc{sul2026mok,
title={Mixture-of-Kittens: {MoE} Megakernel for {NVL72s}},
author={Stuart H. Sul and Nash Brown and Henry Wildermuth and William Lin and Federico Cassano},
organization={Cursor Research},
year={2026},
publisher = {GitHub},
howpublished = {\url{https://github.com/cursor/mixture-of-kittens}},
}*本稿の読了と有益なフィードバックを提供してくれた Chris Ré、Sasha Rush、Less Wright、Chen Lu、Nathan Wang に特別感謝いたします。*
- この記事では、dispatch は「すべてのノード間通信(all-to-all communication)」を指し、combine も同様に「すべてのノード間通信」を指します。通常、「combine」に含まれる重み付きの集約処理は、ここでは「重み付き和(weighted sum)」として別途言及します。↩
原文を表示
Today, we're open-sourcing Mixture-of-Kittens (MoK), our production MoE training megakernel for NVL72s.
As we have scaled the training and inference of Composer, our agentic coding model, the mixture-of-experts layer has consistently remained the major bottleneck. Depending on the workload and training configuration, it can consume more than half of end-to-end training time.
MoK addresses that bottleneck by fusing all MoE communication and computation into a single, fully deterministic kernel. It now powers Composer training across tens of thousands of GPUs.
You can try MoK and explore the code on GitHub. We look forward to your feedback and contributions.
MoK grew out of several earlier attempts to speed up the MoE layer. Over the past year, we wrote our own MXFP8 and NVFP4 training kernels and developed the "warp decode" approach for MoE inference.
But those techniques optimized only the compute portion of the layer and assumed inter-GPU communication would be handled separately. In our production workloads, communication had become the limiting factor. That led us to redesign the full MoE layer from first principles, with communication built directly into the kernel.
In addition, our move to GB300 NVL72s changed the problem in two important ways. First, an NVL72 is a multi-node rack within a single NVLink domain, enabling fast, fine-grained overlap of computation and communication across all 72 GPUs.
Second, the integrated Grace CPUs (the "G" in GB300) tend to be slow relative to the GPUs. We found that GPU streams easily caught up to CPU-side work, causing the GPU to be completely idle during that time. So we have to aggressively minimize CPU work and CPU-GPU synchronization.
Our solution to this set of challenges is Mixture-of-Kittens (MoK), a highly optimized MoE training megakernel built from first principles for NVL72s. MoK fuses all MoE communication and computation into a single kernel, is fully deterministic, and achieves state-of-the-art performance against publicly available implementations.
In our production training stack across several NVL72 racks, MoK increased end-to-end tokens per second by 1.41x.
The rest of this post explains the key ideas behind MoK, including how we chose the right communication direction, structured the overlap between computation and communication, and eliminated CPU-GPU synchronization with ring token buffers. We also cover the megakernel design, determinism, MXFP8 support, and several other implementation details.
Overlapping computation and communication in MoE
MoK targets DeepSeek-V3 (DSV3)-style MoE layers, which are widely used across open-weight models including GLM, Qwen, Kimi (up to K2.7), and DSV itself. These layers combine one shared expert with many routed experts, often hundreds.
For each token entering the MoE layer, a router projection selects the top-k routed experts and assigns a router weight to each one. Each selected expert then runs the standard feed-forward network computation, consisting of up and gate projections, a SwiGLU activation, and a down projection. The layer combines the outputs of the shared and routed experts using the router weights.
We use the following notation:
- = model dimension
- = expert intermediate dimension
- = set of routed top-k experts
- Input token
- Router weights
- Expert weights , ,
And the MoE layer computes:
where
With expert parallelism (EP), we shard the routed experts and spread their weights across many GPUs, or ranks, and we call the number of ranks that collectively hold all expert weights the EP degree. For example, with 256 routed experts and an EP degree of 64, each rank holds 4 routed experts, plus the shared expert. As a result, tokens must be transferred across GPUs before and after the MoE layer, according to the router projection.
The most straightforward implementation of distributed MoE sends each token to the ranks holding its assigned experts (dispatch all-to-all), runs the FFN, returns the results to each token's original rank (combine all-to-all), and takes a weighted sum of the expert outputs. But because the communication can take as long as the computation itself, running the two sequentially is inefficient.
The standard remedy is to overlap dispatch/combine1 communication with per-expert FFN through pipelining: transfer one chunk of tokens, compute FFN on it while overlapping transfer of the next chunk, and repeat. MoK is one variant of this scheme, with a set of novel, target-specific techniques that make it faster than existing baselines.

Choosing the right communication direction
When sending tokens across GPUs, one can choose a push-based mechanism, where the GPU that owns the tokens actively stores them into the remote destination GPUs, or a pull-based mechanism, where the GPU that needs the tokens loads them from the remote source GPUs. Existing approaches often rely on push-based communication for scattering and gathering tokens across GPUs (e.g., DeepEP).
The common notion is that pushing saturates inter-GPU links better, since it involves less protocol communication, and thus it becomes the default choice. Our observation, however, is that each mechanism has its own tradeoffs, and choosing the right one for each communication operator matters for maximizing performance, for the following three reasons.
Scheduling
To dispatch and combine tokens as fast as possible, the following conditions must hold:
- All NVLink lanes interconnecting the 72 GPUs in the rack must stay saturated. We cannot afford stretches of time where tokens travel over only a subset of (source → destination) lanes, so tokens must be selected such that each source rank's sends are spread evenly across all destination ranks at any given moment.
- Tokens sent to a rank should arrive ordered by that rank's local experts. If arrivals are unordered, the expert-grouped GEMMs wait longer for a full tile of tokens before the tensor core matrix multiplications can begin.
- There should be zero local copies. Tokens for an expert should land directly in contiguous memory, so the grouped GEMMs can start without reordering anything locally.
- The overhead of satisfying the above three conditions must stay minimal. We want to spend most of the time actually sending tokens, and very little time scheduling or searching for tokens to send.
With push-based dispatch, we need to produce a schedule table with columns {src_index, dst_rank, dst_index}, where src_index is the index into the local incoming activation buffer and dst_index is the location in the destination rank's memory where the token must land. The row index of this table will decide the order in which tokens are sent over NVLink. We want the rows of this table to cycle through dst_rank round-robin, so that every connected lane stays busy.
Among the rows targeting a given dst_rank, the dst_index values must be interleaved evenly across all source GPUs, and they must also follow increasing local expert order on that destination rank. Building this table involves multiple sorts, and each rank's schedule must account for every other rank's, since no two source ranks can write to the same dst_index.
| Schedule Idx | Src Idx | Dst Rank | Dst Idx |
|---|---|---|---|
| 0 | 17 | 0 | 0 |
| 1 | 0 | 1 | 0 |
| 2 | 29 | 2 | 0 |
| 3 | 3 | 3 | 0 |
| … | … | … | … |
Push-based schedule: consecutive entries must interleave destination rank for full network saturation.
| Schedule Idx | Src Rank | Src Idx |
|---|---|---|
| 0 | 0 | 0 |
| 1 | 1 | 5 |
| 2 | 2 | 13 |
| 3 | 3 | 7 |
| … | … | … |
Pull-based schedule: consecutive entries must interleave source rank for full network saturation.
With pull-based dispatch, the schedule table simplifies to two columns, {src_rank, src_index}, and the row index of the table directly corresponds to the token index in the local destination buffer.
In theory, the number of rows in the table and in the destination buffer would match (which would require large memory allocation; more on this in a later section). No sorting is needed here. We walk over the router projection results, and whenever we find a token that should land on the current rank, we write its source rank and index into our schedule. The algorithm looks as follows:
Inputs: routing tensor , where is the global index of the expert assigned to the -th route of token on source rank ; local rank ; experts per rank
Outputs: token counts per local expert , token counts per local expert and source rank , region offsets , schedule table
,
for to , to , to do
if then
;
end if
end for
for to do
end for
for to , to , to do
if then
;
end if
end for
In practice, our schedule kernel implementing this algorithm takes less than 3% of the total MoE runtime, and runs fully on the device-side without any CPU-GPU communication.
We can also reuse this schedule as-is for combine by choosing push-based combine, simply reading {src_rank, src_index} as {dst_rank, dst_index}. In fact, we can build the schedule once and reuse it for all four communication operations across forward and backward, by choosing:
- Pull-based forward dispatch
- Push-based forward combine
- Pull-based backward reverse-combine
- Push-based backward reverse-dispatch
The schedule is only a few megabytes in the worst case, so we can keep it around for reuse without any memory pressure. An additional benefit is that this completely eliminates inter-GPU, multi-lane signaling, as explained later below.
NVLink bandwidth utilization
As with any networking system, all user data (payload) being transferred over NVLink is sent with additional protocol metadata containing information about the source and destination, acknowledgements, etc., that depends on the networking protocol. While the details of the NVLink communication protocol are undisclosed, we can observe the data being sent on the link and reason about it carefully.
What we find is that push-based NVLink communication moves fewer total bytes (i.e., protocol metadata plus payload) and sends almost everything in one direction, so it achieves higher bandwidth utilization when all lanes are fully busy.
On the other hand, pull-based transfers move more bytes in total, but the protocol metadata is more split between both directions of the link. The puller first sends metadata one way, then receives more metadata plus the payload the other way.
We can verify this by writing a simple cross-GPU transfer kernel, and profiling it with NCU. For instance, in our microbenchmark sending one 256x256 BF16 tile (131,072B) over NVLink, we observe the following:
| Total | Total RX | Total TX | Protocol RX | Protocol TX | Payload RX | Payload TX | |
|---|---|---|---|---|---|---|---|
| Push | 159.6 KB | 2.9 KB (1.84%) | 155.6 KB (99.16%) | 2.7 KB | 24.6 KB | 0 KB | 131.1 KB |
| Pull | 172.0 KB | 147.5 KB (85.71%) | 24.6 KB (14.29%) | 16.4 KB | 24.6 KB | 131.1 KB | 0 KB |
In the above table, RX refers to the receiving direction, while TX refers to the sending direction. We can see that push involves roughly 12.4 KB less bytes in total, ideally leading to better NVLink saturation overall. However, NVLink has separate lanes for each direction. To reach the bandwidth advertised in the NVLink spec (1.8 TB/s for fifth-generation NVLink on GB300), traffic must fully flow both ways. And, for a workload like MoE dispatch/combine, each transfer tends to be only a few kilobytes and imbalanced, leaving execution bubbles on the link.
Empirically, pull-based communication delivers up to 29% higher NVLink bandwidth utilization than push-based communication when dispatching tokens under expert imbalance, making it the better choice.
Signalling
Signalling is necessary because the GPUs must know when dispatch/combine communication has completed. The GPUs can only begin operating on the received tokens after they receive a completion signal. It is important to minimize this signalling overhead, and choosing the right communication direction has a meaningful impact.
With push-based dispatch or pull-based combine, completion signals must cross between GPUs. Before an expert-grouped GEMM can begin, a rank must wait for signals from as many as 71 peers and flush memory across the rack by issuing a memory fence. In addition, the data is already sitting at the destination during the signal's transit time.
With pull-based dispatch and push-based combine, cross-GPU signalling disappears. Each rank issues a load, waits for the data to arrive, and can begin using it immediately. No multi-node synchronization is required. Also, only a single entity performs signalling, so the overhead does not grow with the degree of expert parallelism.
We found this signalling overhead to be quite large. In our multi-node microbenchmarks, push-based dispatch signalling incurs roughly 5.8x higher latency than pull-based dispatch signalling, at 103 µs versus 18 µs, and the cost accumulates quickly within a megakernel.
Based on these results, MoK uses pull-based forward dispatch, push-based forward combine, pull-based backward reverse-combine, and push-based backward reverse-dispatch.

Properly structuring the computation-communication overlap
Computation-communication granularity
Computation-communication granularity is the granularity at which computation and communication tasks signal each other of completion. It comes with an interesting trade-off.
At one extreme, communication can be very fine-grained. We send only enough tokens for the tensor cores to launch a full matrix-multiply-accumulate instruction, 256 on Blackwell, for instance, and continuously overlap each transfer with the matrix multiplications. Comet is a good example of this approach.
At the other extreme, communication can be coarse-grained. We send thousands or tens of thousands of tokens at once and wait for the full batch to arrive before beginning the tensor core operations. DeepEP is a good example here.
Our observation is that neither extreme is the right choice. The optimal point sits somewhere in the middle and depends on the workload.
If the communication is too fine-grained, the tensor cores never fully saturate. Nvidia tensor cores are heavily pipelined, bandwidth-optimized accelerators, and we want to keep feeding them rather than hitting a barrier wait every few MMA instructions.
If the communication is too coarse-grained, the tensor cores wait too long for the first round of tokens to arrive and for the final round of outputs to be combined.

We call the set of tokens we send each round a minibatch. MoK makes the minibatch size a tunable parameter so that it's easy to sweep possible configurations before running a workload.
A rough heuristic for choosing the optimal minibatch size is to consider how many waves each operator (e.g., expert-grouped GEMM) requires.
A wave is one round of concurrent execution across all SMs on the GPU. If a work is too small (e.g., an expert-grouped GEMM receives too few tokens), we get a partial wave, in which some SMs sit idle. Alternatively, we may see a tail effect, where the final wave occupies only a fraction of the SMs.
We find it useful to choose a minibatch size that gives each expert grouped-GEMM operator in the FFN at least two full waves. This works for two reasons: (1) a single full wave is enough to fully saturate the tensor cores; and (2) with two waves, the second wave's MMAs overlap with the first wave's epilogue and dependent operators (e.g., the SwiGLU activation or the next grouped GEMM), amortizing the tail effect.
Specifically, on Blackwell GPUs, fully utilizing the tensor cores for an GEMM, where is the reduction dimension, means each SM works on a output tile, regardless of 1-SM or 2-SM MMA.
So given:
- : number of tokens per transfer (i.e., the minibatch size)
- : hidden dimension
- : expert intermediate dimension
- : number of SMs
We want each GEMM to produce enough tasks to fill at least two waves, while keeping the communication granularity as small as possible for maximum overlap. For the up and gate projections, which run simultaneously since neither depends on the other, this means:
For the down projection, which cannot run in parallel with other operators:
Combining the two, the requirement we are looking for is:
For example, take Kimi 2.5, the base model for Composer 2.5, where and . On Blackwell GPUs, , so we get:
On our microbenchmark running MoK forward for Kimi 2.5 shapes, we observe the following, roughly matching the heuristics:
| ms | |
|---|---|
| 512 | 5.981 |
| 1024 | 4.669 |
| 1536 | 3.981 |
| 2048 | 3.666 |
| 2560 | 3.425 |
| 3072 | 3.447 |
| 3584 | 3.524 |
| 4096 | 3.473 |
Scheduling the overlaps
Once the communication granularity is decided, the next question is how to schedule the different tasks (dispatch, combine, FFN) on GPUs so that the overlap happens efficiently. To do this, we employ the inter-SM overlapping technique, in which we assign some SMs to the expert FFNs (comp SMs) and some to dispatch/combine (comms SMs), and have the two groups signal each other through a local counter. This is possible because with Tensor Memory Accelerator (TMA) loads and stores, we can fully saturate NVLink bandwidth with less than a third of the SMs.
We use inter-SM overlapping as our base strategy. In the MoE forward pass, the comms SMs begin the pull-based dispatch all-to-all and signal the comp SMs each time a full minibatch of tokens has arrived and is ready for computation. The comp SMs then run MLP-SwiGLU atomically on those tokens, and once everything through the down projection is complete, they signal the comms SMs that the tokens are ready for combine.
Comms SMs go through all of dispatch tasks before doing any combine all-to-all tasks, so many completion signals arrive from the comp SMs by the time comms SMs are done with dispatch. Comms SMs will then move on to the push-based combine while the comp SMs finish the FFNs for the last-arriving tokens. Additionally, we can fit in shared expert FFN while the comp SMs wait for the first dispatch to complete.

The backward pass is similar, though it gets a bit more complex with forward replay (more on this in the next section). The comms SMs perform the reverse-combine and signal the comp SMs to run the FFN backward, excluding the wgrads (i.e., 3 expert-grouped GEMMs for 3 dgrads and the SwiGLU activation backward), and the comp SMs signal the comms SMs once the FFNs are complete and the tokens are ready for the reverse-dispatch.
We perform the three remaining wgrads after the dgrads for all minibatches are complete. We delay the wgrads for two reasons: (1) reverse-dispatch does not depend on them, so wgrads can overlap with the last reverse-dispatches; and (2) since the token axis is the reduction dimension for wgrads, accumulating over the full token axis at once minimizes numerical instability. As in the forward pass, we overlap the full shared expert backward with the first round of reverse-combine.

Fully eliminating CPU-GPU synchronization with ring token buffers
The last missing piece is handling MoE dynamism; we do not know in advance how many tokens will arrive at a destination rank.
There are two existing ways to handle this. The first is token dropping, where any tokens beyond a fixed buffer size are simply ignored and never enter the expert FFNs. The second is CPU-GPU synchronization, where the per-rank token counts computed from the router results are sent to the CPU, and the CPU uses them to allocate exact-sized buffers.
Both have obvious downsides. We do not want to drop tokens for the sake of training quality, and CPU-GPU synchronization prevents the GPU stream from running ahead of the CPU thread.
CPU-GPU synchronization is especially costly on GB300 NVL72s because the integrated Grace CPUs are very slow. Our traces often show GPU kernels bottlenecked by CPU work such as logging or pushing training metrics to a remote server, even though the same training stack caused no such problems on DGX machines with Intel CPUs. Given this, we want to minimize CPU-GPU synchronization as much as possible.
In MoK, we resolve this with a ring token buffer, which we call macrobatch. Instead of allocating a large, mostly unused destination buffer for the comms SMs to transfer tokens into, we create a fixed-size ring buffer of a few hundred megabytes and cycle through it at minibatch granularity.
Implementing macrobatching efficiently requires careful overlap of computation and communication. Neither should stall because a buffer slot is still in use or because its data has not yet arrived. Ideally, we overlap across the ring boundary, computing on the tail end of the buffer while dispatching into its start.
The key idea is to drain each slot as early as performance allows so that it can be reused as early as possible. In the forward pass, dispatch fills slots and combine drains them, freeing them for the next dispatch.
However, if we run all dispatches followed by all combines (as in the previous section's schedule), the next macrobatch cannot begin until every combine has finished, and computation and communication only start overlapping again after its first dispatch completes.
To avoid this, we perform dispatch-combine interleaving for the forward pass. The combine for a minibatch of the previous macrobatch is interleaved with the dispatch for the corresponding minibatch of the next macrobatch, which uses the same region of the ring buffer. This way, the buffer is refilled as soon as it is emptied. Note that the first macrobatch's dispatches and the last macrobatch's combines are not interleaved, as they do not have their counterparts.

Reversed ring to minimize forward replay during backward
The backward pass is slightly more complex because the backward megakernel must support forward activation replay. The macrobatch buffer is a ring, so if there are multiple macrobatches, some saved activations are overwritten during the forward pass and must be recomputed. The replay only needs to run up to the SwiGLU activation, since backward only needs the down projection's input activation and output gradient, not its forward output.
One key optimization here is the reversed ring. If we consume tokens in ascending macrobatch order, the last macrobatch leaves the ring buffer partially filled, and the backward pass ends up with more activations to replay. MoK therefore walks through the tokens in reverse macrobatch order during the forward pass, so that the saved ring buffer is always either completely full or contains all of the tokens, minimizing forward replay during the backward pass.
Other features and implementation details
Megakernel
We built MoK using the megakernel technique, where instructions overlap at the SM task level instead of being separated by kernel launch boundaries. Megakernels are usually suited for fusing an entire forward or backward step, but we found the technique useful for MoK for two reasons: (1) implementing minibatching and macrobatching without megakernel fusion would require multiple kernel launches, increasing the launch boundary overhead; and (2) for inter-SM overlapping, we found multiple streams with green contexts unreliable at partitioning SMs exactly as intended, while software partitioning gives exact allocation guarantees.
Determinism
For internal ablations and on-policy RL post-training, we designed MoK to be fully deterministic. The order of floating point operations is fixed, so the same input produces bitwise-identical output regardless of hardware scheduling and instruction issue order.
Cluster Launch Control (CLC) for RDMA overlap
The MoK megakernel schedules its tasks through CLC to allow RDMA overlap. CLC is a hardware-native work-stealing feature introduced in Blackwell as a new way of implementing persistent grid kernels. A CLC-based kernel can naturally yield to kernels on a higher-priority stream without having to wait for the current kernel to fully complete. This matters because, during training, we often need to overlap intra-rack communication and computation with inter-rack InfiniBand/RoCE communication, such as FSDP all-gather, which require SMs of their own. Without CLC, the inter-rack communication would serialize behind the megakernel, causing significant performance degradation.
MXFP8 support
MoK supports both BF16 and MXFP8 precision modes. We train in MXFP8 mode, since it is faster and causes no numerical issues. In MXFP8 mode, the shared expert stays in BF16, as we found this to affect the training stability.
One added cost of MXFP8 is that tensors must be quantized before they are fed into the tensor cores. To minimize this overhead, we (1) provide an optimized MXFP8 quantization kernel for pre-quantizing the weights; and (2) fuse activation quantization into the dispatch all-to-all, the expert-grouped GEMMs, and the SwiGLU.
Router weight gradient computation
The MoK backward pass also computes the router weight gradients, using a SonicMoE-style calculation. Instead of saving the full down-projection output, it computes them from the inner product of the SwiGLU activation and the down-projection dgrad. This computation is fused into the SwiGLU backward to avoid additional memory traffic.
Computation/Communication SM tuning
The optimal ratio of comp to comms SMs depends on the workload. The local token count, the model shapes, and the hardware compute and network bandwidth all change how long the dispatch/combine tasks take relative to the expert FFN tasks. Even within the same workload, the optimal ratio differs between the forward and backward passes. MoK therefore exposes tunable parameters that set the number of comms SMs separately for forward and backward.
Speedups
We evaluated MoK with two types of benchmarks: individual MoE layer benchmarks, for which we release all benchmark code, and end-to-end benchmarks on our internal production training stack across multiple NVL72 racks.
MoE layer benchmarks
We benchmark single MoE layer execution: global token scheduling, token dispatch all-to-all, expert FFNs, token combine all-to-all, and the final weighted sum. We benchmarked the forward and backward passes separately, in both BF16 and MXFP8 modes. All tests ran within a single NVL72 rack, with EP degree 64 and 2,048 tokens/GPU before routing.
We chose baselines that (1) support both the MoE forward and backward passes; and (2) run on NVL72s:
- NCCL + PyTorch
- DeepEP + PyTorch
- DeepEP + TransformerEngine
- HybridEP + Megatron, which is Nvidia's recommended option on NVL72s over DeepEP + Megatron
We tested the shapes of widely used open-weight models:
- Kimi K2.7 Code shape (: 384, : 7168, : 2048, top-k: 8)
- GLM-5.2 shape (: 256, : 6144, : 2048, top-k: 8)
- Qwen3.5-397B-A17B shape (: 512, : 4096, : 1024, top-k: 10)
- DeepSeek-V4-Pro shape (: 384, : 7168, : 3072, top-k: 6)
The graphs below show results for all four shapes across all baselines:
Overall, we observe that MoK is up to 2.37x faster for the MXFP8 forward, 1.78x for the MXFP8 backward, 1.92x for the BF16 forward, and 1.58x for the BF16 backward, compared to the fastest baseline.
End-to-end benchmarks
In addition to the benchmarks against publicly available implementations, we report speedups on our internal production training stack. Previously, our training stack relied on a DeepEP-based implementation with custom MXFP8 MoE computation kernels for expert-parallelism. We compare the end-to-end tokens per second metric by varying the MoE layer implementation choice (DeepEP vs MoK). The benchmarks were conducted on 512 GPUs across several GB300 NVL72 racks.
| DeepEP-based | MoK | |
|---|---|---|
| Tokens / second / GPU | 760.9 | 1,070.2 (1.41x) |
Overall, MoK delivered an approximately 41% tokens-per-second speedup over our previous DeepEP-based production setup, allowing us to train our models more efficiently across our GB300 NVL72 infrastructure.
Megakernels for the agent era
Mixture-of-Kittens is fully open-sourced as of today. We plan to keep maintaining it and welcome community feedback and contributions. Our hope is that this lowers the barrier to AI research, allowing more researchers and labs to train models efficiently. MoK also aims to be flexible. We put a lot of effort into making it easy to modify, so that with the help of agents, you can quickly adapt it to platforms beyond the ones we run on.
As a side note, with the arrival of AI models capable of writing performant kernels, the kernel optimization space has become a lot more interesting. Single-operator kernels that used to be written by hand are now almost entirely handled by AI. Given the right design directions (e.g., how to specialize warps, which PTX instructions to use), agents can one-shot state-of-the-art kernels.
Even more interesting, however, is the new capabilities this gives us. Last year, writing a megakernel required creating a layer of simplified abstractions like this one. This year, we did not need a framework. Instead, we removed the additional layer of abstraction and, with the help of agents, worked through the complexity ourselves from scratch.
Agents automated the simpler tasks (e.g., single-operator kernels) and helped us get through the harder ones (e.g., writing a distributed MoE megakernel) much faster and with a very small team.
That said, there is plenty of work ahead of us in optimizing high-stakes ML workloads that run across hundreds of thousands of GPUs. If you find this post and this type of work interesting, we would love to hear from you. Reach out to us at hiring@cursor.com.
Citation
If you use this work, please cite:
Stuart H. Sul, Nash Brown, Henry Wildermuth, William Lin, and Federico Cassano. "Mixture-of-Kittens: MoE Megakernel for NVL72s." Cursor Research, Aug 2026. https://github.com/cursor/mixture-of-kittens
Or in BibTeX:
@misc{sul2026mok,
title={Mixture-of-Kittens: {MoE} Megakernel for {NVL72s}},
author={Stuart H. Sul and Nash Brown and Henry Wildermuth and William Lin and Federico Cassano},
organization={Cursor Research},
year={2026},
publisher = {GitHub},
howpublished = {\url{https://github.com/cursor/mixture-of-kittens}},
}*Special thanks to Chris Ré, Sasha Rush, Less Wright, Chen Lu, and Nathan Wang for reading this post and offering valuable feedback.*
- In this post, dispatch refers to the dispatch all-to-all communication and combine refers to the combine all-to-all communication. The weighted reduction usually included in "combine" is referred to separately as the weighted sum. ↩
同じ出来事を3媒体で確認
同じ出来事を扱う別媒体の記事です。見出しと公開時刻を比較できます。
News to Guide
ニュースの次に確認する
発表内容を、現在の料金や仕様と照らし合わせられる関連ガイドです。
今日のまとめ
AIデイリーブリーフで今日の重要ニュースをまとめ読み