# round 27 track b — qsa prep and merge

## decision

ship parallel aligned `float2 (m,l)` loads, threadgroup-cached `l`, and lane-strided `simd_max` in `qsa_merge2` and `qsa_merge`. keep the serial `L` and output FMA chains, all three merge barriers, and buffer layouts unchanged. `qsa_merge2` is the production decode path.

do **not** ship raw-row single-barrier `norm_rope` fusion or reduction unroll: bitwise-correct, but confirmation found no useful speedup. `qsa_score` already fully unrolls its four-value dot products; `qsa_attn2` already uses `char4` key loads and fully unrolls the eight-value and six-head inner loops. neither changed without evidence.

## artifacts and provenance

- patch: `/tmp/sol_r27_qsa/sol_r27_qsa.patch`
- isolated candidate: `/tmp/sol_r27_qsa/strata/`
- immutable baseline kernels: `/tmp/sol_r27_qsa/baseline/`
- rejected fusion source: `/tmp/sol_r27_qsa/qsa-with-prep-fusion.metal`
- exact source hashes: `/tmp/sol_r27_qsa/manifest.json`
- vendor head: `296b59d2f73c8f04a805df29c2fa97655adace78` (baseline kernel hashes, not head alone, identify measured working-tree inputs)
- all raw logs: `/tmp/sol_r27_qsa/*.log`

only the three owned files occur in the patch. no tracked vendor file edited, no server calls or full-model benchmarks. `git diff --check` and `git -C vendor/strata apply --check /tmp/sol_r27_qsa/sol_r27_qsa.patch` both exit 0.

## evaluation

apple m5; runtime library `vendor/strata/build-metal/libmtl_rt.a`; clang++ c++20 -O2. kernel runtime defaults retained. alternating A/B timing order, median nine paired rounds, batches of 32 existing bitwise dispatches or 16 new full-window dispatches. large prep uses one dispatch per batch. gpu clock/load clearly varied across invocations; use within-pair ratios, not absolute times across runs. concurrent live-server load was not controlled.

build:

```
clang++ -std=c++20 -O2 -Ivendor/strata/metal/src   /tmp/sol_r27_qsa/strata/metal/tests/qsa_parity.cpp   vendor/strata/build-metal/libmtl_rt.a -framework Metal -framework Foundation   -o /tmp/sol_r27_qsa/qsa_parity
```

set `STRATA_METAL_KERNELS=/tmp/sol_r27_qsa/strata/metal/kernels` for every invocation. existing `--bitwise /tmp/sol_r27_qsa/qsa.metal.baseline` tests v1; add `QSA_ATTN=2` and use `qsa2.metal.baseline` for v2. new `--paired /tmp/sol_r27_qsa/baseline P --tokens N` compares every buffer byte and times prep/score/topk/attn/merge separately. `--prep-only` avoids allocating enormous attention partial buffers for prefill; full-window tokens capped at 128, prep-only at 2048.

## correctness

- final v1: 40/40 random-KV `(position, ntok)` cases, full `part` and `xp` memcmp equal; positions 0,1,63,64,512,2047,2048,2050,8192,30000; tokens 1,2,4,8.
- final v2: identical 40/40 matrix, full `part` and `xp` memcmp equal.
- final full-window seeded-input tests: `(P,N,attn,mrope)` = `(0,1,1,0)`, `(3,3,1,0)`, `(2047,8,1,0)`, `(2050,8,2,0)`, `(3000,8,2,1)`. all 13 complete buffers pass: `qcur qidx kq vq ksc vsc tail dead pooled scores ids part xp`. identical nonzero seeded history for tail/dead/pooled, other outputs initialized identically. prior KV history is zero in this mode; existing bitwise mode supplies random KV codes/scales/selection instead.
- prep-only T=2048 full-buffer comparison passes; the untouched attention buffers are trivially equal in this mode.
- all final commands exit 0; final bitwise invocations 1.70s/1.63s, final full-window invocations 0.13–0.61s, final prefill 0.25s.

bitwise claims cover tested finite production-style inputs and these specialization modes, not all possible NaNs or all function constants. no summation reassociation: `L` remains serial `fma(l[k], ek[k], L)` in ascending k; output chain unchanged. `(m,l)` alignment follows even `QD=256`, head stride 258 and chunk stride 6192. maxima are parallelized, sums are not.

## measured merge wins

final existing random-KV evaluator, gpu microseconds baseline / candidate:

| kernel | P | tokens | baseline | candidate | latency reduction |
|---|---:|---:|---:|---:|---:|
| merge2 | 512 | 1 | 18.69 | 15.94 | 14.7% |
| merge2 | 512 | 4 | 9.45 | 8.12 | 14.1% |
| merge2 | 2048 | 1 | 12.28 | 9.87 | 19.6% |
| merge2 | 2048 | 4 | 50.52 | 46.22 | 8.5% |
| merge2 | 8192 | 1 | 12.25 | 9.82 | 19.8% |
| merge2 | 8192 | 4 | 50.53 | 46.58 | 7.8% |
| merge | 512 | 1 | 13.45 | 11.66 | 13.3% |
| merge | 512 | 4 | 13.90 | 11.70 | 15.8% |
| merge | 2048 | 1 | 7.73 | 5.89 | 23.8% |
| merge | 2048 | 4 | 24.88 | 23.41 | 5.9% |
| merge | 8192 | 1 | 7.57 | 5.87 | 22.5% |
| merge | 8192 | 4 | 24.91 | 23.34 | 6.3% |

initial independent random-KV runs showed the same direction. three additional merge2 full-window confirmations at P=2050,N=8 gave 101.081/90.672, 101.065/90.281, 100.320/90.599 us (~10%). small-context v1 tradeoff: P=3,N=3 final 10.703/10.948 us (~2.3% slower), P=0,N=1 7.023/7.125 (~1.5% slower). these are below the predeclared 3% effect threshold, not a universal speedup claim. score and attention are unchanged; apparent differences there are load/clock noise.

## rejected prep fusion

initial full-window T=2048 timing looked ~33% faster, but this did not survive isolated confirmation. three prep-only confirmations plain: 2919.667/2918.042, 3041.292/3052.083, 3045.125/3048.708 us. mrope: 1591.625/1597.875, 3055.542/3099.292, 2983.917/2993.292. neutral or slightly worse.

parent-requested decode confirmation at P=3000, two independent runs per token count:

| tokens | run 1 baseline/candidate us | run 2 baseline/candidate us |
|---:|---:|---:|
| 1 | 6.880/6.893 | 6.875/6.922 |
| 2 | 8.448/8.508 | 8.477/8.523 |
| 4 | 12.190/12.143 | 12.164/12.156 |
| 8 | 20.018/19.872 | 20.018/19.901 |

no >3% benefit; baseline helper restored. the compiler may already eliminate some barrier/reduction overhead, or added raw-row/gamma loads may offset it; no assembly claim made.

## budget incident / limits

one early full-window T=2048 invocation took 6.11s, exceeding the requested 5s. the old partial allocation scales to multiple gigabytes at that size. parent was notified immediately; added prep-only mode and a full-window token cap. all subsequent invocations were below 5s. no full-model or :8080 use. no full-model end-to-end throughput claim.
