[Fix] moe: Count routed_scaling_factor once when top-k folds it - #44901
Conversation
|
Adding an A/B for the Waterfill item (#3 in the description) that the PR lists as owed: "DeepEP with Setup. 8x H20-3e, DeepSeek-V3 (FP8 checkpoint), Two metrics: GSM8K (
"fixed" = Observations:
I'll also add a GPU regression test for Waterfill ( |
|
Follow-up: the Waterfill regression test mentioned above is #45088 (test only, depends on this PR; on current |
Some quant methods and runners fold routed_scaling_factor into the top-k weights (FusedMoE.should_fuse_routed_scaling_factor_in_topk: ModelOpt NVFP4 on non-Marlin runners, FP8 on CUTLASS or TRT-LLM routed, unquantized TRT-LLM routed), and aiter's grouped router folds it on its own. Several steps after top-k decided separately whether that had happened, and some got it wrong: - DeepseekV2MoE.op_output, the two-batch-overlap epilogue, checked only aiter, so it scaled a folded routed output a second time. - remap_topk_for_per_rank_shared_slots gave the fused shared expert 1/routed_scaling_factor unless on aiter, so with a folding quant method the shared expert counted 1/rsf instead of once. - WaterfillBalancer used 1/routed_scaling_factor always, wrong on aiter and with a folding quant method. - The forward_deepep of glm4_moe, glm4_moe_lite and dots3 checked only the quant method's fold flag, not aiter, while their own forward_normal and the shared-slot weight treat aiter as folded. The opt-in unified grouped router (SGLANG_OPT_USE_JIT_KERNEL_GROUPED_TOPK) on aiter folded the factor only when given shared slots, which select_experts no longer passes on aiter, so the factor was lost there. topk.is_routed_scaling_factor_fused_in_topk now answers the question once (the fold flag, or aiter), and fused_shared_expert_weight derives the shared slot's weight from it. DeepseekV2MoE stores the answer at construction; forward_deepep and op_output share one epilogue that reads it, and MegaMoE reads it too. The per-rank remap, the aiter fused append, Waterfill, the unified router and the GLM and dots3 epilogues use the same helpers. The DeepSeek-V4.1 vision routing no longer needs its own shared-weight override on MegaMoE.
The first change answered "are the routed top-k weights already scaled by routed_scaling_factor?" with "the quant method's fold flag, or aiter". The fold flag covers the quant methods and runners that fold the factor in top-k (ModelOpt NVFP4 on non-Marlin runners, FP8 on CUTLASS or TRT-LLM routed, unquantized TRT-LLM routed, and the MXFP4 FlashInfer TRT-LLM, FlashInfer CUTLASS and Marlin methods that declare it). But on aiter only two routes fold the factor whatever the flag says: grouped routing with a correction bias (aiter_biased_grouped_topk, or the unified Triton router) and ungrouped sqrtsoftplus routing (aiter topk_gating or rocm_router_gate). Grouped routing without a bias, the other ungrouped routes and HashTopK do not, so on aiter they lost the factor wherever a later step trusted the answer. - is_routed_scaling_factor_fused_in_topk now takes the TopKConfig and answers for the route it describes; fused_shared_expert_weight takes that answer. - DeepseekV2MoE reads the answer from the router it built. HashTopK layers fold only for their flag, which fixes DeepSeek-V4 hash layers on aiter. forward_normal and forward_normal_dual_stream now multiply on aiter when the route did not fold, instead of whenever aiter is on. - HashTopK passes its fold flag to the per-rank shared-slot remap, so its shared weight comes from the same answer. - bailing_moe_v3's all-to-all epilogue uses the helper like the GLM and dots3 ones; the unified router now folds on aiter, so its old check would have scaled that path twice. - The DeepSeek-V4 TopK keeps folding on aiter, with an accurate comment.
cdf052b to
8f95be3
Compare
…project#44901) cherry-pick -x 3a059de. PORT ORDER: landed FIRST in this family because its merged mainline chronology makes it the strict precondition of sgl-project#44904 (main merges: sgl-project#44901 16:54 -> sgl-project#44904 16:56 -> sgl-project#44912 16:57 -> sgl-project#44913 16:59; sgl-project#44904's diff preimage literally consumes fused_shared_expert_weight / is_routed_scaling_factor_fused_in_topk introduced here, and sgl-project#44913's preimage deletes the helpers sgl-project#44912 introduces -- the only cherry-pickable order is the mainline one; later commits in this family build on earlier ones). Conflict resolutions (2): * models/deepseek_v2.py: the pick's hunk anchors on `class MergedMoEOutput(MoEOutput)` as CONTEXT, but that class is upstream sgl-project#44657 content this fork never took (review-record skip family), so the region conflicted empty-vs-context. Kept ONLY sgl-project#44901's own addition: _scale_routed_and_add_shared (verified byte-identical to the upstream post-image function text) plus all nine routed_scaling_factor_fused_in_topk sites (two __init__ sets, the aiter shared-scaling compensation block, two non-CUDA post-MoE-multiply guards, and the forward_deepep/state-based forward replacements). Nothing in this tree references MergedMoEOutput; did not smuggle it in. * test/registered/unit/layers/moe/test_mega_moe_decode_capacity.py: modify/delete conflict (the file was created by sgl-project#44657, absent here; sgl-project#44901 merely touched 1 line inside it). Kept it deleted. Fidelity: 7/12 python files are BYTE-IDENTICAL to the upstream post-image (topk.py, hash_topk.py, waterfill.py, moe_ep_setup.py, glm4_moe.py, glm4_moe_lite.py, vl_routing.py). mega_moe/mega_moe_flydsl/bailing_moe_v3/ dots3 landed inside files carrying nemotron-era (sgl-project#44518) upstream drift we do not have; their fold changes were diff-reviewed line by line (old `should_fuse_routed_scaling_factor_in_topk or _use_aiter` predicates replaced by the new config-level predicate; residual old-form reads match upstream's own post-image -- legitimately kept). WaterfillBalancer's routed_scaling_factor kwarg became shared_weight (one production constructor, moe_ep_setup, updated by the pick; no other call sites in the tree). Battery (CVD=9) vs base 0df18e9: test_fused_shared_expert_scaling.py 6P -> 12P/25sub (upstream's +343-line fold matrix, green); test_hash_topk.py 3P -> 4P; test_jit_grouped_topk 10S -> 12S (CUDA-gated adds); every pin held (standard_dispatcher_runner_backend 12P, smallm 28P/66sub, b12x 67P/46sub, kernels_namespace 76P, model_overrides 111P/1S/179sub, modelopt suites exact). Pre-existing classes unchanged (trtllm-gen 8F, fused_gate 46F, round-robin 37F, triton-fused-moe 96F, mem_pool_ep 1F, mega_moe_deepgemm 1F). ruff --select=F401,F821,F823,UP037 + ruff format clean on all 18 files. Fold-predicate e2e on real NVFP4 weights stays GPU-window debt (the standalone CPU contract probe follows this family as its own commit). (cherry picked from commit 3a059de) Co-authored-by: port-moe-contracts subagent (cherry picked from commit c4511d515077ea018200eeccfff256cc25ff18e5)
…ts only (sgl-project#44904) cherry-pick -x 48ea486. Landed second of the renorm pair: mainline makes sgl-project#44904 build ON TOP of sgl-project#44901 (its diff preimage contains fused_shared_ expert_weight/is_routed_scaling_factor_fused_in_topk), so the family follows the mainline order (see sgl-project#44901's note here). Wrong-output class: before this, the fused per-rank shared-slot paths asked the gate for top_k while num_fused_shared_experts markers also went through, renormalizing over one routed expert too many (routed sum 0.85-0.97 on CUDA). Conflict resolution (1): test/registered/kernels/ops/moe/test_moe_fused_gate.py carries pre-sgl-project#44690 docstring drift here (this fork never took sgl-project#44518's nemotron rework of that file's header); took the upstream post-image's new one-line docstring, kept this fork's file otherwise. The _reference_gate renorm-order fix, the renorm x apply_scale parametrization, and the production-impl comparison changes all landed. Fidelity: topk.py, hash_topk.py, state_capturer/routed_experts.py, test_fused_append_remap_per_rank_shared_slots.py and test_fused_shared_expert_scaling.py are BYTE-IDENTICAL to the upstream post-image; _router_triton_kernel (kernels/ops/moe/moe_fused_gate.py) verified byte-identical function body against upstream (the file's OTHER regions carry this fork's jit-loader drift and were not touched); fused_moe_triton_kernels.py hunks are docstring-only. Battery (CVD=9) vs sgl-project#44901 commit: test_fused_shared_expert_scaling 12P -> 13P/25sub; waterfill_eplb 4P -> 5P; scattered_a2a 3P -> 5P/2sub (the new routed-width capture case); hash_topk 4P held; append 6S -> 9S, padded 10S -> 11S (CUDA-gated adds); moe_fused_gate 46F -> 52F = the file's pre-existing no-CUDA class +6 new parametrized members (collects clean; needs hardware); kernels_namespace 76P (inventory guard, nothing added/removed); every other pin exact. ruff + format clean on all 11 files. Remap-on-CUDA numerics (torch.cat width change + padded-row remask) remain GPU-window debt, pinned at contract level by the fold-predicate probe commit that follows. (cherry picked from commit 48ea486) Co-authored-by: port-moe-contracts subagent (cherry picked from commit 0432a8fce0b4919b4a85fc6d3e724c6de30189be)
…lass NVFP4 route Standalone CPU contract test pinning WHAT WE NOW BELIEVE after the two fold fixes (sgl-project#44901 + sgl-project#44904) land on our production FP4 arm -- ModelOptNvFp4FusedMoEMethod on a FLASHINFER_CUTLASS runner, the backend auto resolves to on SM110/SM120 (and the value entry 230's fork patch publishes for it): * FusedMoE._fuses_routed_scaling_factor_in_topk answers True for NVFP4 on every non-Marlin runner (and False on Marlin) -- the predicate list sgl-project#44901's shared-slot fix keys off; no NVFP4 class-level fuse_routed_scaling_factor_in_topk flag exists, so the verdict comes from the runner arm. * topk.is_routed_scaling_factor_fused_in_topk (sgl-project#44901) agrees via apply_routed_scaling_factor_on_output. * topk.fused_shared_expert_weight (sgl-project#44901) therefore writes the fused shared column 1.0 -- not 1/routed_scaling_factor -- exactly where sgl-project#44904 moved the kernel/append fill to AFTER routed renormalization. 8 passed here (import/config level only, register_cpu_ci base-a-test-cpu). Red proof of newness: is_routed_scaling_factor_fused_in_topk and fused_shared_expert_weight do not exist at the port base (0df18e9), so the import alone fails pre-pick -- this file cannot pass without sgl-project#44901/sgl-project#44904 landed. GPU-WINDOW DEBT, explicitly NOT covered here: the end-to-end double-scale numeric check (generate against a single-scale reference on 4xSM120 with the real checkpoint; sgl-project#44901's routed-sum 0.85-0.97 symptom and the shared-contribution 1.0x property only OBSERVABLE on hardware). Test-side of the same property: kernels/ops/moe/test_moe_fused_gate.py's new renorm x apply_scale parametrizations are CUDA-gated (52F here, the file's pre-existing device class at base was 46F -- same class, +6 new). Co-authored-by: port-moe-contracts subagent (cherry picked from commit 22ae8e7e609fce945cdc85d3f985a253cfba1aa2)
…gl-project#44912) cherry-pick -x 964ab2b. Applies CLEANLY (zero conflicts): the fork never edited topk.py/hash_topk.py since merge sgl-project#4, and sgl-project#44901/sgl-project#44904's post-images (landed in the two preceding commits) exactly reconstruct upstream's preimage at the touched lines. Semantics: TopKConfig.num_fused_shared_experts (the count of fused shared experts) splits into num_shared_topk_slots (the trailing columns the ROUTER writes -- zero when Waterfill appends the shared expert after routing), and TopK.__init__ now derives top_k for the config as routed-per-token + slots instead of mutating the caller's numbers. New helpers waterfill_ routes_shared_experts / shared_topk_slots live in moe/utils.py; the enable_waterfill branch stops silently redefining top_k (waterfill now forces STANDARD output format explicitly). Entry-247 interaction preserved: b12x's _keeps_global_expert_ids and the slot-count vocabulary this family touched before were not in sgl-project#44912's hunk regions; moe/utils.py post-image differs from upstream ONLY by the 7 b12x enum/predicate lines already in the tree (git diff vs 964ab2b: +7/-0), so the split landed upstream-identical and test_b12x_w4a16_route stayed 67P/46sub unchanged. test_manual NPU file (test_npu_topk.py) took the 1-line rename too (hardware_ backend/npu/moe/topk.py likewise); NPU paths are inert here. Battery (CVD=9) vs sgl-project#44904: waterfill_eplb 5P -> 6P/2sub (upstream's new waterfill slot case); everything else byte-stable (scaling 13P/25sub, hash 4P, append 9S, padded 11S, jit 12S, smallm 28P/66sub, dispatcher 12P, b12x 67P/46sub, kernels_namespace 76P, trtllm-gen 8F class). ruff + format clean. NUM_SHARED_TOPK_SLOT wiring at runtime (per-rank append widths on real dispatch) stays GPU-window debt, same as the renorm pair above. (cherry picked from commit 964ab2b) Co-authored-by: port-moe-contracts subagent (cherry picked from commit ad00ea48739c10c0345f572bed8d8aad818c0169)
…ies 28,165,166,186,194,206,296,297,299,300
PER-MARKER TRUTH (census: B5 markers in this set now ZERO):
kernels/aot topk.cu (28): VERIFIED-ABSORBED -- upstream's in-range
legacy_radix_topk::select rewrite gates the threshold-bin population
against the stash BEFORE staging (coarse_population >= StashEntries ->
full-row byte-per-pass refinement), structurally fixing the overflow
the fork patched after-the-fact; jit twin keeps its s_prefix_bins.
kernels/ops/attention/utils.py (194): VERIFIED-ABSORBED -- the function
re-sited to ops/attention/kv_indices.py (spec_kv_index_token_blocks with
the 32768 gate) and every consumer imports it at that module; the saved
utils re-export would be a dead hunk (zero importers).
elementwise/elementwise.py (166): VERIFIED-PRESENT -- the batch-invariant
num_warps clamp rides upstream's guard_or_false refactor ('or
is_batch_invariant_mode_enabled()' intact).
kvcache/__init__.py (186): VERIFIED-PRESENT -- the store_cache_fp8
registry entry is the merge commit's own graft (entry 310), imports +
capabilities in place.
moe/hash_topk.py + moe/topk.py (296/297/299/300): the merged upstream
canonicals of sgl-project#44901/sgl-project#44904/sgl-project#44912/sgl-project#44913 are IN RANGE (all four
MERGED commits proven ancestors of 2a6c204); every fork symbol
(fused_shared_expert_weight, is_routed_scaling_factor_fused_in_topk,
num_shared_topk_slots x54, has_per_rank_fused_shared_slots,
SharedExpertsConfig wiring) verified present. Markers stale.
moe/flashinfer_trtllm.py (165): SUPERSEDED-BY-EQUIVALENT -- upstream's
own in-range rewrite of the deferred block covers the routed triple
(do_finalize=not defer + _make_deferred_finalize_output) fail-LOUDER
(valid_deferred_input raise); the fork's sgl-project#6077 ignored-autotune-buffer
targeted the OLD block shape and is deliberately NOT restored (GPU
perf-only, defer+routed is not our production arm).
moe/mega_moe.py (296): deep_gemm transform_sf redirect RE-PLANTED to the
guarded wrapper (import-safety contract; sibling fix in dsv4 below).
fused_moe_triton/layer.py (165/206/239): _built_runner_backend hunk
ABSORBED by sgl-project#44993 (config carries the built runner); the entry-206
supports_routed_deferred_finalize property RE-PLANTED (upstream's
packed_nvfp4_routed_deferred covers TRTLLM only, NOT trtllm_routed --
our runner arm), with the sgl-project#44993 2-arg fold predicate; the debug-log OR
re-plant paired.
models/deepseek_v2.py (296/297/300/236): fold chain verified; the
entry-206 format-ternary consumer arm RE-PLANTED (BYPASSED ->
supports_deferred_finalize, else supports_routed_deferred_finalize --
restoring what sgl-project#44993's rewrite dropped).
Battery ACTUALS (CVD=9, prod venv, PYTHONPATH=python, import sglang OK):
fused_shared_expert_scaling + mega_moe_deepgemm_api + moe_runner_
extensions + topk_routing_inputs + model_overrides + kernels_namespace:
GREEN (see B5-4 commit for full 43-file battery actuals)
This PR is part of a stack (oldest at bottom):
Motivation
DeepSeek-style MoE blocks scale the routed experts' output by
routed_scaling_factor(rsf; 2.5 for DeepSeek-V3). Some quant methods and runners fold the factor into the top-k weights instead (FusedMoE.should_fuse_routed_scaling_factor_in_topk: ModelOpt NVFP4 on non-Marlin runners, FP8 on CUTLASS or FlashInfer TRT-LLM routed, unquantized TRT-LLM routed, and the MXFP4 methods that declare it), and on ROCm two aiter routes fold it on their own: grouped routing with a correction bias (aiter_biased_grouped_topk, or the unified Triton router) and ungrouped sqrtsoftplus routing (aitertopk_gating/rocm_router_gate, DeepSeek-V4's gate). Grouped routing without a bias, the other ungrouped routes andHashTopKdo not. Once the factor is in the top-k weights, no later step may apply it again, and a fused shared expert's weight must not compensate for a multiply that never happens.Each step after top-k decided this for itself, and six of them got it wrong (static reading at
main):DeepseekV2MoE.op_output, the TBO epilogue, skipped the multiply only on aiter, whileforward_deepepalso skips it when the quant method folds the factor. With--enable-two-batch-overlap, an all-to-all backend and a folding quant method (for example ModelOpt NVFP4 with--moe-a2a-backend deepep --moe-runner-backend flashinfer_cutedsl; nothing in server-arg resolution rejects it), every MoE layer's routed output was scaled by rsf², 6.25 instead of 2.5 for DeepSeek-V3.remap_topk_for_per_rank_shared_slotsgave the fused shared expert a weight of 1/rsf unless on aiter (its comment noted the gap). On the per-rank slot path (DeepEP-class and MegaMoE backends with--enforce-shared-experts-fusion) with a folding quant method nothing scales the output afterwards, so the always-on shared expert counted 0.4x in every MoE layer. DeepSeek-V4.1's vision routing worked around this for MegaMoE in [1/N][DSv41] Enable DP attention with MegaMoE #43228; the other paths did not.WaterfillBalanceralways used 1/rsf, wrong both with a folding quant method and on aiter.--enable-waterfillturns shared-experts fusion back on even for runners that disable it (such asflashinfer_cutedsl), so ModelOpt NVFP4 with DeepEP and Waterfill reaches it.SGLANG_OPT_USE_JIT_KERNEL_GROUPED_TOPK=1on aiter,biased_grouped_topk_gpufolded the factor only when given fused shared slots, but since MiniMax-M3: allow shared-experts fusion on ROCm gfx942 and newer #36576select_expertsgives it none on aiter. The routed weights then lacked the factor while every later step assumed it, leaving the routed output 1/rsf too small.forward_deepepofglm4_moe,glm4_moe_liteand the dots3 model, andbailing_moe_v3's all-to-all scaling, applied the multiply unless the quant method's fold flag was set, also on aiter where their biased grouped routing already folds it (GLM's gates always carry a bias). With a DeepEP-class backend on ROCm (mori, forglm4_moe) the routed output was scaled twice and a fused shared expert by rsf. Forbailing_moe_v3this became true for the unified router too once item 4 is fixed.DeepseekV2MoEtreated every aiter route as folded. DeepSeek-V4's hash layers (HashTopKuses the factor only for the 1/rsf shared weight) and grouped routing without a correction bias (DeepSeek-V2'sgroup_limited_greedy) therefore got the factor zero times on aiter, inforward_normaland the all-to-all epilogues alike.Modifications
topk.pynow owns the answer:is_routed_scaling_factor_fused_in_topk(topk_config): whether the routed weights aTopKwith that config emits already carry the factor: its fold flag, or on aiter one of the two folding routes above. AHashTopKfolds only for its own flag.fused_shared_expert_weight(routed_scaling_factor=..., fused_in_topk=...): the shared slot's weight derived from that answer (1.0 when folded or when there is no factor, else 1/rsf).How each site decides now:
DeepseekV2MoE.forward_deepeprouted_scaling_factor_fused_in_topk, set once in__init__from the router it built (the helper forTopK, the flag forHashTopK)DeepseekV2MoE.forward_normal,forward_normal_dual_streamDeepseekV2MoE.op_output(TBO)forward_deepep(_scale_routed_and_add_shared)_run_mega_routed)TopKfold flagtopk_gatingfolds anyway, the flag covers the other routes)remap_topk_for_per_rank_shared_slotsfused_shared_expert_weightfrom the config's answer;HashTopKnow passes its fold flag in that configfused_shared_expert_weight(still 1.0 there)WaterfillBalancershared_weight, whichprepare_moe_topkcomputes withfused_shared_expert_weightfrom theTopK's answer or theHashTopK's flagbiased_grouped_topk_gpu)aiter_biased_grouped_topkforward_deepepofglm4_moe,glm4_moe_lite, dots3;bailing_moe_v3's all-to-all scalingTopKconfig (same on CUDA)Existing normalized CUDA routes without a folding quant method retain their scaling behavior. The per-token-bias route now retains the configured fold even without normalization and emits a routed-sum-independent shared coefficient. On aiter, routes that do not fold now get the factor once instead of zero times in
DeepseekV2MoE. The global AITER append retains its existing default. DeepSeek's nonfolding global route supplies an inverse-rsf weight, multiplied by its existing EP replication factor, because that model restores rsf after the fused MoE output. One edge changes: with no rsf (Noneor 0) and no fold, the per-rank remap now writes 1.0 instead of leaving whatever the router put in the shared column.The dynamic per-token-bias router now uses the same folding policy as the static biased AITER route. Its torch implementation applies an explicit fold even without normalization and writes the shared coefficient independently of the routed-score sum. The eager/fused remap equivalence test exercises folding and nonfolding configurations explicitly instead of inferring folding from the platform.
Accuracy Tests
Unit tests:
test/registered/unit/layers/moe/test_fused_shared_expert_scaling.py(CPU), extended:prepare_moe_topk, and the per-rank slot's are 1.0 exactly where the route folded: post-MoE multiply, folded by the flag, aiter biased grouped routing, and aiter grouped routing without a bias (1/rsf);DeepseekV2MoE.__init__(built with a stub gate and experts) derivesrouted_scaling_factor_fused_in_topkfrom the experts' fold flag and the route: biased or unbiased grouped routing, DeepSeek-V4's gate, and hash layers;DeepseekV2MoE.op_output) scales the routed output once, with and without a shared output.test/registered/kernels/ops/moe/test_jit_grouped_topk.py(GPU): on aiter the unified router's routed weights carry rsf when given no shared slots; elsewhere they do not unless asked.test/registered/moe/test_hash_topk.py: the per-rank shared slot of aHashTopKkeeps 1/rsf on aiter too.DeepseekV2MoEgained the new attribute:test_mega_moe_deepgemm_api.py,test_mega_moe_decode_capacity.py,test/registered/moe/test_hash_topk.py.Runs on one 8x H200 machine (Python 3.12, torch 2.14.1), each file in its own process, CPU-registered files with the GPUs hidden and CUDA-registered files on one GPU:
86e67f7d93with only the first commit's new tests applied):test_fused_shared_expert_scaling.pyran 9 tests with 4 failures and 1 error, exactly the new cases: the folded per-rank slot (0.4 instead of 1.0), Waterfill when folded and on aiter (0.4 instead of 1.0), and both folded TBO epilogue cases.test_jit_grouped_topk.py: the aiter case failed and the other passed (1 failed, 7 passed, 4 skipped single-group cases that CUDA does not route).86e67f7d93), 91 unit test files for these areas (unit/layer_boundary/,unit/layers/moe/,unit/models/test_deepseek_*.py,unit/models/test_shared_experts_fusion_gates.py,unit/eplb/,unit/batch_overlap/,unit/lora/test_lora_moe_inplace_unit.py,moe/test_topk_*.py) all exited 0: 626 unittest and 186 pytest cases passed, 13 skipped by platform gates.e19a5728f8): the targeted files (the tests above,test_fused_append_remap_per_rank_shared_slots.pyon GPU,test_stage_producers_leave_sums.py,test_moe_output_reduction.py, the GLM and dots tests) all passed.test_fused_shared_expert_scaling.pyfailed exactly the route cases (aiter grouped routing without a bias in the construction test and in the shared-weight table, and an aiter DeepSeek-V4 hash layer; 3 failures), andtest_hash_topk.py's aiter case failed (shared weight 1.0 instead of 0.4).bd4de12cfa): the same 13 targeted files all passed,glm4_moe,glm4_moe_lite, dots3 andbailing_moe_v3import, and the same 91 area files as above all exited 0 (626 unittest and 186 pytest cases passed). The tested tree is identical to the pushed commit's.Owed, not run:
--moe-a2a-backend deepep --moe-runner-backend flashinfer_cutedsl --enable-two-batch-overlap.--moe-a2a-backend megamoe --enforce-shared-experts-fusion, or DeepEP with--enable-waterfill.SGLANG_OPT_USE_JIT_KERNEL_GROUPED_TOPK=1, DeepSeek-V4 (hash layers), and a GLM-4.5-classGlm4MoeForCausalLMwith--moe-a2a-backend mori. No unit test reaches the GLM, dots3 and bailing all-to-all epilogues.Additional regression validation at
6c1b70e909(one H200; torch 2.14.1+cu130, FlashInfer 0.7.2, Triton 3.8.0, sgl-kernel 0.4.10):Speed Tests and Profiling
No kernel changes. The decisions are made once at construction or read from a stored boolean, as before.
Related
DeepseekV2MoE.forward_normalmultiplies the factor on HIP without aiter although the Triton runner's final sum already applies it there ([Fix] [XPU] glm4_moe_lite: Skip model-side routed scaling on XPU (fix GLM-4.7-Flash accuracy) #43891, open, addresses the XPU case of this pattern inglm4_moe_lite); and the TP paths (forward_normal) of dots3 andbailing_moe_v3never multiply, so on aiter a route that does not fold (dots3 without a correction bias, bailing with ungrouped or softmax routing) gets no factor.Checklist
CI fixture follow-up at
cdf052beb7The ROCm 10 MI35x job failed at
faaf4bb4e6becausetest_deepseek_v4_amd_tp4.py::_moe_stubbypassesDeepseekV2MoE.__init__and omittedrouted_scaling_factor_fused_in_topk. The stub now supplies that construction state. The other five test files in that partition passed. This fixes the test fixture; production executable behavior is unchanged.On one H200, the original fixture reproduces the same AttributeError with the AITER epilogue flags forced; after repair, all four normal/dual-stream and shared-TP1 combinations pass through the actual forward methods. This checks the Python fixture/epilogue contract, not native ROCm collectives or graph replay. The complete AMD TP4 file still needs CI validation.
TopKConfig documents its total ordinary output width and the global AITER absolute shared coefficient versus the standard-path multiplier. Eager/fused subtest labels include the folding mode. Duplicate ordinary model-context scopes and a derived routed-count property remain optional follow-ups. Applicable pre-commit hooks pass. No native AMD rerun or model accuracy result is claimed for this follow-up.
Landing cleanup (2026-10-19)
Current head:
8f95be3e63, rebased onto current main.Trimmed repetitive comments and docstrings while retaining scaling and layout contracts. The cleanup preserves the executable AST; configured pre-commit checks passed. Earlier test results above retain their recorded revisions. No new accelerator or full-model accuracy run is claimed for this cleanup/rebase.
The final PR in this stack moves the extended routing matrix and compile-context check to opt-in Extra coverage while keeping the focused regressions on the normal path.
CI States
Latest PR Test (Base): ❌ Run #39344262524
Latest PR Test (Extra): ❌ Run #39344262390
Latest PR Test (AMD ROCm 10): ❌ Run #39344262320