Skip to content

[ROCm][Perf] Reuse graph-stable attention metadata across Kimi-K3 cache groups - #53301

Closed
JohnQinAMD wants to merge 3 commits into
vllm-project:mainfrom
JohnQinAMD:upstream-pr/kimi-k3-metadata-reuse
Closed

JohnQinAMD wants to merge 3 commits into
vllm-project:mainfrom
JohnQinAMD:upstream-pr/kimi-k3-metadata-reuse

Conversation

@JohnQinAMD

@JohnQinAMD JohnQinAMD commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Purpose

Kimi-K3 on ROCm runs many KV-cache groups over one batch: at TP8, six MLA groups and
fourteen KDA groups. Every group currently builds its own attention metadata, but most
of it describes the batch — query offsets, sequence lengths, speculative offsets, the
split-K schedule — and is identical across groups. Only the page indices and state
indices differ, because only the block table differs.

This builds once per step and, for the remaining groups, redoes only the
block-table-dependent part.

Full CUDA graphs bake tensor addresses at capture, so metadata cannot simply be handed
to another group, and buffers two groups will share must be rebound before capture.
Four hooks, declared on AttentionMetadataBuilder with no-op defaults so backends that
don't opt in are unaffected:

  • share_reusable_metadata_buffers() — before capture, point compatible groups at one
    allocation for the buffers that are read-only during execution.
  • can_reuse_metadata() — per step; anything it rejects falls back to a normal build.
  • update_block_table() — redo only page/state indices.
  • get_metadata_reuse_key() — extra identity beyond (builder type, kv-cache spec).

AITER MLA and Kimi-K3 KDA implement them.

Test Plan

Unit tests, in the upstream image matching this branch's base:

docker run --rm --entrypoint bash --device /dev/kfd --device /dev/dri \
  --group-add video --ipc=host -v $PWD:/work \
  vllm/vllm-openai-rocm:nightly-d626108b1841888ec90aced33367149a6bbc7e4b -c '
  pip install -q tblib
  SP=$(python3 -c "import vllm,os;print(os.path.dirname(vllm.__file__))")
  for f in models/kimi_k3/amd/kda.py models/kimi_k3/amd/kda_metadata.py \
           models/kimi_k3/nvidia/kda_metadata.py v1/attention/backend.py \
           v1/attention/backends/mla/rocm_aiter_mla.py v1/worker/gpu/attn_utils.py; do
    cp /work/vllm/$f $SP/$f; done
  cd /work && python3 -m pytest -q tests/v1/worker/test_attn_utils.py \
    tests/v1/attention/test_rocm_aiter_mla_mtp_split.py \
    tests/models/kimi_k3/test_kda_metadata.py'

ruff check and ruff format --check (pinned 0.14.0) on the six changed files.

Accuracy on 8x MI355X, Kimi-K3 TP8 with a DSpark draft and fp8 KV. Control and candidate
are the same image with only these files swapped in (same approach as #52356); serve
flags follow #51253 / #50619:

vllm serve /model --served-model-name k3 --tensor-parallel-size 8 --trust-remote-code \
  --kv-cache-dtype fp8 --max-model-len 16384 --max-num-seqs 128 \
  --max-num-batched-tokens 16384 --gpu-memory-utilization 0.93 --block-size 128 \
  --no-enable-prefix-caching --mm-encoder-tp-mode data \
  --compilation-config '{"custom_ops":["+fused_rms_norm_gated"]}' \
  --speculative-config '{"method":"dspark","model":"/draft","num_speculative_tokens":2,"attention_backend":"TRITON_MLA"}'

lm_eval --model local-chat-completions \
  --model_args model=k3,base_url=http://127.0.0.1:8000/v1/chat/completions,num_concurrent=32,tokenized_requests=False \
  --tasks gsm8k --num_fewshot 5 --batch_size 32 --limit 200 \
  --apply_chat_template --gen_kwargs temperature=0

Test Result

Unit tests: 72 passed. Two of them are regression tests that fail without this
change: one covers mamba_cache_mode="none" (the default), where the state column is a
contiguous prefix rather than the align offset; the other covers the full-cudagraph
branch of update_block_table.

Lint: clean on all six files.

gsm8k, 200 questions, 5-shot greedy: 0.995 control, 0.995 candidate, both filters.

Metadata build cost (14 KDA groups, 8 requests, num_speculative_tokens=2),
fourteen builds versus one build plus thirteen reuses:

context 14x build 1 build + 13 reuse saved
1,024 663.4 us 191.1 us 472.3 us 3.47x
16,384 674.8 us 199.4 us 475.4 us 3.38x
262,144 668.3 us 197.2 us 471.1 us 3.39x

Scope of the claim:

  • The saving does not vary with context length. Both halves are launch-bound: MLA's
    page-index expansion is also flat at ~10.3 us per group from 1K to 262K tokens. It
    scales with group count and batch size instead.
  • It is a fixed ~0.5 ms per decode step, so its relative value depends on step time. At
    num_speculative_tokens=2 a step is ~10 ms. Under a num_speculative_tokens=7
    draft-verify loop a step is ~163 ms and the same saving is ~0.3%.

No serving throughput number is claimed: a single control/candidate pair on this
hardware is within run-to-run drift, and separating them needs alternating arms with a
paired bootstrap.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@mergify mergify Bot added kimi k3 rocm Related to AMD ROCm mrv2 Model Runner V2 specific labels Aug 21, 2026
@github-project-automation github-project-automation Bot moved this to Todo in AMD Aug 21, 2026
@JohnQinAMD
JohnQinAMD force-pushed the upstream-pr/kimi-k3-metadata-reuse branch 2 times, most recently from d751cdb to d8bf7b9 Compare August 21, 2026 17:41
@JohnQinAMD
JohnQinAMD marked this pull request as draft August 21, 2026 18:42
@mergify

mergify Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

This pull request has merge conflicts that must be resolved before it can be
merged. Please rebase the PR, @JohnQinAMD.

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@mergify mergify Bot added the needs-rebase label Aug 21, 2026
JohnQinAMD and others added 3 commits August 22, 2026 02:18
Kimi-K3 has six target MLA cache groups with identical request and query geometry but distinct block tables. Rebuilding the persistent AITER schedule and serially expanding a 327K-token page table for every group consumed about 1 ms per decode step.

Full CUDA graphs retain capture-time tensor addresses, so returning the first group metadata object is not safe: later graphs continue reading their own persistent buffers. Share the compatible builders’ read-only numeric schedule tensors before graph capture, then retain group-local page indices and pointer-bearing work metadata. Runtime metadata preparation builds the shared schedule once and only expands each group-specific block table, with long page expansion distributed over token tiles.

At TP8 c1 M3 and 327482 tokens, the six-group source microbenchmark improves from 749.364 us to 236.221 us (3.17x, -0.513 ms); three independent minimal-version repeats save 511.2/489.9/508.6 us, median 508.6 us. The full-graph endpoint phase improves MLA metadata from 1.000845 ms to 0.473963 ms per step (-0.526882 ms). Mutated CUDA-graph probes validate shared schedules and group-local page buffers; the two focused test files pass 39 tests.

Signed-off-by: Yanyuan Qin <yanyuan.qin@amd.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Move the Kimi-K3 decode metadata builder to a platform-neutral module and reuse it from ROCm while preserving the ROCm device-side prefill chunk hook. The generic GDN builder spends about 2.69 ms per c1 target step across 14 KDA groups; the specialized builder reduces this to about 1.18 ms in the production endpoint phase measurement. Use num_spec_decodes as the caller contract because the specialized builder intentionally does not materialize spec_sequence_masks. Preserve prefill-only views for mixed regular-decode/prefill batches.

Signed-off-by: Yanyuan Qin <yanyuan.qin@amd.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Kimi-K3 builds the same speculative query offsets and accepted-token counts for 14 KDA cache groups, while only the aligned state indices depend on each group block table. Repeating the full specialized builder therefore leaves more than half a millisecond of c1 pre-target work.

Add a V2-only metadata-reuse contract that can fail closed for mixed and prefill batches. Compatible FULL-graph builders share only query-offset and accepted-count buffers before capture. Runtime builds those common values once and launches a small kernel per remaining group to update its graph-local state-index buffer. The legacy block-table update contract remains disabled.

At TP8 c1 M3 and sequence length 327482, three independent 14-group source microbenchmarks save 621.1/610.3/597.9 us per step, median 610.3 us, and reduce the specialized builder family by about 3.0x. Mutated FULL-graph replay validates shared common buffers and group-local state buffers; the combined KDA, MLA, and metadata-dispatch test set passes 55 tests.

Signed-off-by: Yanyuan Qin <yanyuan.qin@amd.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@JohnQinAMD
JohnQinAMD force-pushed the upstream-pr/kimi-k3-metadata-reuse branch from d8bf7b9 to 8233773 Compare August 22, 2026 02:21
@mergify mergify Bot removed the needs-rebase label Aug 22, 2026
@mergify

mergify Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

This pull request has merge conflicts that must be resolved before it can be
merged. Please rebase the PR, @JohnQinAMD.

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

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

Labels

k3 kimi mrv2 Model Runner V2 specific needs-rebase rocm Related to AMD ROCm

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant