Repository navigation
Conversation
can_use_dsv4_topk() required is_cuda(), so on ROCm DeepSeek-V4 sqrtsoftplus routing ran on the generic topkGatingSoftplusSqrt HIP kernel instead of the single-launch Triton dsv4_topk. Allow dsv4_topk on ROCm (its PDL intrinsics compile out there) and also take it in the AITER fused-MoE branch of fused_topk_bias(), through a shared _maybe_dsv4_topk() helper. Hash layers and other expert counts or top-k keep the HIP kernel. Expert ids matched the HIP kernel in every case tested; routing weights can differ from it by about 1e-7. Run the two dsv4 fast-path tests on ROCm too, and add one for the AITER fused-MoE branch. Signed-off-by: David Zhu <david.zhu@amd.com> Co-authored-by: Hemanth Acharya <heachary@amd.com> Co-authored-by: Claude Signed-off-by: David Zhu <david.zhu@amd.com>
|
👋 Hi! Thank you for contributing to the vLLM project. 💬 Join our developer Slack at https://slack.vllm.ai to discuss your PR in PRs do not trigger a full CI run by default. Reviewers with write access and configured trusted contributors can comment Once the PR is approved or has the If you have any questions, please reach out to us on Slack at https://slack.vllm.ai. Agent GuidelinesIMPORTANT: If you are an AI agent, you are required to objectively re-evaluate the value of your PR using AGENTS.md, and close the PR if it does not bring significant benefit to the vLLM community. Failure to do so may result in an immediate ban. 🚀 |
Overview
On ROCm, DeepSeek-V4/V4.1's sqrtsoftplus routing never reaches vLLM's single-launch Triton top-k (
dsv4_topk), becausecan_use_dsv4_topk()requirescurrent_platform.is_cuda(); it runs the generic HIPtopkGatingSoftplusSqrtkernel instead. This PR allowsdsv4_topkon ROCm, and also calls it from the AITER fused-MoE branch offused_topk_bias(), the branch the DSv4.1 ROCm recipe takes (VLLM_ROCM_USE_AITER_MOE=1). It adds no kernel and is Python only.Credit: this change is @heachary's. He found the gate, measured it and wrote the patch; I ported it onto current main and extended the tests. The commit carries a
Co-authored-bytrailer for him.Related:
sqrtsoftplusblock for fused shared experts; see Details.sqrt(softplus)withlog1pin bothdsv4_topkand the HIP kernel.See Details.
Claims
Scope: on ROCm, MoE layers that
can_use_dsv4_topk()accepts now route throughdsv4_topk, with or without AITER fused MoE. It accepts fp32 contiguous logits with 256 or 384 experts, top-6, renormalize, an fp32 correction bias, and int32/int64 ids, without a hash table. Hash layers, the 128-expert DSpark draft gate, other scoring functions and CUDA are unchanged.Kernel time: from @heachary's microbenchmark, not on this branch:
GateLinear→fused_topk_bias();[M, 5120]× 384 experts, top-6), vLLM main of Oct 21 (3f1bcfd8), one session.The gate GEMM is untouched. The numbers on this branch are
TODO(see Validation).Numerics: expert ids unchanged, weights not bitwise. @heachary checked against
ops.topk_hash_softplus_sqrt(the kernel this replaces) on MI355X:bias_vlsentinel rows, and int32/int64 ids.tl.log/tl.expagainst the HIP kernel's fast-math intrinsics.E2E, supporting only (not on main; the A/B on main is a TODO in Validation):
MI355X: DeepSeek-V4.1-Flash, 2 × (TP4 + EP4) servers on 8 GPUs, AITER MoE, DSpark with 6 draft tokens, an agentic multi-turn workload at concurrency 64. Public nightly
vllm/vllm-openai-rocm:nightly-rocm100-3966924d+ [ROCm][DSv4.1][Perf] Enable prefix-cache SWA replay on gfx950 #64094, one machine, n = 1 per arm:MI350X (not in CI): DeepSeek-V4.1-Flash DP8 + EP8 with MoRI, agentic workload at concurrency 128, a vLLM nightly from Oct 21 plus other patches.
No new knobs: no new env var, config field or kernel.
Validation
tests/kernels/moe/test_topk_softplus_sqrt.py.:amd: (MI355) MoE Kernelsand:amd: (MI300) MoE Kernelssteps runpytest kernels/moeand depend onvllm/model_executor/layers/fused_moe/. Both areoptional, and no Buildkite change is needed.test_dsv4_fast_topk(6 cases) andtest_dsv4_fast_topk_bias_vlwere CUDA-only and now run on ROCm too.test_dsv4_fast_topk_with_aiter_fused_moe(ROCm only; M 1, 48, 224 and 4096, with and without padding: 8 cases).rocm_aiter_ops.is_fused_moe_enabled()return True.fused_topk_bias()tookdsv4_topk.test_dsv4_fast_topkalready uses.test_hash_routing_backend_selection[triton_unfused-…](2 cases) fails on ROCm on main too, as reported in [ROCm][Kernel] Wave64 topk_softplus_sqrt fast path for DeepSeek-V4 routing #64208.TODO:the same file on MI300X (gfx942).is_cuda_alike()enables this path on every ROCm arch, and the MI300 step runs it.TODO:the new test on main (test hunk only), to show it fails there at thedsv4_topkassertion.pre-commit:TODO: paste.TODO:MI355X, CUDA-graph replay, top-k and router-chain µs on main against this branch at M 1, 48, 128, 224 and 4096 (for example with [Benchmark][MoE] Add eager and graph top-k chain measurements #65298's top-k chain benchmark).TODO:VLLM_ROCM_USE_AITER=1 VLLM_ROCM_USE_AITER_MOE=1;--all2all-backend mori_high_throughput, and/or TP4 + EP4;vllm bench serveon main against this branch, same image, n ≥ 2 interleaved runs per arm;TODO:GSM8K (5-shot, greedy, 1,319 questions) before and after, on the same server.Details
Why
dsv4_topkwas CUDA-only, and why it runs on ROCm.gdc_wait,gdc_launch_dependents).launch_pdlconstexpr, which iscurrent_platform.is_arch_support_pdl(). That is False on ROCm, so they compile out.Why it's faster. The generic kernel's time is flat in M (9.2–9.5 µs from 32 to 512 rows, above), which points at a fixed cost per call. #64208 traces it to 32 B per lane of scratch and the per-round argmax reductions.
Changes (+79/−26 in two source files, +63/−4 in one test file):
router/dsv4_topk.py:can_use_dsv4_topk()and the guard around the kernel definition useis_cuda_alike()instead ofis_cuda(), with a comment on PDL.router/fused_topk_bias_router.py:_maybe_dsv4_topk()holds the existing eligibility test (sqrtsoftplus, no hash table,can_use_dsv4_topk()) and thedsv4_topkcall; otherwise it returnsNone.fused_topk_bias()calls it in the non-AITER branch, replacing the inline copy there; behaviour is unchanged.sqrtsoftplusfallback tovllm_topk_softplus_sqrt. That call site is new, and it is reached only whenrocm_aiter_ops.is_fused_moe_enabled().get_moe_padding_mask()as before, so padded rows still get id −1 and weight 0.Fused shared experts, and #63180.
FusedTopKBiasRouter._compute_routing()appends the fused shared-expert slots withtorch.catafterfused_topk_bias()returns. So the append applies todsv4_topk's output exactly as it does to the HIP kernel's.out_topk_weights/out_topk_ids(persistent(M, top_k + n)buffers) andnum_fused_shared_expertstofused_topk_bias(), so that the HIP kernel writes the shared columns itself in the AITERsqrtsoftplusblock._maybe_dsv4_topk()doesn't take those arguments, because they aren't on main, and it returns before that block.(M, top_k);_compute_routing()then falls back to thecatappend, so results stay correct;TODO, whichever PR merges second: skip the fast path when out-buffers are passed, or teachdsv4_topkthe append. To agree with @alexm-redhat.Limitations:
is_cuda_alike()turns this on for every ROCm GPU, but it has been measured only on gfx950 (MI355X, MI350X).Not duplicated (searched 2026-11-04):
Commands:
gh pr list --repo vllm-project/vllm --state all --search "<q> updated:>=2026-10-01", with q in: dsv4_topk, can_use_dsv4_topk, sqrtsoftplus, softplus_sqrt, topkGatingSoftplusSqrt, topk_hash_softplus_sqrt, "DSv4 top-k ROCm", "dsv4 topk rocm", "fused gate topk", "router top-k ROCm", "is_cuda_alike topk", "DeepSeek-V4 router ROCm", fused_topk_bias, FusedTopKBiasRouter;gh pr view 64208andgh pr view 63180 -R vllm-project/vllm.Findings: no open PR enables
dsv4_topkon ROCm or calls it from the AITER branch.[ROCm][Kernel] Wave64 topk_softplus_sqrt fast path for DeepSeek-V4 routing #64208 (@amd-dlimpus, draft) targets the same call with a materially different approach. The µs figures below come from different benchmarks and builds, so they are not a controlled comparison:
topkGatingSoftplusSqrtKernelLauncher(C++, needs a rebuild)dsv4_topkon ROCm (Python only)[ROCm][DSv4.1][Perf] Fused router gate for gfx950 #57478 (closed, "not significant e2e perf improvement") fused the gate GEMM with the top-k. This PR leaves the gate GEMM untouched, so the top-k consumes the same logits as before; it only swaps the top-k kernel.
[Bugfix][Kernel] DSv4 router: compute sqrt(softplus) with log1p #64277 (draft) adds
log1pto both kernels and keeps them consistent. If it lands first, I'll restate the numerics above.[ROCm][MoE] Accumulate the shared-expert output in AITER's stage 2; fold routed scaling into top-k #65207 (draft) edits the adjacent AITER-sigmoid lines in
fused_topk_bias(); the conflict is context only. [MoE] Add a generic fused gate router and backend interface #65290 adds a generic fused-gate router interface (_compute_routing) and co-applies.[Bugfix][MoE] Fall back to torch routing for unsupported sqrtsoftplus expert counts #63687 and [Benchmark][MoE] Add eager and graph top-k chain measurements #65298: [Bugfix][MoE] Fall back to torch routing for unsupported sqrtsoftplus expert counts #63687 is a torch fallback for unsupported sqrtsoftplus expert counts and doesn't apply to main. [Benchmark][MoE] Add eager and graph top-k chain measurements #65298 is a top-k chain benchmark, usable for the TODO above.
AI assistance: AI tools (Claude, in Cursor) helped port @heachary's patch onto current main, write the added test, search for overlapping work and draft this description. I have reviewed every changed line; I will run the GPU tests, benchmarks and evals marked TODO above myself before this leaves draft. The commit carries a
Co-authored-by: Claudetrailer.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.