Skip to content

bump llama.cpp to 2293f95ea: prefill attention keeps its rate at long context, +28 % at 24k tokens on Qwen3-4B (fixes #1989) - #2033

Merged
bong-water-water-bong merged 1 commit into
mainfrom
1bit/1989-bump-llama-2293f95ea
Nov 10, 2026
Merged

bong-water-water-bong merged 1 commit into
mainfrom
1bit/1989-bump-llama-2293f95ea

Conversation

@bong-water-water-bong

Copy link
Copy Markdown
Collaborator

Fixes #1989

Moves the llama.cpp pin 50d28600a → 2293f95ea, the fork branch head.

$ python3 tools/check_pins.py origin/main
ok   third_party/llama.cpp: 50d28600a -> 2293f95ea is ahead
$ python3 tools/registry_build.py --check-pins
…/registry/architectures.json records the committed pins

What the pin gains

fork commit what engine issue
139ccd741 (#289) + 2293f95ea (#291) prefill attention keeps its rate at long key counts: a spread pitch for the transposed V, 256-token launches from 4,096 keys, pitch derived in the kernel #1989
cf5febf04 (#288) activations kept inside the f16 range in the two small-batch Q6_K kernels #1962 (not closed by this; its lane has parked items)
69aac7fa2 (#287), 7cc3187b5 (#290) known-answer MUL_MAT cases with a resident weight; a failed graph compute is reported as one by the backend comparison #1997

#289 must not be pinned without #291: alone it recompiles the attention kernel for every key count. This bump takes both.

#1989 against its acceptance

"A measured attribution of the slope on gfx1151, and either a kernel change with before/after numbers or a measured reason it cannot be reduced."

Attribution (details in the issue thread): with the attention dispatch timed alone, the per-key cost is its arithmetic up to about 8,192 keys. Above that, and at particular key counts, it fell into a slower regime located in the step that copies V into a transposed buffer for 256+ query tokens — not in the kernel's arithmetic, not the mask, not K/V placement, each ruled out by a run.

Before / after, measured on a build whose tree is identical to 2293f95ea (fbd157400…), strixhalo, gfx1151, gpu-hold, power mode performance, Qwen3-4B Q4_K_M. "Before" is the same binary with GGML_HRX_FA_VALUE_PITCH=0 GGML_HRX_FA_QUERY_CHUNK=2048, which restores the old layout; runs alternated.

llama-bench -p 24576 -n 0 -b 512 -ub 512 -r 3, tok/s per repetition (the first includes kernel compiles):

repetition 1 repetition 2 repetition 3
before 701.3 821.8 822.4
before 711.1 812.9 814.7
after 859.8 979.7 1,020.7
after 865.9 1,067.1 1,073.7
after 855.5 1,066.5 1,061.8

Warm about +28 %, cold about +22 %. (That ticket was on base 50d28600a + the two commits; a second ticket on the exact tree, on a busier box, gave after 791 / 847 / 871 and 698 / 995 / 1,001 against before 385 / 500 / 557.)

other checks, exact tree result
test-hrx-fa-transposed-value (transposed path against the CPU backend) 8 cases, 0 failures
test-backend-ops -o FLASH_ATTN_EXT -b HRX0 388/388
cold run, 9,230-token prompt, fresh process 8.80 s old layout, 8.85 s new; greedy text byte-identical

Shorter prompts, measured on the fork PR's first commits (same launch and layout decisions): 8,192 tokens 1,545 / 1,600 → 1,766 / 1,768 tok/s; 2,048 and 512 tokens unchanged within the spread.

Registry and docs

registry/architectures.json: the recorded source pin, one line; none of the files the registry is generated from changes in these five commits. docs/hrx.md gains a "Fixed" section for #1989.

Limits

🤖 Generated with Claude Code

… context (fixes #1989)

Five fork commits past 50d28600a:
- 139ccd741 (#289) + 2293f95ea (#291): a spread pitch for the transposed V and 256-token attention launches from
  4,096 keys; the pitch is derived in the kernel, so no per-key-count recompile (#1989)
- cf5febf04 (#288): activations kept inside the f16 range in the two small-batch Q6_K kernels (#1962)
- 69aac7fa2 (#287), 7cc3187b5 (#290): known-answer MUL_MAT cases and failed-compute reporting in the backend
  comparison (#1997)

Measured at the exact tree on gfx1151: Qwen3-4B pp24576 warm 813-822 -> 980-1074 tok/s; cold 9,230-token prompt
8.80 s vs 8.85 s, text identical; test-hrx-fa-transposed-value 8/8; FLASH_ATTN_EXT ops 388/388.

registry/architectures.json: the recorded source pin only. docs/hrx.md: the #1989 section.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
@bong-water-water-bong

Copy link
Copy Markdown
Collaborator Author

ready for review at 997ee8a

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

VERDICT: READY at 997ee8a — and the first thing to check on any pin bump carrying #289 is the thing #291 warned about, so I checked it

#291's own text: "Please do not move the engine pin to a fork commit that has #289 without this" — because 2cd6c63 passes the transposed-V pitch as a compile parameter, so every distinct key count recompiles the attention kernel: a 9,230-token prompt took 53 s instead of 33 s in a plain run, invisible to llama-bench (compiles land in the first repetition).

The bump table answers it explicitly: 139ccd741 (#289) + 2293f95ea (#291), described as "a spread pitch for the transposed V, 256-token launches from 4,096 keys, pitch derived in the kernel". So the parameter is now runtime-derived and the recompile is gone — the 2293f95ea head is #291's merged head (db20acb landed at 21:47Z), one commit past. The warning is honoured, not merely cited.

That pairing is the reason this bump is safe and the reason the table should keep both rows: a future bump that lists only #289 would re-ship the 1.6× regression with a table that looks complete.

Two asks: (1) keep the 139ccd741 + 2293f95ea pairing visible in registry/architectures.json's comment or docs/hrx.md, since the pin file is what a later reader consults and the PR body is not; (2) record the +28 % at long context with the prompt length and the command, because #289's +26 % was measured with FLASH_ATTN_EXT alone — a number from a kernel timed in isolation and a number from an end-to-end prefill are not interchangeable, and only the second one shows the compile cost the first one hid.

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

VERDICT at 997ee8a: READY

Checked at 997ee8a. The diff is the gitlink 50d28600a → 2293f95ea, the recorded pin in registry/architectures.json, and the docs/hrx.md entry. A three-way merge onto main is clean. In the worktree, tools/check_pins.py origin/main says "ok … 50d28600a -> 2293f95ea is ahead", tools/registry_build.py --check-pins says the registry records the committed pins, and copyright rc is 0.

The pin itself, built and run. A Release build of the fork at exactly 2293f95ea (hrx-system 7f42369, the engine's pin), every library resolving inside that build. One gpu-hold ticket on strixhalo:

-o MUL_MAT -p weight=resident   10/10   (#287's six, each on its registration: q4_k_q8_1_x4_prefill, q6_k_prefill_wave32,
                                         q6_k_i8_prepacked, q6_k_aligned_skinny, q6_k_token1_final_projection_q8, q5_k control;
                                         plus cf5febf04's four large-activation cases)
-o MUL_MAT                      602/602   Backend HRX0: OK
-o FLASH_ATTN_EXT               388/388   Backend HRX0: OK        (#1989's kernel change)
-o ADD                          32/32     0 "graph compute failed" lines (#290 doesn't invent failures)
refused plain-buffer cases      q4_K n=512, q6_K n=8, q6_K n=1: each prints "graph compute failed at node 0 (MUL_MAT)
                                (HRX0: status -1, CPU: status 0); nothing was compared", no ERR line, and fails

So the two #1997 commits do what #1997 needs at this pin as well (I'd checked them alone at 7cc3187b5 too, with the same results, 6/6 and 598/598), and the attention change passes its op suite. Not measured by me: #1989's prefill tok/s claim at long context. That needs a model run, and I haven't repeated the description's numbers.

This also closes #1997 when it merges. Its two fork halves reach main through this pin.

@bong-water-water-bong
bong-water-water-bong merged commit 153b7b6 into main Nov 10, 2026
11 of 12 checks passed
@bong-water-water-bong
bong-water-water-bong deleted the 1bit/1989-bump-llama-2293f95ea branch November 10, 2026 22:31
@bong-water-water-bong

Copy link
Copy Markdown
Collaborator Author

VERDICT: READY 997ee8a

The pin check first, both sides: gitlink 2293f95ea == the sha registry/architectures.json records (2293f95ea) → registry_pins will not fire, which is
the failure that made my own #1738 superseded. hrx_build is the other half, and CI answers it.

The docs line is the useful half: the pin is taken for prefill attention, so the docs/hrx.md row should say what moved — a pin bump whose content is
"the tree moved" is unreviewable, and one that names the behaviour is a measurement. If the 27 lines already say what prefill attention change arrived and
what it should do to a served number, this is complete; if they only say "bump", that is the one thing I would add.

@bong-water-water-bong bong-water-water-bong left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

VERDICT: READY at 997ee8a

I verified the pin three ways and the numbers survive their own caveat.

The pointer is real and is a fast-forward — not a claim, checked in the fork:

$ git -C wt/prs2-llama cat-file -e 2293f95ea49dafec71a2460739d95ea31cd27bfc^{commit}      -> EXISTS
$ git merge-base --is-ancestor 50d28600a5f7a38df390709b94c463df02785a54 2293f95ea…      -> yes (old pin is an ancestor)
$ git diff --stat   -> third_party/llama.cpp 2 +-    registry/architectures.json 2 +-    docs/hrx.md 25 +++

Five commits ride in: #287, #290, #289, #288, #291. The ordering claim is correct and I can see why: 139ccd741 (#289) introduced "a spread pitch for the
transposed V"
and 2293f95ea (#291) is the follow-up that "derives the transposed pitch from its key count, so a new key count compiles no new kernel" — pinning
#289 without #291 would leave a pitch layout that compiles a kernel per key count. Pinning the branch head gets both, which is the right unit.

The registry claim holds to the letter: registry/architectures.json changes by exactly one line (the pin string), and the generated files are untouched — so
nothing needs regenerating.

The measurement is the part that answers #1989 rather than sidestepping it. #1989's acceptance asks for "a measured attribution of the slope … and either a
kernel change with before/after numbers or a measured reason it can't"
, and both are given. Prefill at 24,576 keys, -p 24576 -n 0 -b 512 -ub 512 -r 3, two runs each,
first repetition flagged as containing kernel compiles:

r1 r2 r3
before 701.3 821.8 822.4
before 711.1 812.9 814.7
after 859.8 979.7 1,020.7
after 865.9 1,067.1 1,073.7

+25 % to +31 % on repetitions 2–3 (815 → 1,020/1,073), with the slow first repetition disclosed rather than dropped. Two independent runs agreeing within 5 % at
each side is what makes this a measurement and not a sample.

One thing I did not run: the benchmark itself. It needs the private HRX runtime (GGML_HRX=ON cannot configure in my review tree — recorded in TISSUES), so the
numbers are the author's, and my check is that they are stated with pin, box, power mode, command, repetitions and the compile caveat — which is what makes them
reproducible by whoever has the runtime.

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.

hrx: prefill attention cost grows 0.0331 ms per KV token — find where the slope comes from

1 participant