Skip to content

[AMD] [GLM5] Reland: Fuse shared-expert append into aiter grouped-topk - #35113

Open
Jacob0226 wants to merge 8 commits into
sgl-project:mainfrom
Jacob0226:jacob/reland-aiter-topk-fuse-shared-expert
Open

Jacob0226 wants to merge 8 commits into
sgl-project:mainfrom
Jacob0226:jacob/reland-aiter-topk-fuse-shared-expert

Conversation

@Jacob0226

@Jacob0226 Jacob0226 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Relands #31323, which #35105 reverted after it failed base-a-test-cpu (0) on main. That failure is fixed here, along with two latent bugs found while re-testing on MI355X, and the optimization finally gets the test coverage it shipped without.

What the optimization does

On GLM-5.2 with the non-EP aiter grouped-topk route, every decoder layer ran a separate kernel to append the shared expert into the top-k ids/weights. Instead, the shared columns are pre-populated once in a persistent top-k buffer and the aiter kernel writes only the routed columns via row stride, so the per-layer append kernel disappears (prefill and decode).

  • Bit-identical: the shared column carries the same constant the append kernel used to write.
  • Safe fallback: the buffer is sized to the max prefill batch; larger token counts take the plain append path.
  • Auto-enabled when moe_ep_size == 1 and num_fused_shared_experts > 0. Other archs, EP mode and non-applicable routes are untouched.

GSM8K on MI355X TP4 (GLM-5.2-MXFP4): 0.931 → 0.933. Throughput and TPOT move under 1% either way — the gain is one fewer kernel launch per layer, not raw speed.

Fixes on top of #31323

1. Read the sizing config through get_schedule() — the actual revert cause. base-a-test-cpu (0) failed with alias-form process-global config field reads grew: 2 > baseline 0. The ratchet baseline dropped to 0 on Aug 5, after this branch last ran green against a baseline of 12, so the two ServerArgs alias reads only became a violation once merged against current main.

2. biased_grouped_topk_cpu accepts fused_shared_experts_scaling_factor. select_experts passes the kwarg unconditionally, so on CPU + AMX the alias raised TypeError during argument binding even when the value was None. A non-None value is rejected, matching how fused_topk_cpu handles parameters it cannot serve.

New test: TestAiterGroupedTopkSharedFuse

#31323 shipped with nothing exercising the fused path — fused_shared_experts_scaling_factor had no non-default coverage anywhere in the repo, which is why both latent bugs above had to be found by hand. test/registered/moe/test_fused_append_remap_per_rank_shared_slots.py now adds three tests:

  • The buffer must equal the plain append, bit for bit. The same input goes through the pre-populated buffer and through the plain append (forced by patching the token budget to 0), and the ids and weights must compare equal under torch.equal, not merely close. Covered over a decode step and three prefill chunk sizes on the DeepSeek-V3 / GLM-5 routing shape, under both fused_shared_experts_scaling_factor spellings and four routed scaling factors.
  • Expert parallelism must not fuse, since the pre-populated shared ids only hold when the shared experts live on every rank.
  • Reaching the path before the MoE EP group exists must fall back, not raise — a regression test for fix 3.

Validation

On MI355X everything related to this change is green: the new tests (7 passed, 16 subtests) and the ratchet suite.


CI States

Latest PR Test (Base): ✅ Run #35053826506
Latest PR Test (Extra): ❌ Run #35053826394
Latest PR Test (AMD ROCm 10): ❌ Run #35053826475

Relands sgl-project#31323, which sgl-project#35105 reverted after it broke
test_global_config_read_ratchet.py on main and blocked unrelated PRs.

The revert was correct. The alias-form ratchet baseline dropped to 0 on
Aug 5, after this branch's last green CI run had passed against a
baseline of 12, so the two ServerArgs reads only became a violation once
the branch was merged against current main.

Three fixes on top of the original change:

- _get_aiter_topk_fuse_shared_max_tokens reads chunked_prefill_size and
  max_prefill_tokens from get_schedule() instead of aliasing the
  process-global ServerArgs (from sgl-project#35075, which was closed as unmergeable
  against the reverted tree).

- The moe_ep_size read that gates the fused path degrades to the plain
  append path when the MoE EP group is not initialized, instead of
  asserting. Both the check in biased_grouped_topk_gpu and its mirror in
  select_experts go through the same helper so the two stay consistent.

- biased_grouped_topk_cpu accepts fused_shared_experts_scaling_factor and
  rejects a non-None value, matching how fused_topk_cpu handles the
  parameters it cannot serve. select_experts passes the kwarg
  unconditionally, so without it the CPU+AMX alias raises TypeError during
  argument binding even when the value is None.
…tical test

The pre-populated buffer had no test: nothing asserted it returns what the
plain append would have, and nothing exercised the fallbacks, so the
uninitialized-EP-group regression only surfaced by hand on an MI355X.

Compare the two spellings on the DeepSeek-V3 / GLM-5 routing shape across a
decode step and three prefill chunk sizes, each carrying a different routed
scaling factor (unset, 1.0, 1.5, 2.5), under both fused_shared_experts_scaling
spellings. Also pin the two conditions that must drop to the plain append:
expert parallelism, and reaching the path before the MoE EP group exists.
The three blocks each restated the whole scheme, so the bit-identical claim
and the fallback rule appeared three times over. Keep each where it is
load-bearing and drop the narration.

Also invert the append guard: the empty branch existed only to hold a comment
that repeated the one above it.
@Jacob0226
Jacob0226 marked this pull request as ready for review August 17, 2026 11:19
@Jacob0226
Jacob0226 force-pushed the jacob/reland-aiter-topk-fuse-shared-expert branch from 90d6745 to a75d427 Compare August 17, 2026 11:50
@Jacob0226

Copy link
Copy Markdown
Contributor Author

/rerun-failed-ci

@Jacob0226

Copy link
Copy Markdown
Contributor Author
image

@Jacob0226

Copy link
Copy Markdown
Contributor Author

@amd-bot ci-status

@amd-bot

amd-bot commented Aug 20, 2026

Copy link
Copy Markdown

@Jacob0226

CI Status for PR #35113

Merge verdict: From this PR's perspective it looks safe — the changed code (topk.py aiter grouped-topk shared-expert fuse) is exercised by its new unit test, which passed on both NVIDIA (base-b) and AMD (stage-b-1gpu-small), and topk.py appears in zero failing tracebacks. All executed failures are in unrelated code/backends (DeepSeek/Qwen OOM, CPU AMX, NPU, disagg startup, a stale-mock test). However PR CI is incomplete: both Extra pipelines fast-failed at the gate, XPU was cancelled, and AMD ROCm 7.2 large/disagg stages have unrelated red — a human should confirm those aren't a repo merge-gate blocker.

Note

This PR's changed code is covered by PR CI. The added test test/registered/moe/test_fused_append_remap_per_rank_shared_slots.py is registered on CUDA base-b/1-gpu-large and AMD stage-b-test-1-gpu-small-amd. The main NVIDIA run 32087142988 was fully green, and every stage-b-test-1-gpu-small-amd-rocm720 shard passed — so the aiter fuse path was actually exercised and verified on both backends.

Caution

PR CI is incomplete. PR Test Extra (32087142921) and PR Test Extra (AMD) (32087142829) both failed at call-gate / pr-gate, so their downstream suites (9 + 3 jobs) were skipped — not tested. PR Test (XPU) was cancelled. Confirm required vendor pipelines finish (rerun / update branch, or bypass-fastfail sparingly) before merge.

Changed files: python/sglang/srt/layers/moe/topk.py (+133/-18), test/registered/moe/test_fused_append_remap_per_rank_shared_slots.py (+143/-3)

Executed CI failure attribution: AMD: 4 executed failures (0 related) · Others (CPU/NPU): 9 executed failures (0 related). The PR-claimed CPU ratchet fix worked — no alias-form process-global config field error anywhere.

AMD Executed Failures (run 32087142949, ROCm 7.2)

Job Test File Test Function Error Related? Why
stage-c-large-8gpu (1) test/registered/amd/test_deepseek_v32_basic.py test_a_gsm8k HSA_STATUS_ERROR_OUT_OF_RESOURCES (0 MB free) → server exit -6 🟢 DeepSeek-V3.2 OOM/resource crash; topk.py not in trace; fuse path is gated to moe_ep_size==1 + fused shared experts
stage-c-large-8gpu (3) test/registered/ops/test_aiter_allreduce_fusion_amd.py test_dense_tp_fuses AttributeError: 'SimpleNamespace' has no attribute 'moe_ep_size' at communicator.py:828 🟢 Stale test mock vs communicator.py (neither touched by this PR). moe_ep_size overlap is superficial — different code path
stage-c-mi35x-8gpu (0) test/registered/amd/perf/mi35x/test_qwen35_fp8_ar_fusion_mi35x.py setUpClass Server process exited code -9 (OOM) 🟢 Qwen3.5 AR-fusion perf test; not GLM5/topk path
stage-b-disagg-mi35x test/registered/amd/disaggregation/test_disaggregation_basic.py ConnectionRefused 127.0.0.1:11200 (server never came up) 🟢 Disagg server startup/infra; topk.py not in trace

(pr-test-amd-rocm720-finish is the aggregator for the above — collapsed, not a distinct failure.)

Other Executed Failures

Job Test File Test Function Error Related? Why
build-test (xeon, base-b-cpu) test/registered/cpu/test_intel_amx_attention_backend_a.py TestDPAttention.setUpClass NotImplementedError → server exit -9 🟢 Intel AMX CPU attention + DP; unrelated to AMD aiter topk
NPU ×7 (base-b-4/16, base-c-acc-4/16, base-c-perf-2, multimodal-gen-1) various NPU suites NPU backend failures 🟢 Ascend NPU backend; does not run the aiter/CUDA topk path

Details / what to do before merge

  • Nothing to fix in this PR's code. No executed failure touches topk.py; the dedicated fuse test is green on NVIDIA + AMD.
  • Ensure full CI signal: the two Extra pipelines fast-failed at the gate and XPU was cancelled → rerun / update the branch so required downstream stages actually execute before merge.
  • Confirm the AMD ROCm 7.2 stage-c/disagg reds are pre-existing (DeepSeek/Qwen OOM, disagg startup, and the stale-mock test_aiter_allreduce_fusion_amd.py at communicator.py:828) are known main failures, not merge blockers per repo policy — none are caused by this diff.

Generated by amd-bot using Claude Code CLI

Keep the aiter shared-expert fuse buffer after main's radix4 early-return
and fp32-bias aiter path; the two edits occupy the same allocation site.
@Jacob0226

Copy link
Copy Markdown
Contributor Author

/rerun-failed-ci

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants