Skip to content

[ROCm][Perf] Drop two dead per-step ops from the AITER sparse MLA metadata build - #58354

Merged
dllehr-amd merged 3 commits into
vllm-project:mainfrom
ZhengGong-amd:rocm-sparse-mla-metadata-dead-ops
Oct 14, 2026
Merged

dllehr-amd merged 3 commits into
vllm-project:mainfrom
ZhengGong-amd:rocm-sparse-mla-metadata-dead-ops

Conversation

@ZhengGong-amd

@ZhengGong-amd ZhengGong-amd commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Purpose

ROCMAiterMLASparseMetadataBuilder.build() runs once per KV-cache group per scheduler step and launches two GPU ops whose results are not needed:

  1. Unused query_lens. build() computes query_start_loc[1:] - query_start_loc[:-1], but generate_sparse_seqlen_triton only reads its size and device; the kernel derives per-request lengths from cu_query_lens. The parameter is dropped and num_seqs is taken as cu_query_lens.size(0) - 1.
  2. Self-aliasing fill_. paged_kv_indptr[n + 1:].fill_(paged_kv_indptr[n]) passes a view of the tensor being filled, so ATen clones the value first. A broadcast copy_ from the same element writes the same values without the clone.
op aten ops GPU kernels
x[n+1:].fill_(x[n]) fill_ → clone (empty_strided + copy) → copy_ 2
t.copy_(x[n].expand_as(t)) copy_ 1
qsl[1:] - qsl[:-1] sub (+ alloc) 1

Each build() launches two fewer kernels and makes two fewer device allocations, with no change in output. #57885 removed a similar dead op from the DSA indexer builder.

On ROCm, sparse-MLA layers that do not bring their own backend select ROCM_AITER_MLA_SPARSE. Of 18 current checkpoints probed on main (dummy weights, 8 layers, TP=1), three reach this build():

model binding build() per decode step
GLM-5.3 (glm_moe_dsa) DeepseekV32MLAAttention → ROCMAiterMLASparseBackend 2
GLM-5.3-Quark-MXFP4-AttnFP8 (glm_moe_dsa) same 2
GLM-5.3-Flash (glm5_next, DSA every 4th layer) MLAAttention → ROCMAiterMLASparseBackend 1

DeepSeek-V4-Flash/Pro and V4.1-Flash use their own builders, and the other 12 have no sparse MLA. CUDA never selects this backend.

Test Plan

  • pytest tests/kernels/attention/test_rocm_aiter_mla_sparse_metadata_sync.py. The new test_build_fills_paged_kv_indptr_tail poisons the paged_kv_indptr tail and checks that it reads as the running total; it fails if the tail copy is removed.
  • Output equivalence: every benchmark run hashes the full paged_kv_indptr buffer (tail poisoned first), the returned metadata tensors and the AITER work/reduce metadata.
  • Micro: the real build() on a builder constructed from a real VllmConfig (max_num_batched_tokens=8192, top-k 2048, decode B ∈ {1, 7, 29, 61, 127}). Both implementations alternate on one builder, order flipped every pair, 300 warmup + 3000 pairs, in 3 processes. Steady: contexts > top-k, so the persistent-metadata key hits. Growing: the key misses, so get_mla_metadata_v1 and a stream sync run on every build.
  • e2e: GLM-5.3-Flash, TP=2, --kv-cache-dtype auto (fp8 KV faults on main with HSA_STATUS_ERROR_MEMORY_FAULT, before and independent of this PR), a fresh server per run, 4 runs per arm with the first discarded.
vllm serve zai-org/GLM-5.3-Flash --tensor-parallel-size 2 --max-model-len 8192 --no-enable-prefix-caching --trust-remote-code
vllm bench serve --model zai-org/GLM-5.3-Flash --dataset-name random --random-input-len 1031 --random-output-len 509 \
    --random-range-ratio 0.25 --num-prompts 509 --max-concurrency 61 --request-rate inf --ignore-eos --seed 1234
  • e2e: GLM-5.3-Flash, TP=2, --kv-cache-dtype auto (fp8 KV faults on main with HSA_STATUS_ERROR_MEMORY_FAULT, before and independent of this PR), a fresh server per run, 4 runs per arm with the first discarded.
vllm serve zai-org/GLM-5.3-Flash --tensor-parallel-size 2 --max-model-len 8192 --no-enable-prefix-caching --trust-remote-code
vllm bench serve --model zai-org/GLM-5.3-Flash --dataset-name random --random-input-len 1031 --random-output-len 509 \
    --random-range-ratio 0.25 --num-prompts 509 --max-concurrency 61 --request-rate inf --ignore-eos --seed 1234
  • Accuracy: lm_eval --model local-completions --tasks gsm8k --num_fewshot 5, GLM-5.3-Flash, TP=2, max_model_len=4096, --kv-cache-dtype auto, seed=1234.

Test Result

Unit: 5 passed on MI355X (gfx950), main 863475eb9c. Output hashes match between the arms for every config and batch size.

Micro, MI355X (gfx950), torch 2.12.0, main c961121519:

config B main (µs) PR (µs) paired Δ per call (µs), median [min, max] of 3 processes gain
GLM-5.3, steady (AITER persistent path) 1 91.48 80.68 10.82 [9.71, 10.89] +11.2 %
29 91.29 81.22 10.12 [9.71, 10.66] +11.0 %
127 91.62 81.22 10.55 [10.00, 10.59] +11.1 %
GLM-5.3, growing (metadata recompute every build) 1 162.81 149.77 12.81 [11.92, 13.91] +8.3 %
29 170.80 158.44 12.03 [11.94, 13.72] +7.5 %
127 200.38 191.20 10.41 [9.88, 15.20] +5.3 %
GLM-5.3-Flash, steady (Triton sparse path) 1 72.75 62.01 10.70 [10.41, 11.06] +14.8 %
29 72.80 61.98 10.83 [10.69, 11.37] +15.0 %
127 73.13 62.33 10.83 [10.79, 10.86] +14.8 %

The saving is a flat ~10.5 µs per call, independent of batch size and decode path; this PR is faster in 88–99 % of pairs. A blocked cross-process A/B on main 72e7874fa6 (4 runs per arm, the first discarded, median of runs 2–4) agrees: 10.6–12.3 µs saved when steady, 13.0–15.8 µs when growing, 12.9–13.4 µs on the non-persistent path, with identical output hashes. At one or two builds per decode step this is under 0.1 % of a GLM-5.3 decode step, below end-to-end noise.

e2e, GLM-5.3-Flash (Triton sparse path), TP=2, 509 prompts in 1031 / out 509, concurrency 61, main 72e7874fa6. The difference is within run-to-run spread, so no end-to-end speedup is claimed; per-run numbers are in the first comment.

GLM-5.3-Flash, TP=2, bf16 KV: median of runs 2–4 (range) main PR Δ
Total tok/s 4509 (4492–4537) 4534 (4493–4544) +0.6 %
Mean TTFT (ms) 409.8 (406.4–430.2) 407.9 (407.0–410.4) -0.5 %
Median TTFT (ms) 170.2 (168.2–171.4) 168.5 (167.1–171.4) -1.0 %
Mean TPOT (ms) 38.96 (38.74–39.08) 38.75 (38.66–39.13) -0.6 %
Median TPOT (ms) 39.82 (39.67–39.97) 39.73 (39.63–40.15) -0.2 %

GSM8K (strict-match), GLM-5.3-Flash, TP=2, main 72e7874fa6:

model main PR
GLM-5.3-Flash 0.9105 0.9181

Per-sample strict-match agreement with main is 1235/1319.


Found by Hyperloom; reviewed and measured by hand.

@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.

@ZhengGong-amd
ZhengGong-amd force-pushed the rocm-sparse-mla-metadata-dead-ops branch 2 times, most recently from 1853574 to b88ba14 Compare September 30, 2026 12:10

@simondanielsson simondanielsson left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM, can we attach gsm8k before and after just to validate we're not breaking anything (even though I doubt it :) )

@ZhengGong-amd

Copy link
Copy Markdown
Contributor Author

e2e results, per run

MI355X (gfx950), torch 2.12.0+rocm10.0.0, aiter 0.1.23, VLLM_ROCM_USE_AITER=1. Both arms run upstream main 72e7874fa6 Python on the image's compiled build (vLLM 0.30.1rc1.dev312+g36768d1bf); the PR arm adds this diff only.

Every run starts a fresh server and waits for /health:

vllm serve zai-org/GLM-5.3-Flash --tensor-parallel-size 2 --kv-cache-dtype auto --max-model-len 8192 \
    --no-enable-prefix-caching --gpu-memory-utilization 0.90 --trust-remote-code
vllm bench serve --backend vllm --model zai-org/GLM-5.3-Flash --dataset-name random --random-input-len 1031 \
    --random-output-len 509 --random-range-ratio 0.25 --num-prompts 509 --max-concurrency 61 \
    --request-rate inf --ignore-eos --seed 1234
  • Each arm runs in one block of 4 consecutive runs. The first run warms the compile caches and is not listed.
  • Host load is the 1-minute load average when the bench started → when it ended.
  • TTFT and TPOT are in ms.
  • fp8 KV is not used: on main, GLM-5.3-Flash with --kv-cache-dtype fp8 hits HSA_STATUS_ERROR_MEMORY_FAULT during CUDA graph capture (and on the first decode with --enforce-eager).
arm run start (UTC) total tok/s mean TTFT median TTFT mean TPOT median TPOT host load
main 2 18:47 4492.0 430.2 171.4 39.08 39.97 50 → 9
main 3 18:55 4509.0 409.8 170.2 38.96 39.82 114 → 12
main 4 19:02 4536.7 406.4 168.2 38.74 39.67 98 → 11
PR 2 19:19 4493.3 410.4 171.4 39.13 40.15 93 → 10
PR 3 19:27 4533.9 407.9 168.5 38.75 39.73 98 → 11
PR 4 19:34 4544.1 407.0 167.1 38.66 39.63 111 → 12

@ZhengGong-amd

Copy link
Copy Markdown
Contributor Author

@simondanielsson — GSM8K on the only end-to-end model that reaches this build() path.

GSM8K results

MI355X (gfx950), torch 2.12.0+rocm10.0.0, aiter 0.1.23, VLLM_ROCM_USE_AITER=1. Both arms run upstream main 72e7874fa6 Python on the image's compiled build (vLLM 0.30.1rc1.dev312+g36768d1bf); the PR arm adds this diff only.

Each arm starts a fresh server, waits for /health, then runs lm_eval once:

vllm serve zai-org/GLM-5.3-Flash --tensor-parallel-size 2 --kv-cache-dtype auto \
    --max-model-len 4096 --no-enable-prefix-caching --gpu-memory-utilization 0.90 --trust-remote-code
lm_eval --model local-completions --tasks gsm8k --num_fewshot 5 --batch_size 1 --seed 1234 \
    --model_args model=/shared_nfs/models/GLM-5.3-Flash,base_url=http://127.0.0.1:PORT/v1/completions,\
tokenizer=/shared_nfs/models/GLM-5.3-Flash,tokenizer_backend=huggingface,trust_remote_code=True,\
tokenized_requests=False,num_concurrent=64,max_retries=5,max_gen_toks=256,timeout=3600 \
    --log_samples
  • Host load is the 1-minute load average when the run started → when it ended.
  • --kv-cache-dtype auto (bf16 KV): fp8 KV faults on main with HSA_STATUS_ERROR_MEMORY_FAULT during graph capture, independent of this PR.
arm start (UTC) strict-match flexible-extract host load
main 02:36 0.9105 0.9113 32 → 8
PR 02:45 0.9181 0.9189 35 → 8

Per-sample strict-match agreement with main: 1235 / 1319 (84 differ).

@simondanielsson simondanielsson left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

I'm aware this small perf change can't really be measured e2e but imo it's good we're cleaning up the metadata build (as e.g. for GLM-5.3-Flash it's taking a non-trivial amount of time per step)

@simondanielsson simondanielsson added the ready ONLY add when PR is ready to merge/full CI is needed label Oct 9, 2026
@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown

✅ @ZhengGong-amd, CI is now available for this PR.

  • /ci run starts upstream CI; /amd-ci run starts AMD CI only.
  • Your branch must contain every commit currently on its upstream target branch. Merge or rebase onto the latest target branch, then rerun the command. Append --allow-stale to a run command to test an outdated branch at your own risk.
  • /ci retry retries failed jobs in the CI build for the current PR head. If the current head has no CI build, it starts a new CI build for the current head containing only jobs that failed in the latest earlier CI build for this PR.
  • /amd-ci retry retries failed jobs in AMD CI for the current PR head. Use /amd-ci run when the current head has no AMD CI build.
  • /ci cancel cancels scheduled or running CI builds for this PR branch; /amd-ci cancel does the same for AMD CI only.

@simondanielsson

Copy link
Copy Markdown
Contributor

/ci run

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown

✅ Triggered Buildkite CI #93880 for commit 5303695524ca.

@ZhengGong-amd

Copy link
Copy Markdown
Contributor Author

Thank you @simondanielsson ! The CI test passed. Could you please merge this PR if you think this is ready :-)

@vllm-agent

vllm-agent commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

CI selector (shadow): 3 test steps (10 jobs) instead of 75 (99 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.

steps (jobs) Today's rules Selector Would skip Would add
NVIDIA, CPU and others 75 (99) 3 (10) 74 (92) 2 (3)
AMD mirrors 74 (96) 10 (18) 67 (81) 3 (3)
Selector would run (3)
  • cpu-kernel-tests ×2
  • dsv41-mono-decode-numerics-amd
  • kernels-attention-test ×7
Would skip (today's rules run them) (74)
  • amd-lm-eval-small-models-harness
  • amd-qwen3-next-mtp-async-eplb-accuracy
  • ascend-npu-test
  • async-engine-inputs-utils-worker
  • basic-correctness ×2
  • basic-correctness-cpu-offload
  • basic-correctness-cumem
  • basic-correctness-prefetch-offload
  • basic-correctness-sleep-mode
  • basic-models-test-other-cpu
  • basic-models-tests-initialization
  • basic-models-tests-other
  • batch-invariance-b200
  • batch-invariance-h100
  • benchmarks-cli-test
  • cpu-language-generation-and-pooling-model-tests ×3
  • cpu-multimodal-config
  • cpu-params-env-tokenizers-parser
  • cpu-reasoning-renderers
  • cpu-tool-parsers
  • e2e-core-1-gpu
  • e2e-core-large-memory
  • e2e-scheduling-1-gpu
  • e2e-scheduling-accuracy-1-gpu
  • entrypoints-integration-api-server ×4
  • entrypoints-integration-api-server-generate
  • entrypoints-integration-api-server-openai-chat_completion
  • entrypoints-integration-api-server-openai-completion
  • entrypoints-integration-llm
  • entrypoints-integration-multimodal
  • entrypoints-integration-pooling
  • entrypoints-integration-responses-api
  • entrypoints-integration-speech_to_text
  • fusion-e2e-quick-h100
  • fusion-e2e-tp2-ar-rms-dynamo-partition-amd
  • fusion-e2e-tp2-ar-rms-inductor-partition-amd
  • fusion-e2e-tp2-b200
  • fusion-e2e-tp2-quick-h100
  • kernels-fla-ops-test-b200
  • kernels-mhc-test-b200
  • kernels-root-misc-test-b200
  • language-models-tests-granite-l4-compatibility
  • language-models-tests-hybrid ×2
  • language-models-tests-standard
  • metrics-tracing-2-gpus
  • multi-modal-models-standard-1-qwen2
  • multi-modal-models-standard-2-qwen3-gemma
  • multi-modal-models-standard-3-llava-qwen2-vl
  • multi-modal-models-standard-4-other-whisper
  • multi-modal-processor ×4
  • multi-modal-processor-cpu ×4
  • pytorch-compilation-dynamic-shapes
  • pytorch-compilation-passes-unit-tests
  • pytorch-compilation-unit-tests
  • pytorch-compilation-unit-tests-h100
  • pytorch-fullgraph-cudagraph-l4-compatibility
  • pytorch-fullgraph-test
  • regression
  • rl-entrypoints-tests
  • spec-decode-mtp-deepseek-mimo
  • spec-decode-mtp-gemma4
  • spec-decode-mtp-qwen3-5
  • spec-decode-speculators
  • v1-attention-b200 ×2
  • v1-attention-h100-mi300 ×2
  • v1-core
  • v1-executor-worker
  • v1-kv-connectors ×4
  • v1-kv-offload
  • v1-logits-oracle
  • v1-metrics-lmeval
  • v1-others-cpu
  • v1-sample
  • v1-spec-decode
Would add (today's rules do not run them) (2)
  • cpu-kernel-tests ×2 (code map)
  • dsv41-mono-decode-numerics-amd (code map)
AMD mirrors: would skip (67)
  • async-engine-inputs-utils-worker
  • basic-correctness ×2
  • basic-correctness-cpu-offload
  • basic-correctness-cumem
  • basic-correctness-prefetch-offload
  • basic-correctness-sleep-mode
  • basic-models-tests-initialization
  • benchmarks-cli-test
  • e2e-core-1-gpu
  • e2e-core-large-memory
  • e2e-scheduling-1-gpu
  • e2e-scheduling-accuracy-1-gpu
  • entrypoints-integration-api-server ×4
  • entrypoints-integration-api-server-generate
  • entrypoints-integration-api-server-openai-chat_completion
  • entrypoints-integration-api-server-openai-completion
  • entrypoints-integration-llm
  • entrypoints-integration-multimodal
  • entrypoints-integration-pooling
  • entrypoints-integration-responses-api
  • entrypoints-integration-speech_to_text
  • extract-hidden-states-integration-2-gpus
  • fusion-and-compile-unit-tests-2xb200
  • fusion-e2e-quick-h100
  • fusion-e2e-tp2-ar-rms-config-sweep-h100
  • fusion-e2e-tp2-b200
  • fusion-e2e-tp2-quick-h100
  • kernels-fla-ops-test-b200
  • kernels-mhc-test-b200
  • kernels-root-misc-test-b200
  • language-models-tests-extra-standard ×2
  • language-models-tests-granite-l4-compatibility
  • language-models-tests-hybrid ×2
  • language-models-tests-standard
  • lm-eval-small-models
  • metrics-tracing-2-gpus
  • mrcr-eval-small-models
  • multi-modal-models-standard-1-qwen2
  • multi-modal-models-standard-2-qwen3-gemma
  • multi-modal-models-standard-3-llava-qwen2-vl
  • multi-modal-models-standard-4-other-whisper
  • multi-modal-processor ×4
  • openai-api-correctness
  • pipeline-context-parallelism-4-gpus
  • platform-tests
  • pytorch-compilation-dynamic-shapes
  • pytorch-compilation-passes-unit-tests
  • pytorch-compilation-unit-tests
  • pytorch-compilation-unit-tests-h100
  • pytorch-fullgraph-cudagraph-l4-compatibility
  • pytorch-fullgraph-test
  • quantized-moe-test-b200
  • regression
  • spec-decode-mtp-deepseek-mimo
  • spec-decode-mtp-gemma4
  • spec-decode-mtp-qwen3-5
  • spec-decode-speculators
  • v1-attention-b200 ×2
  • v1-attention-h100-mi300 ×2
  • v1-core
  • v1-executor-worker
  • v1-kv-connectors ×4
  • v1-kv-offload
  • v1-logits-oracle
  • v1-metrics-lmeval
  • v1-sample
  • v1-spec-decode
AMD mirrors: would add (3)
  • pytorch-nightly-dependency-override-check (code map)
  • ray-dependency-compatibility-check (code map)
  • torch-stable-abi-audit (code map)

2 changed files · base f47792af7f · head 6586df210b · Python record: build 93039 at 1e5d0ea888 · kernel record: table fe341a5 (build 94315), map fe341a5 · not counted: 11 build steps, 5 A100 steps the generator no longer emits, 11 optional steps the selector would also run

@simondanielsson

Copy link
Copy Markdown
Contributor

/ci run

@github-actions

Copy link
Copy Markdown

❌ This PR is 1 commit behind upstream main. Your branch must contain every commit currently on upstream main. No new CI build was started. Merge or rebase onto the latest main, then rerun /ci run. To test this branch at your own risk, use /ci run --allow-stale.

@mergify

mergify Bot commented Oct 12, 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, @ZhengGong-amd.

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 Oct 12, 2026
…adata build

Generated-by: Hyperloom
Co-authored-by: Claude
Signed-off-by: ZhengGong-amd <zgong@amd.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@ZhengGong-amd
ZhengGong-amd force-pushed the rocm-sparse-mla-metadata-dead-ops branch from abc4855 to 6586df2 Compare October 13, 2026 01:54
@mergify mergify Bot removed the needs-rebase label Oct 13, 2026
@ZhengGong-amd

Copy link
Copy Markdown
Contributor Author

/ci run

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #94351 for commit 6586df210bac.

@ZhengGong-amd

Copy link
Copy Markdown
Contributor Author

/ci run

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #94426 for commit fcf66a3cfd59.

@ZhengGong-amd

Copy link
Copy Markdown
Contributor Author

/ci run

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #94455 for commit aa7438fa42ba.

@dllehr-amd
dllehr-amd merged commit 355553a into vllm-project:main Oct 14, 2026
208 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in AMD Oct 14, 2026
AndreasKaratzas pushed a commit to AndreasKaratzas/vllm that referenced this pull request Oct 16, 2026
…adata build (vllm-project#58354)

Signed-off-by: ZhengGong-amd <zgong@amd.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

Signed-off-by: Andreas Karatzas <AndreasKaratzas@users.noreply.github.com>
michael-gpu pushed a commit to practical-inference/vllm-ampere-extended that referenced this pull request Oct 20, 2026
…adata build (vllm-project#58354)

Signed-off-by: ZhengGong-amd <zgong@amd.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready ONLY add when PR is ready to merge/full CI is needed rocm Related to AMD ROCm

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants