Skip to content

[Triton/Gluon] [Perf] [KDA] Optimize the gfx950 FlashKDA prefill for GLM-5.3-Flash - #7360

Draft
waqahmed-amd-fi wants to merge 3 commits into
mainfrom
waqahmed/SILOTIGER-1364
Draft

waqahmed-amd-fi wants to merge 3 commits into
mainfrom
waqahmed/SILOTIGER-1364

Conversation

@waqahmed-amd-fi

@waqahmed-amd-fi waqahmed-amd-fi commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

On gfx950, flash_kda_fwd now reads strided q, k, v and g in place in its Gluon kernels. Before, it
packed these operands into contiguous copies. The results do not change: they are bit for bit
the same as with packed inputs.

This PR is stacked on #6912, because the change uses the
segment plan that #6912 adds. Review only commit
02689fad. vLLM uses
it in vllm-project/vllm#65224.

Motivation

vLLM's GLM-5.3-Flash KDA layer gives FlashKDA q, k and v as column slices of one conv output. Their
token stride is 3 × H × D. g is also a strided view. On gfx950, flash_kda_fwd packed the four
operands before K1 and before passes A and C. The gfx1250 Gluon kernels already read them at their
own stride.

On MI355X, at TP4, the copies take 53.3 us in each of the 34 KDA layers of an 8192-token prefill
step. That is 1.8 ms per step.

Changes

  • The gfx950 K1 (flash_kda_k1.py) takes stride_q, stride_k and stride_g.
  • The gfx950 passes A and C (flash_kda_k2.py) take stride_v.
  • flash_kda_fwd decides how to pass each operand after it knows the kernel routes. It still packs
    an operand that a Triton kernel reads. It also packs an operand whose rows are not 16-byte aligned,
    or whose span needs offsets larger than 32 bits.
  • The chunk_delta_attn docstring says that gfx950 also reads these operands in place.
  • test_strided_operands_match_packed checks the in-place reads on gfx950 too.

The gfx1250 kernels, the Triton kernels and the other archs do not change. Contiguous inputs take
the same path as before.

Performance (if applicable)

GLM-5.3-Flash MXFP4 (amd/GLM-5.3-Flash-Quark-MXFP4) served by vLLM, rank 0, torch profiler over
60000/600 at concurrency 16. The step is one 8192-token prefill with 8-15 decodes, the most common
prefill step there. Both arms use the same vLLM, with #6912 in AITER; only this commit changes.

Hardware Workload/configuration Before After Change
4x MI355X (gfx950), TP4 copies in each KDA layer 53.3 us 5.3 us -90.1%
4x MI355X (gfx950), TP4 34 KDA layers per step 34.13 ms 32.79 ms -3.9%
4x MI355X (gfx950), TP4 prefill step 125.5 ms 122.7 ms -2.2%

The FlashKDA kernels take about the same time with strided inputs: K1 57.3 → 56.1 us, pass A
41.1 → 42.3 us and pass C 40.5 → 41.0 us per layer. The copies left are the beta pack and others
outside FlashKDA. Over nine serving points, the
change alone is within run-to-run noise: output throughput moves -1.8% to +1.4% (geomean +0.2%),
and median TPOT -1.3% to +0.5% (geomean -0.4%). The long-context points gain most.

Together with vllm#65224, this branch serves GLM-5.3-Flash faster than vLLM and AITER main. Over
the same nine serving points, output throughput goes up by 11.5% and median TPOT goes down by 10.8%
(geomeans). These numbers include vLLM's #63568 and #63612, which vllm#65224 carries. vllm#65224
gives the full tables.

Testing

  • Unit tests added or updated
  • Performance benchmarks run
  • Tested on MI250X
  • Tested on MI300X

The change is for gfx950 only, so it was not run on MI250X or MI300X. See "Validation and unverified
scope" below.

Documentation

  • Docstrings updated
  • User guide updated
  • Performance guide updated
  • No documentation change is needed, with the reason stated below

Dependencies

  • No new third-party dependencies added
  • New dependencies are listed and justified below

Breaking Changes

None.

PR Self-Review

Reviewed revision

1373434cfcaa

Behavior and intent

On gfx950, FlashKDA's Gluon kernels read strided q, k, v and g where they are. Before, the wrapper
copied them to contiguous tensors first. The copies cost 1.8 ms in each 8192-token prefill step of
GLM-5.3-Flash. The output does not change.

Validation and unverified scope

On 4x MI355X (gfx950), in vllm/vllm-openai-rocm:nightly-76c6f3f2a95f6049657db1e513133b824a5c8175
(torch 2.13.0+rocm10.0.0, Triton 3.8.0), from the AITER checkout:

python -m pytest op_tests/triton_tests/kimi_delta_attn -q

Result: 157 passed, 1 skipped.

The performance numbers above come from vLLM serving GLM-5.3-Flash on MI355X at TP4. GSM8K, GPQA
Diamond and NIAH at 65536 do not change; vllm#65224 gives the scores.

Not verified: gfx1250, MI300X and MI250X. The gfx1250 kernels and the Triton kernels do not change.

PR-specific question

Before this PR, test_strided_operands_match_packed checked in-place reads only on gfx1250. On
gfx950 it checked only that the output was equal. Its new check on gfx950 fails before this PR:
the Gluon launchers got packed copies in all layouts, also in the aligned bands layout. No other
test checks the storage that the gfx950 launchers get.

Changed op tests

… metadata async

PR head 5478ffb, applied as its diff against merge base e270b84.
vLLM passes q, k and v as column slices of one conv output, so
flash_kda_fwd packed them on gfx950 before K1 and passes A and C,
about 45 us per layer at 8192 tokens. The gfx950 Gluon kernels now
read them at their own token stride, as the gfx1250 ones do.

- Take `stride_q`, `stride_k` and `stride_g` in the gfx950 K1, and
  `stride_v` in passes A and C
- Decide each operand's packing after the routes are known; pack rows
  that are not 16-byte aligned or need more than 32-bit offsets
- Check gfx950 in-place reads in `test_strided_operands_match_packed`
@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

🏷️ CI Guide

Runs automatically on every PR:

  • ✅ Pre-checks (submodule verification, code formatting)
  • ✅ Aiter op tests (gfx942 + gfx950)
  • ✅ Triton tests on MI35X (only when aiter/ops/triton/** or related paths are changed)

Extended tests (opt-in via labels):

Label Tests
ci:gfx1250-ffm-triton Run the five-shard gfx1250 FFM Triton test suite
ci:triton-300x Run an additional Triton test job on MI300X in PRs (added automatically when gfx942 configs change); main branch always runs both MI35X and MI300X
ci:triton-355 Run the full Triton test suite on MI35X, not only the tests the change affects
ci:windows Windows JIT op smoke test on the gfx1201 ROCm runner
ci:multi-gpu Aiter multi-GPU tests on the 8-GPU runner
ci:sglang SGLang integration tests: DeepSeek-R1-MXFP4 accuracy, Qwen 3.5 accuracy
ci:atom ATOM benchmark: DeepSeek-R1-0528, GPT-OSS-120B
ci:atom_full ATOM accuracy suite for PR and main models from ATOM models_accuracy.json
ci:vllm vLLM benchmark: DeepSeek-V4-Pro, MiniMax-M3-MXFP4
ci:all All standard extended tests (excludes ci:atom_full)

Only add ci:atom_full for FlyDSL or Triton upgrades.
Add labels via the sidebar or gh pr edit 7360 --add-label <label>

One backend per PR:
A PR changes one kernel backend: [Triton/Gluon] (Triton and Gluon count as one), [HIP], [ASM], [CK], [OPUS] or [FlyDSL]. If the title ends up with two backend tags, split the PR -- as stacked pull requests when one part cannot merge without the other.

PR title tags & labels:
Component tags ([Triton/Gluon], [HIP], [CK], [ASM], ...) are added to the PR title and as PR labels automatically from the changed files and re-synced on every push — change-type tags like [fix]/[Perf], op tags like [MLA], and human labels (ci:*) are left untouched. Add the no-auto-title label to stop the title rewrites; labels stay in sync either way.

@aiter-bot

aiter-bot commented Nov 2, 2026 •

Copy link
Copy Markdown

NOT READY FOR HUMAN REVIEW

PR Self-Review

Revision 1373434cfcaa · mode shadow

Waiting for the PR author. The readiness report will appear after the self-review answers are complete for this revision.

Questions to answer in the PR description

  1. What behavior does this PR change, and why is the existing behavior insufficient?
  2. What did you validate, and what remains unverified? Include commands, hardware/configuration and results.
  3. What behavior would each changed op test fail on before this PR, and why is that coverage not already provided by an existing canonical test?
  4. For every changed op_tests/ file, what behavior does it cover and why is it not redundant? Write None if no op test changed.

Edit the PR Self-Review section in the PR description. Include the current revision SHA; this comment refreshes automatically.

Missing or invalid answers

  • FAIL — validation answer must explicitly state what remains unverified (or say none with a reason)

Shadow mode reports what would block but keeps the status successful. Set AITER_PR_SELF_REVIEW_MODE=enforce only after reviewing real false positives.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants