perf(gqa): template the sliding window out of the no-window v5 prefill path (-494 ms TTFT) - #623
Open
BoarQing wants to merge 1 commit into
Open
Conversation
The window was a runtime argument, so its per-row lower bound and the all-masked-row guard stayed live in every instantiation. In a kernel already at the VGPR ceiling that pushed M_TILES=2 BKV=32 D=64 from 120 to 180 bytes/lane of scratch (38 -> 68 spills) and cost the full-attention path 21% at deep KV, even though no window was configured. Making it a template parameter returns every no-window instantiation to byte-identical resource usage with the pre-window kernel and recovers the regression; the windowed instantiations are unchanged. Only D=64 gets a windowed instantiation, since hip_gqa_flash_prefill_v3 declines a window at other head dims. Co-authored-by: Cursor <cursoragent@cursor.com>
BoarQing
requested review from
amd-bqiao,
amd-mingw,
edelaye,
fhanuman,
qianglin-amd and
wcy123
as code owners
August 1, 2026 06:54
|
Thanks for opening a PR! This project follows LLVM's incremental-development and AI-tool-use Before requesting review, please check that:
Reviewers are assigned through |
L2 Accuracy Results (EP vs CPU)
Threshold: 0.01 | Run: 3528 - Commit: |
MorphiZen EP Performance Results
EPContext Export Performance
EPContext Import Performance
OGA Benchmark Results
OGA Wheel Smoke (Python benchmark_e2e.py)
Run: 3528 - Commit: |
Contributor
Author
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.
Stacked on #622 (base is
perf/gqa-flash-prefill-window), so the diff here is one commit against one file.The problem
#622 added the sliding window as a runtime argument. Its per-row state — the
kv_lolower bound, the all-masked-rowemptyguard and the selects it drives — therefore stays live in every instantiation, whether or not a window is configured.gqa_flash_prefill_v5_kernelalready runs at the VGPR ceiling, so in the instantiation the autotuner picks for gpt-oss that state spills:M_TILES=2 BKV=32 D=64The cost lands on the full-attention path, which gained nothing from the window feature.
-Rpass-analysis=kernel-resource-usage, gfx1151.The change
bool HAS_WINDOWbecomes a fourth template parameter, and the three window sites go underif constexpr: the start-tile clamp, the per-rowkv_lomask in the innermost loop, and the all-masked-row short-circuit.local_window_sizestays a runtime argument, since the windowed instantiation still needs its value; only the branches become compile-time.dispatchPrefillV5selects onlocal_window_size > 0. OnlyD == 64gets a windowed instantiation, becausehip_gqa_flash_prefill_v3declines a window at any other head dim, so aD=128windowed variant would be dead code. That puts the instantiation count at 12 rather than 16.No autotuner change is needed: the cache key already carries
_w<window>, so each instantiation tunes itself.Results
Every
HAS_WINDOW=falseinstantiation is byte-identical to the pre-window kernel — the goal was parity, not improvement:M2 B32 D64scratch B/laneM2 B32 D64VGPR spillsM1 B32 D64VGPRs / occupancyThe
HAS_WINDOW=trueinstantiations reproduce #622's numbers exactly, as expected: their branches become unconditional rather than removed.Standalone prefill harness, interleaved arms, 4 rounds, only
custom_kernels_gfx1151.dlldiffering (ms):gpt_oss-sink sq512 past8192(full attention, deep KV)gpt_oss-sink sq512 past512llama-3.2-1b sq2048llama-3.1-8b sq2048(D=128, control)gpt_oss-win sq512 past8192 w128(windowed)gpt-oss-20b at seqlen 16384, interleaved TTFT, 3 rounds — every round of this PR is below every round of #622, against a ~52 ms spread:
RGP capture, chunk 16, layer 1. The two layer types now dispatch different instantiations, so they separate by name rather than by duration:
Known trade-off
One shape moves the other way. On
gpt_oss-20b sq2048 past0atM_TILES=2 BKV=32, #622's spilled codegen is faster — 3.29 ms against 3.74 pre-window and 3.77 here (medians of 5 interleaved autotuner sweeps). It reproduces, and it does not appear in the other three no-window shapes, where #622 is 20-30% slower at that config. Restoring the pre-window codegen restores its weakness on that shape along with its strengths. Net TTFT still improves by 494 ms, but the ~13% there is unexplained and worth a look on its own.Related: the regression was partly masked by the autotuner. On #622 the degraded
M_TILES=2 BKV=32loses toM_TILES=1 BKV=32on three of four no-window shapes, so end-to-end timings understated the per-config damage.Test plan
test_gqa_prefillstandalone harness, 23 cases, ALL PASS with everyrelL2bit-identical to perf(gqa): support sliding windows in the fused flash prefill kernel (-65.7% TTFT cumulative) #622. Removing theemptyguard from the no-window path is a behavioural change on paper, so the cases that caught the original NaN were checked specifically:window=1,window > sequence, andpast_len 8192.All measurements on gfx1151 (Radeon 8060S / Strix Halo), ROCm 7.1.