Implement SM120 DeepSeek V4 flashinfer_mxfp4 moe runner backend + TP2 - #30272
Merged
Merged
Conversation
lucifer1004
requested review from
AniZpZ,
BBuf,
Edwardf0t1,
FlamingoPg,
HaiShaw,
b8zhong and
ch-wan
as code owners
July 6, 2026 14:48
Contributor
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
lucifer1004
requested review from
1am9trash,
DarkSharpness,
Fridge003,
HydraQYH,
Qiaolin-Yu,
YAMY1234,
Ying1123,
celve,
hebiao064,
hubertlu-tw,
ispobock,
kkHuang-amd,
merrymercy,
rainj-me and
yuan-luo
as code owners
July 6, 2026 14:48
b8zhong
reviewed
Jul 7, 2026
b8zhong
reviewed
Jul 7, 2026
b8zhong
reviewed
Jul 7, 2026
Carry the SM120 enablement onto the latest upstream layout and make the D-LLM override test exercise a non-FlashInfer starting backend.
Contributor
Author
|
@b8zhong Resolved |
2 tasks
68 tasks
Fridge003
reviewed
Jul 16, 2026
Collaborator
|
/rerun-failed-ci |
AliceChenyy
added a commit
to AliceChenyy/sglang
that referenced
this pull request
Jul 19, 2026
Brings in sgl-project#30272 (SM120 DSv4 flashinfer_mxfp4 MoE runner + TP2 memfix, previously carried as a local patch) and the sglang.kernels relocation (RFC sgl-project#29630). Conflict notes: - server_args SM120 block: upstream's out-of-box TileLang-indexer defaults kept, but behind is_set guards so a DeepGEMM build with SM120 attention support can opt back into fp8_paged_mqa_logits; the FLASHMLA_SPARSE_PREFILL force-off is dropped (superseded by the arch gate at the backend dispatch site). - deepseek_v4 hc_combine: fused kernel kept, wrapped in upstream's use_symmetric_memory allocation for the NCCL symmetric all-reduce. - flash_mla_sm120 / mhc imports repointed to sglang.kernels.ops. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
AliceChenyy
added a commit
to xutizhou/sglang
that referenced
this pull request
Jul 21, 2026
…gl-project#30272) into deepseek-base-optimization Brings the SM120 DSv4 PR branch (a979b6b, already merged with sgl-project/sglang main 7a03d30 which includes sgl-project#30272) into the EPv2 integration branch. Conflict notes: - deep_gemm.py: this branch carried early cherry-picks of the PR's SM120 fixes (inline TMA-align, pre-dedup swiglu dispatch); replaced by the PR's refined equivalents (_tma_align_packed_ue8m0 helper, _varlen clamp fallback). The EPv2 pre/post permute paths are kept as-is. - configurer.py: kept this branch's DEEPGEMM_BLACKWELL including SM120 (the EPv2/DeepEP fp8-dispatch gates read it); UE8M0 value is identical either way on SM120. - token_dispatcher/base.py + server_args.py: EPV2 and upstream ASCEND_TP enum/choice entries are unioned; the epv2 validation block is kept ahead of upstream's ascend_tp normalization. - metadata.py: fp4-indexer gating combined with the SM120 JIT-metadata gate and M-chunking. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Zhylkaaa
pushed a commit
to Zhylkaaa/sglang
that referenced
this pull request
Jul 29, 2026
jinzhenfan
pushed a commit
to jinzhenfan/sglang
that referenced
this pull request
Jul 29, 2026
efschu
added a commit
to efschu/htsglang
that referenced
this pull request
Aug 1, 2026
…device actually has Boot 11 failed on both architectures of this rig for two different reasons, and neither was a missing capability -- both were a dispatch that named one architecture where it meant "has this kernel". TP0 (5090, sm120) entered the sparse-prefill branch and hit "Sparse Attention Forward Kernel is only supported on SM90a and SM100f". Upstream already fixed exactly this in the merged PR sgl-project#30272 by adding "and not _is_sm120" to the predicate; our base predates it (git log -S on that file is empty, so this is an old base, not a fork regression). Adopted rather than rebuilt, but phrased as flash_mla_sparse_fwd_supported(device) -- the kernel states its own domain in its error text, so the gate says major in (9, 10) and covers Ampere with the same line. The dense branch it falls to is handed the SAME swa_page_indices / swa_topk_lengths the indexer produced: the kernel changes, the attention support does not. Two independent confirmations that this is lossless rather than an approximation: upstream sgl-project#32464 calls the SM120 dense route intended production behaviour, and our own test kit compares both branches against one _pure_torch_dsv4_combined_reference. TP1/TP2 (3080, sm86) died in dsv4/dequant_k_cache.py on "type fp8e4nv not supported in this architecture". The V4 KV layout is fp8-e4m3 BY ARCHITECTURE (448 fp8 nope + 64 bf16 rope + 7 ue8m0 scale bytes in a uint8 pool); --kv-cache-dtype cannot move it, the pool asserts store_dtype == uint8. Triton's NVIDIA backend admits fp8e4nv only from capability 8.9. The briefing named one file. The bug has four call sites, and fixing only the named one would have MOVED the failure rather than removed it -- the two KV *writers* run before any attention: * dequant_k_cache.py (read) -> manual decode * flash_mla_sm120_triton.py -> manual decode; this is the one that actually becomes load-bearing on sm86 once the sparse branch is left * index_buf_accessor.py (write) -> turned out to be a pure byte copy with no arithmetic between load and store, so both ends are viewed as uint8 unconditionally: byte-identical everywhere, no gate needed * quant_k_cache.py (write) -> a real encode. Rather than hand-roll round-to-nearest-even in Triton, the fallback stages the already-clamped float32 and lets torch do the cast, which sgl-project#262 proved bit-exact on sm86. f32 not bf16 staging: bf16 has 8 mantissa bits and would double-round. Third finding, from sgl-project#33194's report and confirmed here: the compile failure is triggered by the kernel ARGUMENT, not the load -- an fp8-typed tensor fails at the def line even if the pointer is never dereferenced. An in-kernel branch alone cannot fix it, so the host passes the uint8 view and a tl.constexpr selects the decode. nope_cache_view returns view and flag together, following sgl-project#192's pairing invariant: a caller cannot take one without the other. Also removed the module-level SGLANG_SM120_FLASHMLA_BACKEND read in flash_mla_sm120.py -- the exact sgl-project#343 anti-pattern, and its default resolves to a flashinfer kernel gated to cc 120/121, so on a mixed group every rank got whichever card imported first. Resolution is per device now; an explicitly set value still applies everywhere (a statement about the launch, not a probe), and is downgraded loudly, not silently, where the kernel cannot exist. TESTS (desk, CUDA_VISIBLE_DEVICES=99, no GPU touched) test_dsv4_fp8_triton_compat_417.py 12 tests / 262 subtests green. Runs the production @triton.jit decode under TRITON_INTERPRET=1 against a pure Python IEEE-754 reference that never touches a torch fp8 dtype (sgl-project#262's rule: a torch reference would share torch's own Ampere bug and pass vacuously), with an e5m2 cross-probe so a comparison of nothing fails. All 256 codes, signed zero at 0x80, both NaN codes, plus the whole paged kernel vs the torch ref. CAN-FAIL: bits & 7 -> bits & 6 gives 128 failed / 10 passed. test_dsv4_arch_dispatch_417.py 16 tests / 28 subtests green, incl. the two cards of this rig disagreeing inside one process. CAN-FAIL: _FLASH_MLA_CUDA_MAJORS -> (8, 9, 10) gives 7 failed / 15 passed. Regression: unit/layers/attention + unit/utils, 44 failed / 362 passed on this branch vs 44 failed / 334 passed at a56f33a (throwaway detached worktree). Failing set diff EMPTY; all 44 pre-existing "No CUDA GPUs are available". The +28 are these tests. ruff 1 finding on the touched files, the identical 1 at the base (F841, pre-existing). codespell, black, isort clean. mypy clean on both new modules. NOT proved at the desk, by construction: that these kernels COMPILE on a real sm86 card. The Triton interpreter never invokes the NVIDIA backend, which is where fp8e4nv is rejected. Gates for the card window are written down in docs/dev/PLAN_417_dsv4_arch_paths.md along with the full per-architecture coverage matrix, the upstream survey, and the open risks -- chief among them that the indexer (dsv4/indexer.py:667, DeepGEMM metadata) is NOT addressed here and may well be where boot 12 stops, and that sgl-project#33194's chain ends at FP4 routed experts, a wall we expect to miss because we serve GGUF.
efschu
added a commit
to efschu/htsglang
that referenced
this pull request
Aug 2, 2026
…device actually has Boot 11 failed on both architectures of this rig for two different reasons, and neither was a missing capability -- both were a dispatch that named one architecture where it meant "has this kernel". TP0 (5090, sm120) entered the sparse-prefill branch and hit "Sparse Attention Forward Kernel is only supported on SM90a and SM100f". Upstream already fixed exactly this in the merged PR sgl-project#30272 by adding "and not _is_sm120" to the predicate; our base predates it (git log -S on that file is empty, so this is an old base, not a fork regression). Adopted rather than rebuilt, but phrased as flash_mla_sparse_fwd_supported(device) -- the kernel states its own domain in its error text, so the gate says major in (9, 10) and covers Ampere with the same line. The dense branch it falls to is handed the SAME swa_page_indices / swa_topk_lengths the indexer produced: the kernel changes, the attention support does not. Two independent confirmations that this is lossless rather than an approximation: upstream sgl-project#32464 calls the SM120 dense route intended production behaviour, and our own test kit compares both branches against one _pure_torch_dsv4_combined_reference. TP1/TP2 (3080, sm86) died in dsv4/dequant_k_cache.py on "type fp8e4nv not supported in this architecture". The V4 KV layout is fp8-e4m3 BY ARCHITECTURE (448 fp8 nope + 64 bf16 rope + 7 ue8m0 scale bytes in a uint8 pool); --kv-cache-dtype cannot move it, the pool asserts store_dtype == uint8. Triton's NVIDIA backend admits fp8e4nv only from capability 8.9. The briefing named one file. The bug has four call sites, and fixing only the named one would have MOVED the failure rather than removed it -- the two KV *writers* run before any attention: * dequant_k_cache.py (read) -> manual decode * flash_mla_sm120_triton.py -> manual decode; this is the one that actually becomes load-bearing on sm86 once the sparse branch is left * index_buf_accessor.py (write) -> turned out to be a pure byte copy with no arithmetic between load and store, so both ends are viewed as uint8 unconditionally: byte-identical everywhere, no gate needed * quant_k_cache.py (write) -> a real encode. Rather than hand-roll round-to-nearest-even in Triton, the fallback stages the already-clamped float32 and lets torch do the cast, which sgl-project#262 proved bit-exact on sm86. f32 not bf16 staging: bf16 has 8 mantissa bits and would double-round. Third finding, from sgl-project#33194's report and confirmed here: the compile failure is triggered by the kernel ARGUMENT, not the load -- an fp8-typed tensor fails at the def line even if the pointer is never dereferenced. An in-kernel branch alone cannot fix it, so the host passes the uint8 view and a tl.constexpr selects the decode. nope_cache_view returns view and flag together, following sgl-project#192's pairing invariant: a caller cannot take one without the other. Also removed the module-level SGLANG_SM120_FLASHMLA_BACKEND read in flash_mla_sm120.py -- the exact sgl-project#343 anti-pattern, and its default resolves to a flashinfer kernel gated to cc 120/121, so on a mixed group every rank got whichever card imported first. Resolution is per device now; an explicitly set value still applies everywhere (a statement about the launch, not a probe), and is downgraded loudly, not silently, where the kernel cannot exist. TESTS (desk, CUDA_VISIBLE_DEVICES=99, no GPU touched) test_dsv4_fp8_triton_compat_417.py 12 tests / 262 subtests green. Runs the production @triton.jit decode under TRITON_INTERPRET=1 against a pure Python IEEE-754 reference that never touches a torch fp8 dtype (sgl-project#262's rule: a torch reference would share torch's own Ampere bug and pass vacuously), with an e5m2 cross-probe so a comparison of nothing fails. All 256 codes, signed zero at 0x80, both NaN codes, plus the whole paged kernel vs the torch ref. CAN-FAIL: bits & 7 -> bits & 6 gives 128 failed / 10 passed. test_dsv4_arch_dispatch_417.py 16 tests / 28 subtests green, incl. the two cards of this rig disagreeing inside one process. CAN-FAIL: _FLASH_MLA_CUDA_MAJORS -> (8, 9, 10) gives 7 failed / 15 passed. Regression: unit/layers/attention + unit/utils, 44 failed / 362 passed on this branch vs 44 failed / 334 passed at a56f33a (throwaway detached worktree). Failing set diff EMPTY; all 44 pre-existing "No CUDA GPUs are available". The +28 are these tests. ruff 1 finding on the touched files, the identical 1 at the base (F841, pre-existing). codespell, black, isort clean. mypy clean on both new modules. NOT proved at the desk, by construction: that these kernels COMPILE on a real sm86 card. The Triton interpreter never invokes the NVIDIA backend, which is where fp8e4nv is rejected. Gates for the card window are written down in docs/dev/PLAN_417_dsv4_arch_paths.md along with the full per-architecture coverage matrix, the upstream survey, and the open risks -- chief among them that the indexer (dsv4/indexer.py:667, DeepGEMM metadata) is NOT addressed here and may well be where boot 12 stops, and that sgl-project#33194's chain ends at FP4 routed experts, a wall we expect to miss because we serve GGUF.
Chronostasys
pushed a commit
to MindLab-Research/sglang
that referenced
this pull request
Aug 24, 2026
jakki-amd
pushed a commit
to jakki-amd/sglang
that referenced
this pull request
Sep 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
DeepSeek-V4-Flash (FP8 checkpoint, MXFP4 experts) could not be served on SM120 (Blackwell desktop/workstation, e.g. RTX PRO 6000). The failures encountered while bringing up TP2/TP4 on 2–4 GPUs were:
-1expert blocks under EP.This PR makes DSv4-Flash serve on SM120 at TP2/EP2 and makes the native FlashInfer W4A8 MoE path the SM120 default.
Modifications
FlashInfer MXFP4 MoE (SM120 default)
--moe-runner-backend autotoflashinfer_mxfp4for DeepSeek V4 on SM120. Explicit backend selections still take precedence.flashinfer_python,flashinfer_cubin, and the Docker JIT-cache version from 0.6.12 to 0.6.14, which supplies the SM120 MXFP8 activation-scaling and sparse-MLA APIs used here.Marlin MXFP4 MoE correctness and memory
marlin_template.h: count and skipexpert_id == -1blocks regardless ofis_ep; the standard dispatcher masks non-local experts even when noexpert_mapis passed.fused_marlin_moe.py: zero-initialize the aliased intermediate cache because skipped rows remain unwritten but are consumed by the weighted sum.marlin_utils_fp4.py: release loader-format scale parameters after Marlin preparation.mxfp4_marlin_moe.py: register loader scales at their checkpoint E8M0 dtype rather than inflating them to FP32.Attention
deepseek_v4_backend.py: disable the unsupported sparse-prefill path on SM120 and route extend batches through FlashInfer sparse MLA.flash_mla_sm120.py: call FlashInfer's dispatching entry. Decode-sized batches use split-K; larger extends use the generic paged-attention kernel without per-token split-K scratch.Indexer
tilelang_kernel.py: makefp8_paged_mqa_logitsstaging buffers two-dimensional so TileLang's GEMM layout lowering can determine the row size.server_args.py: enable the TileLang indexer by default on SM120 while retaining the Torch fallback.indexer.py: chunk the Torch reference query dimension at 1024 tokens to bound fallback memory.Accuracy Tests
is_ep=truereference.n=32,split_kv=8) and prefill (n=2048,split_kv=1): max absolute logit difference2.3e-5, max relative difference3.5e-7.strict-match=0.956027). A 64-sample FlashInfer qualification scored 0.96875, matching the pre-change local Marlin baseline.Speed Tests and Profiling
random-8k1kserving benchmark: 8,000 input / 1,000 output tokens, four requests per concurrency unit, streaming completions through AIPerf, DeepSeek-V4-Flash TP2/EP2 on 2× RTX PRO 6000 (SM120). The baseline is the successful local Marlin Inferlab run from before this FlashInfer work; the final column comes from the final FlashInfer Inferlab run with the same model, topology, hardware, and client workload. Server launch and JIT startup are outside the measurement window.All 252 requests in the final benchmark succeeded. Full GSM8K also completed successfully, and Inferlab verified server cleanup.
Load-end model memory at TP2/EP2 is approximately 75.06 GB/rank, down from 91.99 GB/rank before the E8M0 loader-container fix, leaving roughly 17 GB/rank more KV-pool headroom.
Checklist
prekCI States
Latest PR Test (Base): ⏳ Run #29632757211
Latest PR Test (Extra): 🚫 Run #29632757138