[ROCm][Perf] Kimi-K3 AMD MLA: fuse the q-a and kv-a RMSNorms - #52080
Draft
mpashkovskii wants to merge 2 commits into
Draft
mpashkovskii wants to merge 2 commits into
mpashkovskii wants to merge 2 commits into
Conversation
The AMD K3 MLA front-end ran two separate RMSNorm launches per token -- q_a_layernorm(q_c) then kv_a_layernorm(kv_c) -- once per MLA layer. Wire the existing portable Triton fused_q_kv_rmsnorm into the AMD path so both norms run in one launch, removing a per-token kernel launch and intermediate write on the prefill hot path. Add an optional q_kv_norm callback to MLAModules: when unset the wrapper keeps its two separate norms (byte-identical); the K3 AMD adapter installs the fused callback on the q-LoRA path, mirroring the vllm-project#50664 output-gate pattern. On the q-LoRA path the wrapper normalizes q_c and kv_c together and rebinds q_c so the q projection and indexer see the normed value. The uncompressed path and every model that leaves q_kv_norm unset are unchanged. Signed-off-by: Matvei Pashkovskii <mpashkov@amd.com>
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
On the Kimi-K3 AMD MLA front-end, every token runs two separate RMSNorm
launches —
q_a_layernorm(q_c)thenkv_a_layernorm(kv_c)— once per MLA layer.This PR collapses them into a single
fused_q_kv_rmsnormcall(
models/common/ops/fused_qk_rmsnorm.py), a portable Triton kernel already in thetree — no CUDA-only gate, and its one PDL branch is
tl.constexpr-guarded oncurrent_platform.is_arch_support_pdl(), which returnsFalseon ROCm — so it runson ROCm as-is with no new kernel.
The front-end norm fires on every token across all MLA layers, so collapsing two
launches into one removes a per-token kernel launch and its intermediate write. This
is a small, low-risk micro-optimization: verified at the kernel level (see Test
Result), throughput-neutral end-to-end on the workloads measured (the norms are a
small, non-critical-path share of GPU time, and the saving is launch overhead that
cudagraph capture already elides on the served decode path). It is offered as a clean
reduction in launch count and intermediate writes with no accuracy risk, not as a
headline speedup.
How it works
MLAModulesgains an optionalq_kv_normcallback (layers/mla.py). WhenNone(every other model/backend), the wrapper keeps its two separate norms —that path is byte-identical to before.
forward, on the q-LoRA path (q_lora_rank is not None),defers
q_a_layernormand, whenq_kv_normis set, normalizesq_candkv_ctogether at the kv-split point. It rebinds
q_cto the normed tensor so both theq-projection and any later indexer see the normalized value — matching the
separate-norm semantics exactly.
KimiMLAAttentioninstalls the callback, wrappingfused_q_kv_rmsnormwith theq_a/kv_alayernorm weights andconfig.rms_norm_eps(both norms share the same eps). Only wired on the q-LoRA path; the uncompressed
path has no q norm and is unchanged.
This mirrors the pattern established by #50664 (optional
MLAModulescallback for theK3 output gate; generic default, AMD installs the fused variant). The uncompressed
path (no q norm) and every other model that leaves
q_kv_normunset are untouched.Relates to #50682 (ROCm/AMD Kimi-K3 gap and roadmap tracking → Performance
Optimization → Mixed Attention / MLA).
Not a duplicate
No AMD adapter or open PR wires
fused_q_kv_rmsnorminto the AMD K3 path (checkedagainst #50682's PR list and open
rocm+k3PRs). The nearby fusions are different:#50664 (MLA output gate), #50637 (AttnRes + RMSNorm), #50634 (KDA decode gate), #41095
(AITER dual-RMSNorm for DeepSeek MLA, not K3). #51772 (chunked-context K/V packing)
does not touch
common/opsor this norm.Files changed
vllm/model_executor/layers/mla.py— optionalq_kv_normfield onMLAModules;wrapper
forwarduses it on the q-LoRA path. No behavior change when unset.vllm/models/kimi_k3/amd/linear.py—KimiMLAAttentionbuilds and passes thefused callback.
tests/models/kimi_k3/test_amd_mla_qkv_norm.py— parity + empty-batch test.Test Plan
Unit — the fused op must equal two independent RMSNorm modules (ROCm GPU):
Covers token counts
{1, 7, 64, 512, 4096}(decode- through prefill-sized) plus anempty batch.
Accuracy — this changes an activation path, so a full eval is required:
End-to-end prefill perf (long input) — baseline
mainvs this PR, same seeds:Test Result
Environment: 8× MI325X (
gfx942),moonshotai/Kimi-K3,int4_per_group_32, TP=8,EP off. This is a small but fair micro-optimization: the kernel-level effect is
real and verified, and end-to-end it is throughput-neutral (within run-to-run noise)
on this prefill-heavy workload — it neither helps nor harms serving numbers, and it
carries no accuracy risk.
Kernel evidence (torch profiler traces) — the fused kernel does exactly what it
should. Each
fused_q_kv_rmsnormlaunch replaces the two separateq_a/kv_aRMSNorm launches, and nothing else in the trace changes:
fused_q_kv_rmsnormlaunchesThe plain-RMSNorm count drops by exactly
768 = 2 × 384— every fused launch removesthe two norms it replaces — for ~1.55 ms less GPU time and 384 fewer kernel launches
across the profiling window. All other kernels are unchanged.
End-to-end serving (
vllm bench serve, 8024/1024, conc 16). Baseline =main(two norms); Candidate = this PR (fused):All deltas are within run-to-run noise (≤0.4%). Expected: the front-end norms are
~4.4% of GPU time and are not on the critical path (the workload is bound by the
projection GEMMs and the SiTU activation), and the saving here is kernel-launch
overhead — which full cudagraph capture already elides on the served decode path. So
the fusion is a clean win in eager mode and at the kernel level, but does not move
aggregate serving throughput at this operating point. It is included as a correct,
low-risk reduction in launch count and intermediate writes, not as a headline speedup.
Correctness
test_amd_mla_qkv_norm.py): TODO PASS/FAIL (N passed— the fused op equalstwo independent RMSNorm modules across token counts
{1, 7, 64, 512, 4096}).main(1319 samples): TODO — baseline acc vs candidate acc; thefused op is fp32-accurate, so parity is expected.
Essential Elements of an Effective PR Description Checklist
supported_models.mdandexamplesfor a new model.AI assistance was used for this change.