Repository navigation
[Feat] [NVIDIA] moe: SM90 NVFP4 MegaMoE via FlashInfer - #45613
ouershua2299 wants to merge 2 commits into
Conversation
Preview approval requiredThis pull request comes from a fork, so Mintlify won't build a preview until someone with write access approves commit |
|
(From GPT6) Please enforce a shared prefill graph bucket across all EP ranks for this backend, or temporarily reject this configuration. Regression coverage should include uneven token counts, idle ranks, and buckets crossing the fused/split boundary. |
| try_cached_model, | ||
| ) | ||
|
|
||
| register_cuda_ci(est_time=900, stage="extra-b", runner_config="8-gpu-h200") |
There was a problem hiding this comment.
Move this test to weekly
There was a problem hiding this comment.
Done, moved to the weekly stage (8-gpu-h200) in 232e96a.
| @@ -0,0 +1,63 @@ | |||
| """Unit tests for the NVFP4 checkpoint conversion of SM90 FlashInfer MegaMoE.""" | |||
…4 NVFP4 checkpoints On SM90, --moe-a2a-backend megamoe with the ModelOpt NVFP4 checkpoints (nvidia/DeepSeek-V4-Flash-NVFP4) repacks the expert tensors at load into the input of flashinfer.megamoe.transform_nvfp4_weights_for_mega_moe (Marlin byte order, local scales rebalanced into the decoder range, one global scale per expert) and runs flashinfer.megamoe.nvfp4_mega_moe (W4A8; fused for small batches, split above). Needs flashinfer-ai/flashinfer#6343 and sgl-project#6517; without them the load fails with an explicit error. EPLB is rejected on this path. Adds the SGLANG_FLASHINFER_MEGAMOE_SM90_{PREWARM,KERNEL_FAMILY} env vars, a DeepSeek-V4 cookbook section, a CPU unit test for the conversion, and a label-gated 8x H200 e2e test that skips when FlashInfer lacks flashinfer.megamoe. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…egaMoE FlashInfer MegaMoE needs the same token count on every EP rank, but graphs bake the token count in at capture. Latch the shared-bucket flag while the FI path is captured into a prefill graph, so every DP rank replays one MAX_LEN bucket and batches with an idle rank fall back to eager. Move the 8xH200 e2e test to the weekly stage and drop the CPU unit test, per review. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
88bd55f to
232e96a
Compare
|
@Fridge003 Thanks for the review. We could not reproduce a hang or wrong output from uneven prefill graph replay on DeepSeek-V4-Flash, but the concern is valid against FlashInfer's contract (same token count on every EP rank), so we changed it as you suggested in 232e96a:
Test setup: 8x H200, branch rebased onto main
"pass" means no request timed out (no cross-rank hang) and greedy GSM8K answers stayed within noise (e.g., 150/450 alternating: 13-16/16 before, 14-16/16 after). With |
|
Thanks for keeping the FlashInfer megamoe wiring explicit (source build with #6343/#6517, not PyPI 0.7.2). Our offline DeepInfer Hopper fork stays on DeepEP V2 + humming for stock MXFP4 DeepSeek-V4.1-Flash and will not vendor flashinfer.megamoe. flashinfer#6999 has now merged on the separate moe_ep NVFP4×FP8 surface; we treat that as a watch item too, still without a CUDA drop. Once this PR merges we can re-test on H200 against a humming JSONL, only for a ModelOpt NVFP4 checkpoint if we actually have one. Question: after #6999 landed, should this PR stay on flashinfer.megamoe (#6517), or do you intend to move Hopper NVFP4 MegaMoE over to moe_ep? |
Co-authors: @AichenF @jychen21 @YijiaZhao
Submitted by: xuechengw
Motivation
On Hopper,
--moe-a2a-backend megamoecovers FP8 checkpoints only. FlashInfer flashinfer-ai/flashinfer#6343 and flashinfer-ai/flashinfer#6517 addflashinfer.megamoe, an SM90 MegaMoE kernel for NVFP4 weights with FP8 activations (dispatch, both expert GEMMs, SwiGLU and combine in one persistent kernel, with a split variant for large batches). This PR wires it into the existingmegamoepath for ModelOpt NVFP4 checkpoints, starting withnvidia/DeepSeek-V4-Flash-NVFP4.On 8x H200 and 8x H20, TP8 / attention-DP8 / EP8, it is faster than DeepEP + DeepGEMM FP8 (W8A8) in prefill and decode (H200: 1.34x prefill TTFT, 1.13x decode ITL; H20: 1.27x / 1.42x) and at parity with the SM90 FP8 MegaMoE path while using NVFP4 weights.
Modifications
layers/moe/mega_moe_sm90_fi_nvfp4.py,layers/moe/mega_moe.py,layers/moe/fused_moe_triton/layer.py):transform_nvfp4_weights_for_mega_moeexpects (Marlin nibble order, one global scale per expert, local scales rebalanced into the kernel's range without changing the dequantized values).commit_staged()andnvfp4_mega_moe. Under DP attention every rank launches with the same padded token count, as FlashInfer requires.layers/quantization/modelopt_quant.py): on SM90 with--moe-a2a-backend megamoe, builds the experts through the module above.environ.py,environment_variables.mdx):SGLANG_FLASHINFER_MEGAMOE_SM90_PREWARM,SGLANG_FLASHINFER_MEGAMOE_SM90_KERNEL_FAMILY.layers/dp_attention.py): when the FI path is captured into a prefill graph it latches the shared-bucket flag, so every DP rank replays one MAX_LEN bucket (FlashInfer needs the same token count on every rank) and batches with an idle rank fall back to eager.Local CPU validation on the branch in the CI image without GPUs: the
layers/moe,layers/quantizationandserver_argsunit suites show no new failures against main6cac4dc3fe(the same 17 GPU-dependent cases fail on both); pre-commit passed for all changed files.Test environment: one host, 8x NVIDIA H200 141 GB (NVLink); image
lmsysorg/sglang:nightly-dev-cu13-20261021-c1c287a4with this branch on top of6cac4dc3fe; FlashInfer built frommainda992edas a wheel (pip wheel "flashinfer-python @ git+https://github.com/flashinfer-ai/flashinfer@da992ed...") and installed into the image, with noPYTHONPATHoverlay and no disabled version checks; modelnvidia/DeepSeek-V4-Flash-NVFP4; sgl-eval 0.1.3. Unless noted:--tp 8 --dp 8 --enable-dp-attention --moe-a2a-backend megamoe.Review update (rebased onto main
ca4bedfffa). The prefill-graph change was checked on 8x H200 (imagenightly-dev-cu13-20261025-65633129, FlashInferda992ed) with uneven DP bursts: one long request with 7 idle ranks, mixes crossing the 256-token fused/split boundary, alternating 120/600 and 150/450-token prefills one per rank, and 7 short + 1 long. Default config (eager prefill) and--cuda-graph-backend-prefill breakable, before and after the change: no hang, GSM8K answers unchanged within noise.--cuda-graph-backend-prefill fulldoes not start on DeepSeek-V4 (its attention backend does not capture EXTEND), with or without this PR.Dependency compatibility. With the image's released FlashInfer
0.7.2wheel (noflashinfer.megamoe), selecting this path raises the expected load-time error naming flashinfer-ai/flashinfer#6343 / flashinfer-ai/flashinfer#6517, the new e2e test skips (8 skipped), and the unit suites above behave as on main.Accuracy Tests
Registered tests on the environment above:
GSM8K with the registered
GSM8KMixinsettings (sgl-eval run gsm8k --num-examples 200 --num-threads 128 --no-thinking, sgl-eval 0.1.3), the cookbook command for this PR and the launch commands below for the baselines, 8x H200:nvidia/DeepSeek-V4-Flash-NVFP4deepseek-ai/DeepSeek-V4-Flashsgl-project/DeepSeek-V4-Flash-FP8Across the runs of this PR (cookbook launch and the e2e gate, before and after rebasing) GSM8K ranged 0.965-0.980.
Speed Tests and Profiling
DeepSeek-V4-Flash on 8x H200 and 8x H20-3e, TP8 / attention-DP8 / EP8, decode CUDA graphs on,
sglang.bench_servingrandom dataset with fixed lengths (--random-range-ratio 1), seed 20260811, warmup requests equal to the concurrency, cache flushed between cases, median of 3 runs per point. Ratios are FlashInfer MegaMoE over the baseline, geometric mean over input <= 8k and concurrency <= 256; > 1 means FlashInfer is better.Workloads: decode-only input 256 / output 512, concurrency 8-256 (1-32 tokens per rank); prefill-only output 1, input 1k-8k; unified 1k/256, 2k/512, 4k/128 and 8k/128.
† Humming versions: H200 uses humming-kernels 0.1.18 (latest), H20 uses 0.1.12 (SGLang's current pin). On H200, 0.1.12 faults in prefill (its tile heuristic picks
block_k=256, and the indexed FP8 group-128 GEMM hits a misaligned address) and its group-128 FP8 GEMMs run at about 17 TFLOPS; 0.1.18 fixes both, so the H200 Humming baseline was rerun with it. Each H200 Humming ratio is against this PR measured on the same node in the same session (prefill TTFT within 0.5%, decode ITL within 1.2% of the "This PR" column). The H200 unified workloads were not rerun with 0.1.18. On H20, 0.1.12 uses a tuned tile table that avoids the fault.Per-point results behind the prefill-only and decode-only rows; values in parentheses are the speedup of this PR over that baseline (> 1 is better).
Prefill-only (output 1), median TTFT (ms):
‡ Measured in a separate session; its ratio is against this PR rerun in that session (TTFT within 4% of the "This PR" column).
Decode-only (input 256 / output 512), median ITL (ms):
Launch commands (all four backends)
Common flags for every backend:
FlashInfer SM90 NVFP4 MegaMoE (this PR):
SGLANG_OPT_DEEPGEMM_MEGA_MOE_NUM_MAX_TOKENS_PER_RANK=4096 \ python -m sglang.launch_server --model-path nvidia/DeepSeek-V4-Flash-NVFP4 $COMMON \ --moe-a2a-backend megamoe --max-running-requests 256 --max-total-tokens 131072DeepEP + DeepGEMM FP8 (W8A8; FP4 experts dequantized to FP8 at load):
SGLANG_DSV4_FP4_DEQUANT=1 SGLANG_DEEPEP_NUM_MAX_DISPATCH_TOKENS_PER_RANK=128 \ python -m sglang.launch_server --model-path deepseek-ai/DeepSeek-V4-Flash $COMMON \ --moe-a2a-backend deepep --deepep-mode auto --max-running-requests 256 --max-total-tokens 131072DeepEP + Humming W4A8 (FP8 dispatch, MXFP4 weights):
SM90 FP8 MegaMoE (cookbook FP8 checkpoint):
SGLANG_DSV4_FP4_EXPERTS=0 SGLANG_OPT_DEEPGEMM_MEGA_MOE_NUM_MAX_TOKENS_PER_RANK=4096 \ python -m sglang.launch_server --model-path sgl-project/DeepSeek-V4-Flash-FP8 $COMMON \ --moe-a2a-backend megamoe --max-running-requests 256 --max-total-tokens 131072Benchmark client (one workload; each runs 3 times):
Workloads: decode-only input 256 / output 512, concurrency 8–256 (1–32 tokens per rank); prefill-only output 1,
input 1k–8k; unified 1k/256, 2k/512, 4k/128 and 8k/128.
The tables above were measured before the final rebase with the FP4 experts of
deepseek-ai/DeepSeek-V4-Flash, which give FlashInfer byte-identical weights tonvidia/DeepSeek-V4-Flash-NVFP4. Rerunning five points on this branch with FlashInfermainda992edgives the same numbers (this branch / tables: decode ITL C32 1.004, C256 0.992; prefill 8k input throughput 1.036; unified 1k/256 and 8k/128 output throughput 1.007 / 1.031).The adapter adds one pre-dispatch kernel (about 2 µs per layer); no extra pad, copy or cast kernels run in the decode
graph.
Scope and Limitations
--moe-a2a-backend megamoewith ModelOpt NVFP4 experts. The integration is not DeepSeek-V4-specific: it uses the generic MegaMoE hook, and FlashInfer plans any legal geometry (tuned for DeepSeek-V4-Flash, DeepSeek-V4-Pro and MiMo-V2.5-Pro). It needs a model with a SwiGLU limit (the kernel's clamped SwiGLU) and has been validated only onnvidia/DeepSeek-V4-Flash-NVFP4.Checklist
Review and Merge Process
/tag-and-rerun-ci,/tag-run-ci-label,/rerun-failed-ci🤖 Generated with Claude Code