Repository navigation
[AMD] Qwen3.5 ASM FMHA chunked-prefill context attention - #36758
Merged
HaiShaw merged 3 commits intoAug 28, 2026
Merged
Conversation
…SM FMHA On MI355X the ck_tile paged batch_prefill runs at ~15% FP8 MFU for head_dim-256 fp8-KV context chunks, while the gfx950 ASM varlen fmha runs the same shapes ~3.5x faster. Gathering the paged fp8 KV into a contiguous varlen buffer costs ~20 us per layer at 70k context, so extend batches with a prefix now gather and call the ASM kernel, with a metadata bounds check that falls back to the paged path on any inconsistency. 70k/300 serving on Qwen3.5-397B-A17B-MXFP4 with EAGLE: mean E2E -25% (TP2 c4) and -25..-29% (TP4 c4-c16); TTFT -23..-26%, TPOT -28%.
zijiecode
requested review from
Fridge003,
HaiShaw,
Qiaolin-Yu,
hebiao064,
ispobock and
merrymercy
as code owners
August 28, 2026 00:55
yichiche
requested changes
Aug 28, 2026
HaiShaw
approved these changes
Aug 28, 2026
2 tasks done
omirosh
added a commit
to omirosh/sglang
that referenced
this pull request
Aug 28, 2026
The ASM context-prefill gather added in sgl-project#36758 returns before the paged branch at the end of forward_extend, so on a build that has both the page-level KV view is dead code: page-64 fp8 hd256 extends with a prefix gather into a contiguous buffer and never reach the paged-varlen kernel. Compute paged_asm_available once and read it from both sites - negated by the gather, affirmed by the paged branch. Two copies of the same condition would drift; one flag cannot. Where the paged kernel cannot serve the batch or the layer the flag is False and the gather runs exactly as before, so this is a handoff rather than a disable. Co-authored-by: Cursor <cursoragent@cursor.com>
3 of 5 tasks
omirosh
added a commit
to omirosh/sglang
that referenced
this pull request
Sep 14, 2026
Dropped in 8cfd17b once ROCm/aiter#4971 landed, on the reasoning that the arch and layout tests were enough to gate the path. Sweeps on two MI355X nodes since then show why that was premature: the paged path wins 1-2% throughput and 3-7% TTFT at 60k and 128k, but is flat to marginally negative at 8k, so which path is better depends on the workload rather than on the shape alone. Bring the flag back so an operator can pick, defaulting on since the long-context win is the larger effect and the shape tests still decide where the path is legal at all. Off restores the sgl-project#36758 gather exactly, because paged_asm_available is derived from paged_kv_view and both call sites read the one flag. Co-authored-by: Cursor <cursoragent@cursor.com>
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.
Motivation
On MI355X, long-input Qwen3.5 serving (70k prompt, chunked prefill) spends most of its prefill attention time in aiter's ck_tile paged
mha_batch_prefill. Measured at production shapes (head_dim 256, fp8 KV, 16k-token chunks), that kernel runs at roughly 15% of the GPU's FP8 peak — and paging is not the reason: a fully contiguous KV layout is just as slow, and fp8 inputs are only ~1.2x faster than bf16, so the pipeline never reaches the fp8 rate.The gfx950 ASM kernel
fmha_fwd_hd256_fp8_causal_group(already used by SGLang for the no-prefix first chunk) runs the same shapes 3.5x faster. The only reason context chunks cannot use it today is that it wants contiguous varlen K/V, while the KV cache is paged.It turns out the gather is nearly free: copying 70k tokens of fp8 KV (~72 MB) into a contiguous buffer takes ~18 µs, against several milliseconds for the attention itself. So we gather and call the ASM kernel.
Modifications
python/sglang/srt/layers/attention/aiter_backend.py, inforward_extend:kv_indptr/kv_indicesmetadata into per-token pool indices, gathers K/V into a contiguous varlen buffer (via auint8view, sinceindex_selectis not implemented for fp8), and callsflash_attn_varlen_fp8_pertensor_funcwith bottom-right causal masking.seq_lensbefore gathering (mixed / speculative batches can disagree); on any mismatch it logs once and falls back to the original paged path. Nothing crashes, worst case is the old speed.vectorized_5dlayout (different physical layout).Accuracy Tests
GSM8K, 5-shot, all 1319 questions, temperature 0, max new tokens 2048; Qwen3.5-397B-A17B-MXFP4, TP2, MI355X.
Benchmarking and Profiling
Benchmarks were run on a tree with #36330 applied (the current MI355X perf baseline for this model); the change itself is independent of it and this PR is based on
main.Kernel microbenchmark
Single MI355X GPU, causal, head_dim 256, fp8 KV, 16384 new tokens attending to a 32k-token paged context (the production context-chunk shape):
fmha_fwd_hd256_fp8_causal_group+ gatherPaged and contiguous ck_tile run at the same speed, and fp8 is only ~1.2x faster than bf16 there — so paging is not the bottleneck, kernel efficiency is. Tail chunks show the same ratio (4464 new tokens over a 65k context: 2.44 ms vs 8.99 ms). The KV gather this PR adds costs 17.6 µs at 65k context (~7.6 TB/s effective), negligible against the attention itself.
End-to-end serving
End-to-end serving on MI355X, Qwen3.5-397B-A17B-MXFP4, random-ids 70k input / 300 output, EAGLE (3 steps, top-k 1, 4 draft tokens), warmup + measured requests, otherwise the pinned recipe (
--attention-backend aiter --page-size 16 --kv-cache-dtype fp8_e4m3 --chunked-prefill-size 16384):TPOT improves as a side effect: other requests' prefill chunks block decode for less time. No crashes and zero guard fallbacks across all runs above.
Checklist
CI States
Latest PR Test (Base): ❌ Run #33142874228
Latest PR Test (Extra): ❌ Run #33142885073
Latest PR Test (AMD ROCm 7.2): ❌ Run #33142874151