Repository navigation
[MoE] Unify DeepGEMM MegaMoE experts behind one feature-matrix layer; add NVFP4 - #61538
Conversation
…matrix Replace DeepseekV4MegaMoEExperts, KimiK3MegaMoEExperts and the DeepGemmMegaMoEBackend/DeepGemmSm100MegaMoEBackend pair with a single DeepGemmMegaMoEExperts layer. Routed precisions are rows of a declarative feature matrix (MegaMoEWeightFormat): MXFP4 (fp8xfp4) and MXFP8 (fp8xfp8) on fp8_fp4_mega_moe with MXFP8 dispatch and MXFP8 shared experts, BF16 (bf16xbf16) on bf16_mega_moe with BF16 shared experts. The activation (MegaMoEActivation: swiglu with clamp/alpha/beta, or situ) is fixed at construction and checked against the format. nvfp4_mega_moe is not wired. Weights keep the vllm-project#60096 procedure: parameters are created in the kernel layout and each checkpoint shard is written straight into it. Whether the shared expert is fused is decided once at construction from the shared MLP's dtype, scales and shape; forward() takes hidden_states, topk_weights, topk_ids and an optional unfused shared expert. Move the input-staging kernel to fused_moe/prepare_megamoe.py (avoids an import cycle through vllm.models.deepseek_v4) and add a BF16 passthrough. Move the layer-level tests to tests/kernels/moe/test_deep_gemm_mega_moe.py and add numerics against a dequantized reference for every format, activation and shared-expert fusion. AI assistance was used for this change. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Yongye Zhu <zyy1102000@gmail.com>
…egaMoE Wire DeepGEMM's nvfp4_mega_moe into DeepGemmMegaMoEExperts as a new feature-matrix row: NVFP4 routed experts (E2M1 + E4M3 per 16 + FP32 global scale) fusing an MXFP8 or BF16 shared expert, swiglu only. - MegaMoEWeightFormat.shared is now a tuple of fusable shared precisions; the layer picks the one matching the shared MLP at init (shared_spec). - NVFP4 weights load straight into the kernel layout like MX; the global scales are per-expert params (w13_weight_scale_2 is the kernel l1_alpha). - Dispatch uses per-token NVFP4 scales (kNvfp4DynamicToken), so w1/w3 input_scale are unused. The L2 activation scale is the max w2 input_scale over all experts (every rank loads all scale tensors), and l2_alpha is folded in the staging kernel so EPLB moves stay consistent. - New Triton prepare_megamoe_nvfp4_inputs stages NVFP4 x/x_sf/x_scales, the MXFP8 or BF16 shared input and routing in one launch; shared Triton helpers are factored out of the MX staging kernel. - get_block_m() picks the format's BLOCK_M heuristic; warmup uses it. Tests: tests/kernels/moe/test_deep_gemm_mega_moe.py, tests/models/test_deepseek_v4_mega_moe.py and tests/model_executor/test_deep_gemm_warmup.py: 141 passed on GB200. NVFP4 staging is bitwise exact vs DeepGEMM's reference quantizers; NVFP4 kernel numerics match a dequantized reference (calc_diff 1.4e-6..1.6e-4), and dropping x_scales, l1_alpha, l2_alpha, the shared input, or using a wrong BLOCK_M fails the expected cases. AI assistance was used for this change. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Yongye Zhu <zyy1102000@gmail.com>
- Run tests/kernels/moe/test_deep_gemm_mega_moe.py in the B200 DeepSeek V4 Kernels step and trigger it on the moved MegaMoE sources; the SM100 numerics otherwise only run where the kernels are skipped. - Keep each logical expert's w2 input scale and take their max, so a weight reload can lower the NVFP4 L2 activation scale. - Make weight_format a required argument; tighten docstrings. - Tests: parametrize the reference numerics over supported format/activation pairs only, readable ids, reciprocal-multiply x_scales reference, drop duplicated lines. AI assistance was used for this change. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Yongye Zhu <zyy1102000@gmail.com>
|
Checked this against weight reload (layerwise today, and the reload plan in #59502 / #60931). The MXFP4 path DeepSeek V4/V4.1 and Kimi K3 use loads the same way as after #60096: shards are written straight into the kernel layout through the param's loader, and the shared MLP is still dropped in Two small requested fixes on the new NVFP4 row: 1. Skip the
2. Check that w1 and w3 Known issue, out of scope for this PR:
That needs a DeepGEMM change and a pin bump, so it's better as a follow-up. This review/comment was written by an AI agent (Claude Code) on my behalf. |
… weight_scale_2 Reload dry runs call the weight loaders with meta stand-ins. Writing a meta w2_input_scale into the host-side L2 scale state raised and aborted the run, so meta tensors now skip that state. The kernel takes one L1 alpha per expert, so the loader now raises if w1 and w3 weight_scale_2 of the same load differ. It pairs the two shards of a load instead of comparing with the live slot, so a reload that changes the scale does not trip it. AI assistance was used for this change. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Yongye Zhu <zyy1102000@gmail.com>
|
Thanks for checking this against reload. Both requested fixes are in 603db01. 1. Meta tensors. 2. w1/w3 CUDA-graph L2 scale. Agreed, this is left for a follow-up together with the DeepGEMM change and pin bump. The PR description notes it. This reply was written by an AI agent (Claude Code) on behalf of @zyongye. |
|
/ci run |
|
✅ Triggered Buildkite CI #94889 for commit |
|
/ci retry |
|
✅ Queued 6 failed job(s) for retry in Buildkite CI #94889. |
The staging kernels only feed the SM100 MegaMoE launch, but the tests ran in the L4 MoE shards after moving to tests/kernels/moe. Below SM90 Triton casts fp32 to fp8 through fp16 (cvt.rz.f16.f32), which double-rounds the NVFP4 E4M3 block scales and broke the bit-exact comparison. The B200 DeepSeek V4 Kernels step still runs them. AI assistance was used for this change. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: Yongye Zhu <zyy1102000@gmail.com>
|
/ci run |
|
✅ Triggered Buildkite CI #94976 for commit ℹ️ This PR is 26 commits behind upstream |
|
/ci retry |
|
✅ Queued 1 failed job(s) for retry in Buildkite CI #94976. |
|
/ci retry |
|
✅ Queued 3 failed job(s) for retry in Buildkite CI #94976. |
vllm-project#61538 moved prepare_megamoe from vllm.models.deepseek_v4.nvidia.ops to vllm.model_executor.layers.fused_moe. step5-preview picked up that move in its 2026-10-16 main merge, so Step5MegaMoE raises ModuleNotFoundError on its first forward and engine startup fails under --moe-backend deep_gemm_mega_moe. The function signature is unchanged. Signed-off-by: Dakai An <dakaian108@gmail.com>
… add NVFP4 (vllm-project#61538) Signed-off-by: Yongye Zhu <zyy1102000@gmail.com> Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-authored-by: Kevin H. Luu <khluu000@gmail.com>
Port the GLM-5.3-Flash FP8 MegaMoE onto the unified DeepGEMM MegaMoE experts from vllm-project#61538 / vllm-project#60096, which replaced DeepseekV4MegaMoEExperts and the post-load MegaMoE backend transform: - Glm5NextMegaMoEExperts now subclasses DeepGemmMegaMoEExperts with the MEGA_MOE_MXFP8 (fp8xfp8) format. E4M3 shards load straight into the kernel layout; the float32 128x128 block scales are staged and every freshly loaded shard is requantized in place to UE8M0 1x32 scales after load_weights (requant_block_fp8_to_ue8m0 moves to the GLM adapter). - The block-FP8 shared expert is fused the same way: the experts own its weights and the separate shared MLP is dropped. - The SwiGLU clamp moves to MegaMoEActivation; the deep_gemm_mega_moe.py and DeepSeek V4 conflicts take main's version. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Overview
Folds the three DeepGEMM MegaMoE expert implementations into one
DeepGemmMegaMoEExpertslayer. The three are DeepSeek V4/V4.1'sDeepseekV4MegaMoEExperts, Kimi K3'sKimiK3MegaMoEExperts, and theDeepGemmMegaMoEBackendpair under both. The new layer is driven by a feature matrix: routed precision × activation × fusable shared-expert precision. The PR also adds NVFP4 routed experts (DeepGEMMnvfp4_mega_moe) as a new row. It keeps the procedure from #60096: weights load straight into the DeepGEMM layout, with no post-load transform.Draft: the end-to-end eval has not been run yet (see Validation).
Claims
One layer. Every DeepGEMM MegaMoE caller uses
forward(hidden_states, topk_weights, topk_ids, shared_experts=None).Fusion decided at construction. Fusion of the shared expert is decided once, from the shared MLP's weights, and never changes at runtime.
Supported matrix. It covers every routed precision and activation that the pinned DeepGEMM exposes:
fp8_fp4_mega_moe(fp8xfp4)fp8_fp4_mega_moe(fp8xfp8)nvfp4_mega_moebf16_mega_moeswiglutakes optionalclamp,alphaandbeta.situ(Kimi K3) takesalphaandbeta.Existing models unchanged. DeepSeek V4/V4.1 and Kimi K3 still build the MXFP4 row and pass the same kernel arguments as before. The mxfp8, nvfp4 and bf16 rows have no model caller yet.
Size. Code under
vllm/is +360 lines net. The refactor commit is −120, the NVFP4 commit is +472, and the two follow-up commits are −15 and +23.Validation
GB200 (SM100), vendored DeepGEMM at the current pin:
.venv/bin/python -m pytest tests/kernels/moe/test_deep_gemm_mega_moe.py \ tests/models/test_deepseek_v4_mega_moe.py \ tests/model_executor/test_deep_gemm_warmup.py # 138 passed in 67sKernel numerics. The real kernel is compared against an fp32 reference that runs on the same quantized weights and activations. The reference covers dispatch quantization, the activation, the L2 activation quantization and the shared expert. The metric is DeepGEMM's
calc_diff; the test requires < 1e-3 and cosine > 0.99. The table shows the worst case over the activations each format supports:Negative controls. Each of these deliberate breaks made the expected cases fail:
x_scales,l1_alphaorl2_alphaBLOCK_MInput staging. The Triton staging kernels match DeepGEMM's reference quantizers (
per_token_cast_to_fp8,per_token_cast_to_nvfp4) bit for bit. This covers values, scales, shared-expert inputs, unrouted padding and the zero-token case.Not yet done:
--enable-expert-parallel,--kernel-config '{"moe_backend":"deep_gemm_mega_moe"}'. The main baseline is 92.65% flexible / 92.57% strict.moe_backend=deep_gemm_mega_moe.Details
Feature matrix.
MegaMoEWeightFormatrow declares five things: the MMA type, the kernel entry point, the routed weight spec (dtype, packing, scale group, scale dtype), the dispatch quant key, and the fusable shared-expert specs.MegaMoEActivationagainst the row at construction.Shared-expert fusion.
forwardaddsshared_experts(hidden_states)once.Input staging.
prepare_megamoe.pymoved fromvllm/models/deepseek_v4/nvidia/ops/tofused_moe/. The old location caused an import cycle throughvllm.models.deepseek_v4.NVFP4.
w1/w3input_scaleare therefore unused. A model that wires NVFP4 must skip them.w2input_scaleacross all experts. Every rank reads every expert's scale tensors during loading, so all ranks agree and EPLB expert moves stay valid.l2_alpha(the per-expertw2global scale × the L2 activation scale) is computed inside the staging kernel. This avoids an extra launch and stays correct after EPLB moves or a weight reload.l1_alphaper expert, so the loader raises ifw1andw3weight_scale_2differ (relative tolerance 1e-5, as in ModelOpt'sallclosecheck). It compares the two shards of the same load, not the live slot, so a reload that changes the scale does not trip it. ModelOpt's and Quark's NVFP4 MoE only warn here; they also use one scale.w2input scales needs a graph recapture. In eager mode the layer recomputes the max on reload. Passing it as a device tensor needs a DeepGEMM change and a pin bump, so it is left for a follow-up.Behaviour changes for existing models:
get_deep_gemm_mega_moe_backend()and the backend classes are removed. Their docstring named them as the override point for plugins and SM90. The extension point is now a newMegaMoEWeightFormatrow, pluscheck_deep_gemm_mega_moe_supported.CI. The new test file and the moved sources are added to the B200 "DeepSeek V4 Kernels" step. Before this PR the SM100 numerics ran only from
tests/models/test_deepseek_v4_mega_moe.py. The staging-kernel tests are gated to SM100, where those kernels run. The L4 "MoE Kernels" shards also collecttests/kernels/moe/, and on SM89 Triton casts fp32 to fp8 through fp16 (cvt.rz.f16.f32). That rounds the NVFP4 block scales twice, so the bit-exact test failed there. The loader and fusion-selection tests in the file still run on L4.Unchanged: the XPU
DeepseekV4MegaMoEExperts, which uses a different kernel.Related PRs:
nvfp4_mega_moe.Pull Request Checklist
I used vLLM's
/pr-checklistskill. (Mandatory for agents, optional for humans).AI assistance was used during the creation of this PR.
Design Fit: Minimizes impact on core components, reuses existing functionality, and justifies added complexity.
Testing and Validation: Validates the change and ensures any added tests are meaningful and reliable, with CI coverage or documented CI resource constraints and validation performed outside CI.
Code Quality and Style: Keeps code and comments clear and concise, and updates relevant documentation and examples.
Pull Request Contents: Includes a brief summary and relevant links, supports claims with evidence, explains root causes and implementation trade-offs, and follows the contributing guide.
AI assistance (Claude Code) was used for this change. The submitter reviews every changed line and runs the tests before marking it ready.
🤖 Generated with Claude Code