Repository navigation
[Bugfix][Kimi-K3] Declare max_tp_shards on the DSpark MLA KV cache spec - #59733
Conversation
vllm-project#57652 sets max_tp_shards=1 on MLAAttention's KV cache spec, but the Kimi-K3 DSpark draft builds its own MLAAttentionSpec without it. The mismatch makes MLAAttentionSpec.merge reject the draft layers, so they no longer share a group with the target's MLA layers. The group size drops from 29 to 5 and Kimi-K3 + DSpark goes from 4 to 20 KV cache groups, adding per-step metadata/block-table work. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: hyukjlee <hyukjlee@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. 🚀 |
|
Some notes on scope: why only Kimi-K3 + DSpark on ROCm regresses after #57652, and why other target + MTP/draft pairs don't. When #57652 changes grouping. Kimi-K3 + DSpark on ROCm meets that condition. The ROCm target MLA goes through the generic Pairs that are not affected (checked on
So this one-line fix covers every grouping regression from #57652 on Remaining Independent measurement. We saw the same effect on MI355X TP8, Kimi-K3 + DSpark k=7, conc 4, no KV offload, 3 seeds per arm:
(Analysis prepared with Claude Code.) |
|
❌ @seungrokj, Only reviewers with write access can use CI commands before CI is delegated to the PR author. |
|
✅ @hyukjlee, CI is now available for this PR.
|
|
/ci run |
|
/ci run |
|
❌ @seungrokj, Only reviewers with write access can use CI commands before CI is delegated to the PR author. |
|
/ci run |
|
❌ This PR is 1 commit behind upstream |
|
/ci run |
|
✅ Triggered Buildkite CI #92996 for commit |
CI selector (shadow): 6 test steps (14 jobs) instead of 36 (52 jobs)Shadow mode: this changes nothing about what CI runs. It shows what the evidence-based selector would pick for this PR, next to today's rules. How it works. Feedback welcome: reply here if it would skip a step this change needs, or runs something unrelated.
Selector would run (6)
Would skip (today's rules run them) (33)
Would add (today's rules do not run them) (3)
AMD mirrors: would skip (33)
AMD mirrors: would add (0)none 2 changed files · base |
There was a problem hiding this comment.
Thanks for the point fix.
cc. @varun-sundar-rabindranath @LucasWilkinson for a second eye.
|
/ci run |
|
The DSpark draft was missing max_tp_shards=1, so it stopped sharing a KV group with the target. K3 went from 4 groups to 20. This one line puts them back together. MI355X TP8: 701 to 727 tok/s/GPU, TPOT 11.47 to 11.09 ms, back to the nightly before the regression. GSM8K 0.9636. CI passed. Good to land after a rebase onto main. |
|
✅ Triggered Buildkite CI #93413 for commit |
|
@hyukjlee can you |
|
/ci run |
|
✅ Triggered Buildkite CI #93456 for commit |
|
/ci run |
Overview
Fixes a Kimi-K3 + DSpark perf regression from #57652: the K3 DSpark draft's
MLAAttentionSpecdoes not declaremax_tp_shards, so its MLA layers stop merging with the target's and the KV cache splits from 4 to 20 groups. One-line fix: declaremax_tp_shards=1on the draft spec, matchingMLAAttention.Claims
main).kv_tp_replicas(KDA layers already pin the gcd to 1).Validation
Unit test (
tests/models/test_dspark_mla.py::test_k3_dspark_mla_kv_cache_spec_groups_with_target_mla): builds the target spec viaMLAAttention.get_kv_cache_specand the draft spec via the K3MultiHeadLatentAttention.get_kv_cache_spec, then groups 24 target + 5 draft layers with_get_kv_cache_groups_uniform_page_size.AssertionError: assert 6 == 1Run inside
vllm/vllm-openai-rocm:nightly-rocm100-ac9126e5…withpython -m pytest -q tests/models/test_dspark_mla.py -k k3_dspark_mla_kv_cache_spec.pre-commitpasses on both files.End-to-end: Kimi-K3 (MXFP4) + Kimi-K3-DSpark, MI355X ×8, TP8, exact InferenceX CI serve command (DSpark k=7 synthetic AL 3.84, fp8 KV,
SimpleCPUOffloadConnector, FULL cudagraph,--max-num-seqs 16). Client:vllm bench serve --dataset-name random --random-input-len 8192 --random-output-len 1024 --ignore-eos --max-concurrency 8 --num-prompts 80, 3 seeds per arm after a 16-prompt warmup, same host, arms run back to back.36768d1(before #57652)ac9126e(includes #57652)ac9126e+ revert #57652ac9126e+ this PRPer-seed median TPOT for this PR: 11.10 / 10.99 / 11.18 ms (vs 11.43 / 11.44 / 11.54 without it). The regression was first seen in InferenceX CI on the K3 agentic c8 cell (
kimik3 fp4 mi355x vllm TP8 mtp dram KV offload vllm-simple c8): −6.1% total throughput/GPU and −16.6% p90 interactivity between the two nightlies.Accuracy (gsm8k): same server config but with real DSpark verification (
"rejection_sample_method": "block", no synthetic acceptance length), evaluated with the InferenceX CI lm-eval setup:lm_eval --model local-chat-completions --apply_chat_template --tasks gsm8k(5-shot, CI task YAML), full 1319-question test set,temperature=0,max_tokens=16384,num_concurrent=16.ac9126e+ this PRMean DSpark acceptance length during the eval was ~3.0, confirming real verification.
Details
Root cause. #57652 added
AttentionSpec.max_tp_shardsand set it to1inMLAAttention.get_kv_cache_spec.vllm/models/kimi_k3/nvidia/mla.py(used for the K3 target on NVIDIA and for the DSpark draft on both vendors) builds its ownMLAAttentionSpecand leaves itNone. On ROCm the K3 target usesMLAAttention(1) while the draft uses this class (None), soMLAAttentionSpec.merge's per-fieldAttentionSpecequality check rejects them._get_kv_cache_groups_uniform_page_sizethen sees buckets of 69 KDA / 24 target MLA / 5 draft MLA layers instead of 69 / 29, the group size drops from 29 to 5, and the group count goes from 4 (3 KDA + 1 MLA) to 20 (14 + 5 + 1). The server log showskv cache group sizesgrowing from 4 to 20 entries andAdd 1 padding layerstwice instead ofAdd 18 padding layers.Why 20 groups is slower.
_build_attention_metadatabuilds per KV cache group each step, and neither the ROCm K3 KDA builder nor the AITER MLA builder supportsupdate_block_tablereuse, so every extra group is a fullbuilder.build()plus its own block-table/slot-mapping work.Why
1is correct. The draft's MLA latent KV has a single head and is replicated across TP ranks, same asMLAAttention. On NVIDIA the target and draft both go fromNoneto1, so they still merge and grouping does not change.kv_tp_replicasremains 1 for K3 because its KDA (Mamba) layers contribute 1 to the gcd, so offloading behavior is unchanged.Not duplicate. No open PR sets
max_tp_shardson the K3 draft spec. #57942 (draft) adds a separate AMD DSpark MLA module, and #58207 changes the group-size heuristic. Both are broader and neither fixes this mismatch onmain. This PR is the minimal fix for the regression.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.
AI assistance (Claude Code) was used to root-cause the regression, write the fix and test, and run the benchmarks and accuracy eval.
🤖 Generated with Claude Code