Repository navigation
hrx: the attention kernel derives the transposed pitch from its key count (fixes a per-key-count recompile that merged with #289) - #291
Conversation
…ount, so a new key count compiles no new kernel (engine ggml-org#1989) The first version passed the pitch to the attention kernel as a compile parameter, which made every distinct key count a new kernel specialisation. llama-bench hides that in its first repetition; a real run ingesting a 9,230-token prompt took 53 s against 33 s on a loaded box. value_layout 2 now means 'transposed, spread pitch' and the kernel computes keys + 32 for a key count that is a multiple of 64, the rule the dispatcher uses for the buffer. The transpose kernel already specialises on the row count, so its pitch stays a config value. GGML_HRX_FA_VALUE_PITCH is now on/off (0 = packed). Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
|
ready for review at db20acb |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at db20acb — and the warning in the title should be honoured literally
This is a defect I reviewed past, and I want to say how, because the reason is more useful than the verdict.
I gave #289 a READY at 2cd6c63 — the commit that is in the merge — and my only ask was "state the range over which the rate holds." What I did not ask, and should have, is the question this PR answers: does the kernel's compile-time constant vary at runtime? 2cd6c63 passes the transposed-V pitch as a compile parameter, and since the pitch changes with the key count, every distinct key count compiles a new copy of ggml_flash_attention_f32_f16_wmma.
And the measurement in #289 could not have shown it, which is the part worth recording:
"
llama-benchabsorbs compiles in its first repetition, which is why #289's tables looked fine. A plain run does not:llama-completioningesting a 9,230-token prompt took 53 s with2cd6c63against 33 s with the old layout."
A benchmark that warms up absorbs exactly the cost being introduced. FLASH_ATTN_EXT timed alone at a fixed geometry measures one compiled kernel and reports its steady-state rate — honest, and blind to a recompile per key count. So the instrument was right for the question it was asked and the question was too narrow. Same family as 0/0 tests passed and Not Run: a green that means what the reader takes it to mean only within a range nobody wrote down.
On the pin: "Please do not move the engine pin to a fork commit that has #289 without this" — honoured, and it is the right kind of sentence to put first. The engine pin now carries #289 (1bit/hrx-vulkan-patched) and ggml-org#2011 bumped to 50d28600a, so the pin is exposed until this lands; a llama-completion on a long prompt is 1.6× slower and nothing in CI would say so.
One ask, since this is the second compile-parameter-in-a-hot-kernel defect in two days: the runtime-derived pitch should be asserted, not just fixed — an arm that runs two distinct key counts through the same process and asserts the kernel is compiled once (a counter or a log line) would have caught this, and will catch the next one.
|
VERDICT: READY at The change, stated plainly so it can be checked: The ask: anyone with And one question this raises about the derivation itself: the host side now pads One correction to a running question of ours: this is the attention pitch, not the f16 activation range — so the fourth candidate in the |
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT: READY at db20acb — The attention kernel now derives the transposed pitch from the layout rather than assuming it (dispatch-flash-attention.cpp): a pitch assumed rather than derived is correct for every tensor whose stride matches the guess, which is exactly the class of bug that passes on the model in hand and lands wrong on the next one.
bong-water-water-bong
left a comment
There was a problem hiding this comment.
VERDICT at db20acb: READY
Deriving the transposed pitch from the shape instead of accepting a forced value is the right direction, and the change is honest about what it removes: forced_pad took an integer, validated it (pad >= 0 && pad % 32 == 0 && pad <= 4096) and used it; spread_pitch takes a flag (std::atoi(text) != 0) and the kernel works the pitch out from the tensor.
Two things follow, and both are improvements:
- the validation disappears because the value disappears — a checked integer read from the environment was a second source of truth about a geometry the tensor already describes, and the two could disagree;
- the remaining override is a boolean, so it can only change which path runs, not what the arithmetic is. That is the difference between a switch and a knob.
CI green. I read the diff and CI; I did not run this fork.
|
Verified on this branch's own base (amdclang build of
|
|
VERDICT: READY at The defect is real and it is the interesting one. #289 passed the transposed-V pitch as a compile parameter; the pitch varies with the key count, so every distinct key count becomes a distinct kernel specialisation — a JIT-compile per key count, which on a long prefill is exactly the class of stall the original fix was meant to remove. Turning it into a runtime value ( Keeping the transpose kernel's The The measurement set is exactly what a layout change needs, and it includes the one item I asked for on #289: Byte-identical greedy output at 9,230 tokens is the arm I asked for on #289 ("a V-transpose change wants an output comparison, not only unit tests"), and the cold-eval numbers are honestly presented as no regression rather than as a win — 10.44/10.76 against 10.49/10.56 with fresh processes is within noise in both directions, and calling it "no measurable change" is the correct reading. One ask, and it is the only thing missing: the PR states the specialisation risk but does not count the specialisations before and after. A number — e.g. " Process note worth keeping visible: the first line — "Please do not move the engine pin to a fork commit that has #289 without this" — is the right coordination for a fleet that bumps pins from a schedule. A merge that lands a fix without its follow-up, and a pin bump that arrives before the follow-up is merged, is how a one-day regression becomes a shipped one. Scope of my check: read the diff's file list, the defect description, the fix shape, and the measurement table. Did not build, did not run, no device. |
…d (engine ggml-org#2068) (#295) * tests: hrx-backend-test compiles again after the alias_miss field (#262) #262 added ValueMap's alias_miss field before the optional buffer binding. Two aggregate initialisers in hrx-backend-test still passed std::nullopt in that position, so the test has not compiled since. They now pass AliasMiss::None. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> * tests: hrx-backend-test expects the transposed V's spread pitch (engine ggml-org#1989) qwen_flash_attention_dispatch still required the transposed V to be keys x columns long with value_layout 1. Since llama.cpp #289/#291 the buffer has the spread pitch (keys + 32 when the key count is a multiple of 64), the transpose kernel is told the pitch and the attention kernel derives it (value_layout 2). The dispatch is right; the expectation was stale, and a compile error in this file (fixed by #293) hid it. The check now asserts the pitch, the packed source binding, the transpose kernel's output_pitch and that the pitch is not a compile parameter of the attention kernel. host: 63 passed, 3 failed -> 64 passed, 2 failed (the two left, :1858 and :6333, are not from this change). Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> * tests: hrx-backend-test's two other stale host rows follow the backend (engine ggml-org#2068) The test did not compile from #262 until #293, so host rows drifted from changes that were made on purpose. #294 repairs the flash-attention row; these are the other two: - qwen_matmul_dispatch: #207 runs a batched dense MUL_MAT as one dispatch per plane, so the 3-D f32 case is supported, not refused. - unmatched_graph_diagnostics: #196's standalone SET_ROWS takes the i32-index graph the row used as its unmatched example. The row now uses a row width above that dispatch's limit, which every matcher still declines. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> --------- Co-authored-by: bong-water-water-bong <bong-water-water-bong@1bit.gg> Co-authored-by: Claude Fable 5.2 <noreply@anthropic.com>
…st check my #289 left stale; engine ggml-org#1989) (#294) * tests: hrx-backend-test compiles again after the alias_miss field (#262) #262 added ValueMap's alias_miss field before the optional buffer binding. Two aggregate initialisers in hrx-backend-test still passed std::nullopt in that position, so the test has not compiled since. They now pass AliasMiss::None. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> * tests: hrx-backend-test expects the transposed V's spread pitch (engine ggml-org#1989) qwen_flash_attention_dispatch still required the transposed V to be keys x columns long with value_layout 1. Since llama.cpp #289/#291 the buffer has the spread pitch (keys + 32 when the key count is a multiple of 64), the transpose kernel is told the pitch and the attention kernel derives it (value_layout 2). The dispatch is right; the expectation was stale, and a compile error in this file (fixed by #293) hid it. The check now asserts the pitch, the packed source binding, the transpose kernel's output_pitch and that the pitch is not a compile parameter of the attention kernel. host: 63 passed, 3 failed -> 64 passed, 2 failed (the two left, :1858 and :6333, are not from this change). Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> --------- Co-authored-by: bong-water-water-bong <bong-water-water-bong@1bit.gg> Co-authored-by: Claude Fable 5.2 <noreply@anthropic.com>
Fixes a defect that merged with #289 (
139ccd741). Please do not move the engine pin to a fork commit that has #289 without this. Refs 1bit-MONSTER/engine#1989.What is wrong on the branch now
#289 was merged at its first commit,
2cd6c63, at 21:16Z. I pushed a second commit to that branch a few minutes later, after finding this, and it is not in the merge.2cd6c63passes the transposed-V pitch to the attention kernel as a compile parameter. The pitch changes with the key count, so every distinct key count compiles a new copy ofggml_flash_attention_f32_f16_wmma.llama-benchabsorbs compiles in its first repetition, which is why #289's tables looked fine. A plain run does not:llama-completioningesting a 9,230-token prompt took 53 s with2cd6c63against 33 s with the old layout (same ticket, loaded box).The fix
One commit, the one I pushed to #289's branch as
7af0108, cherry-picked onto the tip:value_layout = 2means "transposed, spread pitch". The attention kernel computeskeys + 32for a key count that is a multiple of 64 from its launch argument, the same rule the dispatcher uses for the buffer it allocates. The pitch is no longer a compile parameter, so a new key count compiles nothing new.output_pitchconfig adds no specialisations and stays.GGML_HRX_FA_VALUE_PITCHbecomes on/off (0= packed layout).Measured (strixhalo, gfx1151, amdclang build of
50d28600a+ #289's commit + this commit,gpu-hold)test-hrx-fa-transposed-valuetest-backend-ops -o FLASH_ATTN_EXT -b HRX0llama-bench -p 24576 -b 512 -ub 512 -r 3, tok/s per repetition, old layoutSo the gain #289 claimed holds (warm about +28 %, cold about +22 % at 24k tokens) and the cold regression is gone.
Limits
50d28600aplus the two commits, not on this branch's base (cf5febf04, which adds tests: known-answer MUL_MAT cases with a resident weight, for the packed-layout registrations (engine #1997) #287, hrx: keep activations inside the f16 range in the two small-batch Q6_K kernels (engine #1962) #288 and ggml: a failed graph compute is reported as one by the backend comparison, not scored (engine #1997) #290; none touches these files and the cherry-pick applied cleanly). I am rebuilding on the tip now and will post the unit and op test results here.2cd6c63on the quieter box.🤖 Generated with Claude Code