Packed MXFP4 & NVFP4 GEMM + mega-MoE (SM100, true 2-CTA multicast)#2
Packed MXFP4 & NVFP4 GEMM + mega-MoE (SM100, true 2-CTA multicast)#2ipiszy wants to merge 9 commits into
Conversation
Adds a standalone packed-FP4 x packed-FP4 GEMM (`mxfp4_gemm_nt`) and a packed MXFP4 mega-MoE (`mxfp4_mxfp4_mega_moe`) for SM100, both using a true 2-CTA `cta_group::2` multicast TMA load path (`SM100_TMA_2SM_LOAD`) with leader-routed barriers and per-CTA scale-factor loads. Integrated onto the unified mega-MoE API (`mma_type`/`parse_mma_kind`, ring-token buffering): adds `MmaKind::MXFP4` with sub-byte (`get_element_bits`) token byte math, packed-FP4 symmetric-buffer slicing, and the packed-FP4 epilogue that writes E2M1 nibbles directly to their [token][inter] positions. Validated on B200: standalone diff=0.0; mega-MoE diff=7.5e-4. Co-authored-by: Cursor <cursoragent@cursor.com>
38b8e75 to
dd1a973
Compare
Extends the unified packed-FP4 kernels (standalone GEMM and mega-MoE) to also support NVFP4 (E2M1 data, E4M3/UE4M3 scale factors at gran-16, with per-tensor global scales), keyed on `MmaKind`. Adds `nvfp4_gemm_nt` and `nvfp4_nvfp4_mega_moe` entries (mma_type="nvfp4xnvfp4"), the NVF4 2-CTA `kind::mxf4nvf4` MMA atom, NVFP4 quant utils, and tests. Global scales are CPU-side scalar kernel params: the GEMM/MoE accumulators are dequantized by gs_act * gs_weight, and the MoE L1 output is requantized with the L2 activation global scale. Key gran-16 scale-factor handling (vs gran-32 MXFP4): 2 scale-factor int32s per K-block (stride/smem/tmem sized accordingly), and crossing K-uint32s via the SF tensor-memory ADDRESS since `a_sf_id_`/`b_sf_id_` are 2-bit descriptor fields (sf_id only selects within a K-uint32). Validated on B200: standalone GEMM diff=0.0; mega-MoE diff=8.4e-4; MXFP4 paths unchanged (regression intact). Co-authored-by: Cursor <cursoragent@cursor.com>
Benchmarks the standalone GEMM and mega-MoE for both MXFP4 and NVFP4 using `bench_kineto` (pure device time, kernel-name filtered + L2-flushed, the repo-standard method), reporting per-shape latency, GEMM TFLOPS, and the nvfp4/mxfp4 ratio. Device-time results (B200): mega-MoE nvfp4 is ~1.04-1.06x the mxfp4 time, standalone GEMM ~1.09-1.16x. The residual is gran-16 doubling the scale factors (2x UTCCP copies + 2x SF traffic) vs gran-32 MXFP4. Co-authored-by: Cursor <cursoragent@cursor.com>
Switch the NVFP4 mega-MoE from per-tensor scalar global scales to per-expert
(num_experts_per_rank,) float32 device tensors, matching the TRT-LLM format:
- gate_alpha / up_alpha = 1/(l1_input_gs * gate|up_weight_gs) -> L1 acc dequant
(applied per gate/up column before SwiGLU)
- l2_input_global_scale (= 448*6/amax) -> L1-output requant
- down_alpha = 1/(l2_input_gs * down_weight_gs) -> L2 acc dequant
All indexed per local expert in the fused epilogue. Updates the C++/Python
entries, the test (per-expert gate/up/down weight scales + per-expert
intermediate global scale), and the benchmark.
Validated on B200: nvfp4 mega-MoE diff=5.8e-4; mxfp4 + standalone GEMMs unchanged.
Co-authored-by: Cursor <cursoragent@cursor.com>
The per-expert global scales (gate/up/down alpha, l2_input_global_scale) were re-read from global memory inside the per-atom epilogue loops. Hoist them to once-per-block registers. NVFP4 mega-MoE overhead drops from ~1.10x to ~1.05-1.08x vs MXFP4 (the residual is gran-16 SF traffic, not scale loading). Correctness unchanged (diff=5.8e-4). Co-authored-by: Cursor <cursoragent@cursor.com>
Adds device-time benchmarks comparing DeepGEMM mega-MoE (fp8xfp4 / nvfp4) against FlashInfer's NVFP4 MoE backends (cute_dsl, cutlass, trtllm-gen): - tests/bench_flashinfer_vs_deepgemm.py: single-device comparison (CUDA-graph device time) across all backends; EP-aware builders (local_expert_offset / ep_rank) for cute_dsl and cutlass. - tests/bench_ep_multi_gpu.py: multi-GPU expert-parallel benchmark. DeepGEMM uses its native fused dispatch+combine; FlashInfer uses replicated input + expert-sharded compute + all_reduce combine. Per-kernel device time summed from traces; moe/all_reduce breakdown; NVLS combine via symm-registered buffer; sleep-aligned collective timing; avg-over-ranks aggregation. - tests/bench_kernel_breakdown.py: per-kernel device-time breakdown per backend. - tests/fi_trtllm.py + tests/_fi_vendor/: thin driver over FlashInfer's trtllm_fp4_block_scale_moe, reusing the vendored (v0.6.11) weight-shuffle and routing harness. - tests/bench_packed_fp4.py: add fp8xfp4 mega-MoE column to the mega benchmark. Co-authored-by: Cursor <cursoragent@cursor.com>
Reorganize the FP4 MoE handoff note under a new doc/ directory and rename it to reflect its subject. Co-Authored-By: Claude <noreply@anthropic.com>
* Move the four bench_*.py scripts from tests/ into a new benchmarks/
directory; fix their sys.path (add tests/) and repoint helper imports
at the consolidated test modules (signatures preserved via aliases).
* Consolidate the mxfp4/nvfp4 test pairs into two parameterized scripts:
tests/test_fp4_gemm.py and tests/test_fp4_mega_moe.py, each looping
over ('mxfp4', 'nvfp4') via a per-format strategy. Delete the four
old test_mxfp4_*/test_nvfp4_* files. Update doc/fp4_moe.md test paths.
* Expand mega-MoE coverage: 1-rank shape matrix (8 shapes: small, odd,
single-token, large-asymmetric, masked) and a multi-rank EP matrix
(world 2/4/8) via torch.multiprocessing.spawn + init_dist. Multi-rank
uses replicated x/weights/routing so the NVFP4 per-expert l2act_gs
reference is computable without a cross-rank all-gather; each rank
asserts against the full reference. Helper signatures kept stable for
the benchmark imports.
Verified on B200: 32 mega-MoE cases pass (baseline mxfp4 0.00075 /
nvfp4 0.00058 unchanged; 1-rank 16 + EP2 6 + EP4 4 + EP8 4, all
diff < 0.05, incl. masked routing); GEMM 10/10 diff=0.0.
Co-Authored-By: Claude <noreply@anthropic.com>
| 5) stop timer | ||
| so its number includes the cross-device communication cost. | ||
|
|
||
| We report the worst-rank average latency (max over ranks). |
There was a problem hiding this comment.
EP benchmark uses mean not max
Low Severity
The module header and several helpers claim worst-rank (max-over-ranks) latency, but rank_avg_ms all-reduces with SUM and divides by world size, so printed DeepGEMM and FlashInfer EP numbers are means. Straggler ranks are under-represented versus the documented metric.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit 0d7c72a. Configure here.
…; add README * Fold the per-kernel diagnostic into bench_flashinfer_vs_deepgemm.py as a --breakdown mode (kernel_breakdown + show_breakdown helpers, breakdown branch in bench_one, --breakdown CLI flag). Delete the standalone bench_kernel_breakdown.py (nothing imported it). * Add benchmarks/README.md documenting the three benchmarks (FP4-format, single-device DeepGEMM-vs-FlashInfer, multi-GPU EP), prerequisites, how to run, and captured B200 results (DeepGEMM ~1.6-1.9x faster than FlashInfer cutlass single-device; ~1.7x faster fused-EP at world=2), plus a --breakdown example and compatibility notes. Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 5de63a6. Configure here.
| world = dist.get_world_size(group) | ||
| t = torch.tensor([per_iter_ms], device='cuda', dtype=torch.float64) | ||
| dist.all_reduce(t, op=dist.ReduceOp.SUM, group=group) | ||
| return t.item() / world |
There was a problem hiding this comment.
EP bench mean not worst-rank
Medium Severity
The multi-GPU EP benchmark text says it reports worst-rank latency, but rank_avg_ms all-reduces per-rank times with SUM and divides by world size, so printed DeepGEMM and FlashInfer numbers are cross-rank means. Straggler ranks are smoothed out, which misstates end-to-end EP latency when ranks diverge.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit 5de63a6. Configure here.


Summary
Adds packed-FP4 × packed-FP4 GEMM and mega-MoE kernels for SM100 (Blackwell), supporting both MXFP4 and NVFP4 through a single unified kernel keyed on
MmaKind:mxfp4_gemm_nt,nvfp4_gemm_nt—[M,K] @ [N,K].T -> [M,N](BF16 out).mxfp4_mxfp4_mega_moe,nvfp4_nvfp4_mega_moe(mma_type="mxfp4xmxfp4"/"nvfp4xnvfp4").Both operands are packed E2M1 (2 elems/byte). MXFP4 uses UE8M0 scale factors at gran-32; NVFP4 uses E4M3/UE4M3 scale factors at gran-16 with per-tensor global scales passed as CPU-side scalar kernel params (dequant:
acc * gs_act * gs_weight; the MoE L1 output is requantized with the L2-activation global scale).Key technical details
cta_group::2multicast TMA (SM100_TMA_2SM_LOAD) for activations/weights, with leader-routed barriers and per-CTA scale-factor loads.kind::mxf4(block32) for MXFP4,kind::mxf4nvf4(block16 /scale_vec::4X) for NVFP4; UE8M0 vs UE4M3 instruction descriptors.sf_id, which is a 2-bit descriptor field (a_sf_id_/b_sf_id_) and only selects within a K-uint32.parse_mma_kind/ ring-token buffering / sub-byte token byte math viaget_element_bits).out_scale(default 1.0, no-op for fp8/bf16/mxfp4).Validation (B200)
Benchmark (device time via
bench_kineto)The residual nvfp4 overhead is gran-16 doubling the scale factors (2× UTCCP copies + 2× SF traffic).
Tests
tests/test_mxfp4_gemm.py,tests/test_nvfp4_gemm.py,tests/test_mxfp4_mega_moe.py,tests/test_nvfp4_mega_moe.py,tests/bench_packed_fp4.py.Known limitations / follow-ups
gemm_nt(standalone) + mega-MoE; nonn/tn/tt,m_grouped, ork_groupedvariants yet. The standalone GEMM is a fixed-config "de-risk" kernel (no autotuning; requiresN%256, M%128, K%128).BLOCK_K=256(small-token configs) is routed through the new address-based SF path but onlyBLOCK_K=128is covered by mxfp4 tests; nvfp4BLOCK_K=256is tested.Note
High Risk
Large new SM100 kernel surface (GEMM + fused mega-MoE/EP) with distinct MXFP4 vs NVFP4 numerics and symmetric-buffer layout changes; regressions would affect core MoE performance and correctness on Blackwell.
Overview
Adds SM100-only packed FP4×FP4 compute: standalone
mxfp4_gemm_nt/nvfp4_gemm_ntand fusedmxfp4_mxfp4_mega_moe/nvfp4_nvfp4_mega_moe, wired through Python/C++ APIs and new JIT CUDA kernels (2-CTAmxf4/ NVF4 MMA, gran-32 vs gran-16 scale factors, NVFP4 global-scale dequant via optionalout_scaleon the swap-AB epilogue).Mega-MoE plumbing is extended for sub-byte activations: new
MmaKind::MXFP4/NVFP4,get_element_bits/get_sf_gran_k,mma_typestringsmxfp4xmxfp4/nvfp4xnvfp4, and symmetric-buffer views that use int8 packed tokens and SF layouts sized by granularity (NVFP4 MoE takes per-expertgate_alpha/up_alpha/l2_input_global_scale/down_alpha).Ships a
benchmarks/suite (internal MXFP4 vs NVFP4, DeepGEMM vs FlashInfer single-GPU, multi-GPU EP vs NCCL combine) plus a README with captured B200 numbers and run instructions.Reviewed by Cursor Bugbot for commit 5de63a6. Bugbot is set up for automated code reviews on this repo. Configure here.