[ROCm][Perf] Kimi-K3 latent-MoE: overlap the shared all-reduce with the routed up-projection - #51437
Draft
mpashkovskii wants to merge 4 commits into
Draft
mpashkovskii wants to merge 4 commits into
mpashkovskii wants to merge 4 commits into
Conversation
Signed-off-by: Matvei Pashkovskii <mpashkov@amd.com>
…d up-proj Add an overlap tail (Tier 1) to the ROCm ROCmLatentMoERunner that all-reduces and RMSNorms the latent, runs the full replicated up-projection GEMM on the default stream while the shared-expert all-reduce runs on the aux stream, and adds the two -- hiding the shared reduce behind the up-proj GEMM for batches under VLLM_SHARED_EXPERTS_STREAM_TOKEN_THRESHOLD. Dispatch by token count between this tail and the existing column-parallel tail (Tier 2), falling back to the overlap tail when the up-projection is not rank-divisible or the routed scale is non-unit. Reuse the base runner's AITER fused AR+RMSNorm via _allreduce_norm_latent_out. The native path and the base runner are unchanged; the fused path is still gated on tp>1, un-reduced combine, a shared expert, no SP, and unit routed scale. Depends on ROCm/aiter#4471 (SiTUv2 in the packed-int4 MoE stage1 epilogue). Relates to vllm-project#50682. Signed-off-by: Matvei Pashkovskii <mpashkov@amd.com>
4 tasks
mpashkovskii
force-pushed
the
perf/kimi-k3-rocm-latent-moe-overlap
branch
from
August 13, 2026 01:41
57c5b09 to
5c430e6
Compare
Contributor
|
This pull request has merge conflicts that must be resolved before it can be |
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.
Purpose
Kimi-K3's latent MoE combines a routed expert output — produced in a compressed
latent space, then up-projected back to hidden dim — with a shared expert output.
Under tensor parallelism both need an all-reduce. Today the ROCm runner
(
ROCmLatentMoERunner) only implements the column-parallel tail(
_shard_up_proj_tail, "Tier 2"), which folds a sharded up-projection into theshared partial before a single reduce. That tail is efficient for prefill-sized
batches but gives up all overlap: the reduce must follow the accumulate.
This PR adds a token-count-dispatched overlap tail so decode-sized batches stop
leaving the shared all-reduce on the critical path:
_overlap_allreduce_tail. All-reduce + RMSNorm the latent, thenrun the full replicated up-projection GEMM on the default stream while the
shared-expert all-reduce runs on the aux stream (
maybe_execute_in_parallel+aux_stream()), and add. For batches underVLLM_SHARED_EXPERTS_STREAM_TOKEN_THRESHOLDthe shared all-reduce is hidden behindthe up-proj GEMM.
_select_tail_tier. Small batches take the overlap tail;larger batches keep the column-parallel tail; when the up-projection rows do not
divide evenly across ranks (or the routed scale is non-unit) the runner falls back
to the overlap tail, which is correct at any shape.
_allreduce_norm_latent_out. Fused AR+RMSNorm for the latent — collapses thelatent all-reduce and its RMSNorm into a single AITER op, with a plain
all_reduce+RMSNormfallback. This helper is backed by a small addition to the base runner(
moe_runner.py): a module-level_aiter_fused_ar_rmsnormbinding (viarocm_aiter_ops.get_fused_allreduce_rmsnorm_op()) plus a_get_zero_residualbuffer helper. The underlying AITER op already exists in
_aiter_ops.py; the baserunner just did not expose it before.
The native (non-fused) path is unchanged: the fused path is still gated on
tp_size > 1, an un-reduced combine output, a shared expert, no sequence parallelism,and unit routed scale — anything else defers to the base
MoERunner.forward. Noexisting base-runner behavior changes; the only base-runner edit is the additive
AR+RMSNorm helper above, covered by a new unit test.
Relates to #50682 (ROCm/AMD Kimi-K3 gap and roadmap tracking → Performance
Optimization). Tracked as PR #51437.
Files changed
vllm/models/kimi_k3/amd/latent_moe_runner.py— the overlap tier, tier selection,and
_allreduce_norm_latent_out.vllm/model_executor/layers/fused_moe/runner/moe_runner.py(+90/−7) — additive_aiter_fused_ar_rmsnormbinding +_get_zero_residualhelper (no behavior changeto existing paths).
tests/models/kimi_k3/test_amd_latent_moe_runner.py— tier-selection unit tests +multi-GPU parity for both tails.
tests/model_executor/test_moe_runner_fused_ar_rmsnorm.py— unit test for the newbase-runner helper.
This feature has no kernel dependency
The overlap and column-parallel tails contain no activation — they are reductions,
an up-projection GEMM, and an add. The
situ/situv2activation lives entirely in thestage-1 expert GEMM (
_forward_entry), which runs before tier selection and isidentical across Tier 1, Tier 2, and the upstream baseline. So this PR does not depend
on any activation-kernel change, and the unit + multi-GPU parity tests — which feed
synthetic routed/shared tensors straight into the tail methods and never invoke the
expert GEMM — pass regardless of the AITER build.
Environment note (not a feature dependency): serving Kimi-K3 int4 on gfx942 at all
requires ROCm/aiter#4471 ("[FlyDSL] Support SiTUv2 in the packed-int4 MoE stage1
epilogue"), because Kimi-K3 uses
hidden_act = situand the current packed-int4 FlyDSLstage1 otherwise raises
NotImplementedError: split-K stage1 activation supports only 'silu', got 'situv2'(or silently computes SiLU). This affects the upstream Tier-2 baseline (#51253)
exactly as much as this PR — it is a prerequisite for reproducing the end-to-end
serving numbers below, not something this feature introduces or relies on. On an AITER
without split-K SiTUv2 support, force the non-split-K stage1 with
AITER_KSPLIT=1.Relationship to #50657
#50657 ("[ROCm][Perf] Fuse and FP8-pack Kimi-K3 latent MoE output tail") targets the
same boundary but with a different mechanism: a dedicated AITER FlyDSL fused-tail
kernel plus optional FP8 packing of the up-projection weight, which depends on
unmerged AITER PRs (ROCm/aiter#4496, #4503). This PR is complementary — it schedules
existing in-tree collectives/GEMMs across two streams and adds an AITER fused AR+RMSNorm
helper to the base runner (backed by an op already present in
_aiter_ops.py, so no newkernel). The two overlap only on the
runner_cls=wiring line inamd/linear.py(already present on
main); if both land, that single line needs reconciling to arunner that can select between the two strategies.
Test Plan
Unit (no GPU) — tier-selection and fused-path gates:
.venv/bin/python -m pytest -q \ tests/models/kimi_k3/test_amd_latent_moe_runner.py \ -k "not tp4 and not tp8"Multi-GPU arithmetic parity — each tail must equal the replicated up-projection:
# 4- and 8-GPU ROCm host .venv/bin/python -m pytest -q tests/models/kimi_k3/test_amd_latent_moe_runner.pyThe multi-GPU suite covers, at TP4 and TP8:
test_overlap_tail_tp{4,8}_matches_replicated_projection— new Tier 1 output equalsshared + up_proj(rms_norm(all_reduce(routed))).test_shard_tail_tp{4,8}_matches_replicated_projection— Tier 2 unchanged.test_shard_tail_tp4_writes_only_its_own_shard— the column-parallel tail writesonly this rank's hidden shard before the final collective.
End-to-end serving parity (8× MI355X,
gfx950,moonshotai/Kimi-K3):Compare the candidate against
main(baseline = Tier 2 only). Exercise both tiers bysweeping batch size around
VLLM_SHARED_EXPERTS_STREAM_TOKEN_THRESHOLD(small decode →overlap tier; large prefill → column-parallel tier) and force the column-parallel path
with
VLLM_DISABLE_SHARED_EXPERTS_STREAM=1. Note: EP must be off for the fusedtail to engage — with expert parallel enabled the MoE combine already reduces the
fused output, so the runner takes the native path and neither tail runs.
Test Result
All numbers below are placeholders — fill from the runs described in the Test Plan.
Environment: 8× MI325X (
gfx942),moonshotai/Kimi-K3,int4_per_group_32, TP=8,EP off. Serving requires an AITER that handles SiTUv2 (ROCm/aiter#4471), or
AITER_KSPLIT=1to force the non-split-K stage1 — an environment prerequisite for K3on gfx942, not specific to this feature.
Correctness
-k "not tp4 and not tp8"): TODO PASS/FAIL (N passed).main(fused tail must not change outputs within tolerance):TODO — e.g. gsm8k / small lm_eval, baseline vs candidate.
Decode latency / throughput — batch below
VLLM_SHARED_EXPERTS_STREAM_TOKEN_THRESHOLDso the overlap tail (Tier 1) is active.Baseline =
main(Tier 2 only); Candidate = this PR.Overlap evidence — trace excerpt showing the shared-expert
all_reducerunning onthe aux stream concurrently with the routed up-projection
mmon the default stream(confirms the reduce is hidden, not merely reordered): TODO — paste kernel timeline.
Essential Elements of an Effective PR Description Checklist
supported_models.mdandexamplesfor a new model.AI assistance was used for this change.