# round 24 track b: qsa prompt offsets and unrolling

## decision

ship **flash-only** 32-bit `ko[8]` / `vo[8]` offsets, fixed eight/four-trip loop unrolls, and `qbase` hoisting. reject the attention-mm changes (repeatable long-T regressions), unroll-only changes (noise), and score-mm changes (no robust >2% gain).

patch: `/tmp/sol_r24_qsa/sol_r24_qsa.patch`; `git -C vendor/strata apply --check /tmp/sol_r24_qsa/sol_r24_qsa.patch` passed. only `metal/kernels/qsa_prompt.metal`, inside `qsa_flash`, changes. no tracked vendor files were edited. no test changes were necessary: the existing test already provides paired A/B and full-buffer `memcmp`.

**tradeoff:** this is not a universal speedup. T=1984 first-chunk flash is ~2.35% slower, pos2048 ~0.9% slower, while pos8192 is ~11% faster and pos30000 ~1.2% faster. the equal-case summed per-dispatch median is 3.30% / 3.09% faster in two confirmation passes. this supports a mixed-position net win, not an end-to-end model claim; reject if first-chunk-only latency is the objective.

## contract / provenance

- machine: Apple M5 (cooperative k4 path), local existing `vendor/strata/build-metal/qsa_prompt_parity` executable.
- baseline git HEAD: `296b59d2f73c8f04a805df29c2fa97655adace78`; actual working-tree kernel frozen as `/tmp/sol_r24_qsa/baseline.metal`, sha256 `a36eb1e45221a10a3f41a5528218e561c319067b9787572e5262695031af117f`.
- source snapshots and immutable per-run logs: `/tmp/sol_r24_qsa/{unroll,offset,offset_only,score,flash}.metal`, `/tmp/sol_r24_qsa/records.json`, and named `.log` files. scratch checkout `/tmp/sol_r24_qsa/strata`.
- plan written before runs: `/tmp/sol_r24_qsa/plan.md`. acceptance: all memcmp passes, reproducible >2% summed-median gain and no repeatable >3% case regression. low-T/pos0 timing is inconclusive, not counted as a reliable improvement.
- 113 invocations including baseline self-check; every exit status 0; maximum wall duration 2.094 seconds (below 5-second cap). GPU tests serialized in this session. live server left alone; no full-model runs, no :8080 access. other system/GPU activity was not controlled; substantial clock-state noise appeared in short kernels.
- environment: `STRATA_METAL_KERNELS=/tmp/sol_r24_qsa/strata/metal/kernels`, `QSA_TOPK=1`. compare modes supply deterministic selected ids directly; setting QSA_TOPK does not make them exercise runtime top-k selection.

## final flash confirmation

paired alternating order, seven pairs; each timing is 12 consecutive dispatches for T=96 and three for T=1984. table reports milliseconds **per dispatch**, normalized from those command-buffer medians. two separate passes:

| T | pos0 | pass 1 baseline | candidate | pass 2 baseline | candidate |
|---:|---:|---:|---:|---:|---:|
|96|0|0.223917|0.216000|0.053667|0.058667|
|96|2048|1.024167|1.023500|1.025250|1.027167|
|96|8192|1.170083|1.092000|1.165417|1.093000|
|96|30000|1.455667|1.390333|1.452083|1.386583|
|1984|0|9.498667|9.723667|9.523333|9.746667|
|1984|2048|19.892667|20.055667|19.902667|20.090667|
|1984|8192|24.948333|22.154667|24.835667|22.175667|
|1984|30000|30.197333|29.837333|30.233333|29.889667|

T96/pos0 swings with clocks; one pass improves 3.5%, another regresses 9.3% on only ~5 microseconds per dispatch. that is not a repeatable case regression. the large-T results are stable across screening and both confirmations. no shape-specific branches were added to fit these measurements.

reproduce:

```
STRATA_METAL_KERNELS=/tmp/sol_r24_qsa/strata/metal/kernels QSA_TOPK=1 \
 vendor/strata/build-metal/qsa_prompt_parity --flash-compare 1984 8192 \
 /tmp/sol_r24_qsa/baseline.metal 3 7
```

use layers 12 at T96; repeat positions 0/2048/8192/30000. `long_flash_*` and `repeat_flash_*` preserve every pair, not just selected medians.

## ablations

- unroll + qbase, without offsets: all 16 attention/flash memcmp checks passed; T1984 flash differences -0.2% to +0.5%, attention-mm 0% to +1.1%. not a useful independent win.
- offsets + unroll: all 16 checks passed. T1984 flash pos8192 24.790 -> 22.173 ms; attention-mm T1984 regressed ~0.7–3.0%, so attention edits were removed.
- offsets only: all 16 checks passed; T1984 flash pos8192 24.795 -> 22.206 ms; pos0 regressed 2.9%. offsets explain nearly all the flash gain; no claim is made that unroll changes register allocation. no ISA/register-count measurements were taken.
- score unroll + pointer hoist: all eight cases passed twice. confirmation T1984 positions 0/2048/8192/30000: 0.188375 -> 0.187250, 0.474000 -> 0.472500, 1.356208 -> 1.339667, 4.479375 -> 4.406250 ms. gains 0.3–1.6%, below the threshold and noisy in screening; rejected.

## correctness and scope

all 112 recorded variant invocations plus the baseline check passed `memcmp == 0`. final candidate tested flash output at both T values and all four positions in both confirmations; final unchanged attention partial buffer and score buffer also checked at the full eight-case matrix. signed-zero and tiny-cancellation flash edge modes each passed at T96 and all four positions. full-buffer comparison includes poisoned unwritten attention/score storage, not just numerically active cells. tg memory unchanged (flash 7616 bytes).

only address representation / fixed-trip expansion changes; floating-point expression and reduction order preserved. existing `uint(cid[c])` offsets originate in the same 32-bit cache indexing already used by the kernel; adding dimensions before pointer formation remains valid for its supported context/cache extents. M4/non-k4 fallback code unchanged, but **not run on M4 hardware**. no full model or end-to-end timing was performed.

final review: inspected the entire patch with `view_diff` and surrounding softmax/load code with `view_code`; no shared helper warranted for local address expressions. patch apply check passed.
