# round 15 track b: qsa score and top-k

## decision

keep the causal-future tile exit, score epilogue/dead-load hoists, top-k cached-load bound, and redundant radix barrier removal. keep the original 64×64 score tile: the two-pass 32×64 experiment was byte-identical but slower.

- clean patch: `/tmp/sol_r15_qsa/sol_r15_qsa.patch`
- scratch source: `/tmp/sol_r15_qsa/strata`
- evidence, baseline snapshots, rejected slice32 source, logs, and hashes: `/tmp/sol_r15_qsa/`
- `git -C vendor/strata apply --check /tmp/sol_r15_qsa/sol_r15_qsa.patch`: exit 0.
- only the three assigned files change, and kernel edits stay within the assigned definitions. tracked vendor files were not edited. no server requests, port 8080 interaction, or full-model benchmarks.

## contract and correctness

owner: score/top-k kernels and standalone parity harness. buffer layout, causal tail ownership, reduction arithmetic, tie order, and all output bytes must stay unchanged. fixed rng seed 5; alternate baseline/candidate order. use three-pair exploration and two five-pair confirmation rounds. reject the occupancy variant unless exact parity and repeatable >2% aggregate benefit without substantial regressions. every test process had a 5-second deadline; observed tests finished below one second.

baseline was the dirty working-tree snapshot at task start, not upstream head. repository head was `296b59d2f73c8f04a805df29c2fa97655adace78`; exact per-file baseline/candidate hashes are in `manifest.json`. the harness links the existing `vendor/strata/build-metal/libmtl_rt.a`; kernels are selected exclusively through `STRATA_METAL_KERNELS` pointing into scratch.

why the changes preserve behavior:

- early exit is uniform for the entire threadgroup and occurs before matmul/barrier. if `b0 >= max_bid`, every possible full block in that tile fails the original epilogue predicate. column zero is excluded because it independently writes every token's tail score, including cases with zero full blocks.
- `j` and `b` are invariant per thread; `tt = tid >> 6; tt += 2` visits exactly the original indices. once `t >= n_tok`, later rows are also outside the prompt. `dead[0..127]` is invariant across the four tokens assigned to a simdgroup.
- cached keys are initialized for exactly the block indices later read. the same predicate `b < nb` bounds every consumer; uncached contexts do not read `keys`.
- after reading `sel`, the next pass cannot overwrite `sel` until both the histogram-clear barrier and histogram-complete barrier. the final pass no longer writes `sel` at all. the removed end-of-pass barrier protects no live cross-threadgroup state.

checks, all exit 0:

- all nine requested `(T, pos0)` combinations: `scores` and `qids` full-buffer `memcmp == 0`, including poisoned unused/padding entries, both before and after timing; both confirmation rounds passed.
- boundaries `(T,pos0)`: `(1,0)`, `(2,0)`, `(3,0)`, `(17,0)`, `(31,1)`, `(17,2048)`, `(31,4092)`, `(17,16368)`, `(17,16384)`, `(31,30000)` all byte-identical. these cover tiny tails, partial 16-token tiles, the 1024/4096-block cache thresholds, and both implicit/all-cell and selected paths.
- existing `--topk-compare` with `QSA_TOPK_EDGE=1`, T=96, pos0=2048/4092/16368/16384/30000: all byte-identical, including ties and signed zeros. this is correctness evidence, not a claim of universal edge-workload speedup.
- standalone `qsa_prompt_parity`: `ALL PASS`; repeated after final-source rebuild. existing scalar-vs-matmul score check remains tolerance-based, while the new baseline-vs-candidate comparison is exact.
- standalone `qsa_parity` with `QSA_TOPK=1`: `ALL PASS`; zero of 6153 ids differ against its cpu reference at default context 3000.

## measured performance

apple m5, safe math. gpu command-buffer times; five alternating pairs per stage, one extra stage warmup, median reported. score and top-k measured separately and as a serial dependency chain with explicit buffer barriers. no model throughput claim. absolute times vary substantially between processes (especially small-T cold clocks); use within-process paired comparisons, not times from different rounds. the live server was left untouched, so these are short local measurements rather than an exclusive-machine throughput result.

confirmation round 2, milliseconds, baseline → candidate:

| T | pos0 | score | top-k | combined | combined reduction |
|---:|---:|---:|---:|---:|---:|
| 512 | 2048 | 0.112625 → 0.100750 | 0.121417 → 0.108750 | 0.230625 → 0.213000 | 7.64% |
| 512 | 8192 | 0.328458 → 0.316000 | 0.241750 → 0.236042 | 0.606208 → 0.586792 | 3.20% |
| 512 | 30000 | 1.158875 → 1.137333 | 0.518375 → 0.511000 | 1.645667 → 1.633125 | 0.76% |
| 1024 | 2048 | 0.255500 → 0.210417 | 0.237708 → 0.217333 | 0.484625 → 0.428167 | 11.65% |
| 1024 | 8192 | 0.693750 → 0.660542 | 0.437583 → 0.431625 | 1.132292 → 1.081792 | 4.46% |
| 1024 | 30000 | 2.280583 → 2.229125 | 1.018208 → 1.005417 | 3.318250 → 3.238167 | 2.41% |
| 1984 | 2048 | 0.587875 → 0.473583 | 0.472375 → 0.439625 | 1.054500 → 0.906125 | 14.07% |
| 1984 | 8192 | 1.450042 → 1.317000 | 0.861500 → 0.846000 | 2.319000 → 2.172250 | 6.33% |
| 1984 | 30000 | 4.529917 → 4.374208 | 1.975167 → 1.950250 | 6.511333 → 6.351500 | 2.45% |

across both confirmation rounds (18 cases), score median reduction was 1.69–20.64%, top-k 1.02–10.43%, combined 0.76–14.67%. geometric mean of paired case-median ratios gives 8.23%, 3.66%, and 6.25% reductions respectively; these are descriptive aggregates, not confidence intervals. both rounds improve every tested ordinary case, but sub-percent high-context combined gains should be treated as small/noisy. see `confirm{1,2}_*.log` for every sample. final-source smoke checks repeat the default parity and both T=1984 endpoint contexts successfully.

## rejected occupancy experiment

source snapshot: `/tmp/sol_r15_qsa/slice32/`; evidence: `slice32_*.log`. the variant executes two 32-row matmuls with an 8192-byte shared output tile and a barrier before reuse. all nine cases preserve exact scores and ids. five-pair score comparisons against the optimized 16384-byte 64-row candidate lose 1.1–8.5% across all nine cases, so it is not in the patch. preliminary occupancy combined-stage timings used an earlier harness without explicit dispatch barriers; those combined timings were discarded. its isolated score timings are unaffected.

## reproduction

```text
clang++ -std=c++20 -O2 -I/tmp/sol_r15_qsa/strata/metal/src \
  /tmp/sol_r15_qsa/strata/metal/tests/qsa_prompt_parity.cpp \
  vendor/strata/build-metal/libmtl_rt.a -framework Metal -framework Foundation \
  -o /tmp/sol_r15_qsa/qsa_prompt_parity

STRATA_METAL_KERNELS=/tmp/sol_r15_qsa/strata/metal/kernels \
  /tmp/sol_r15_qsa/qsa_prompt_parity --score-compare 1984 2048 \
  /tmp/sol_r15_qsa/baseline/qsa_prompt.metal /tmp/sol_r15_qsa/baseline/qsa.metal 5
```

repeat the latter invocation individually for T=512/1024/1984 and pos0=2048/8192/30000, with a 5-second process deadline. run no-argument parity with the same environment; run `vendor/strata/build-metal/qsa_parity` with that environment and `QSA_TOPK=1`.

setup repairs: the first baseline-only harness invocation exited 2 because the snapshot lacked `mm_bench.metal`, which the existing cooperative-matmul probe needs; copied the remaining scratch kernel helpers and reran successfully (`baseline_repaired.log`). the first 32-row compile exited 3 because `half` is a reserved metal type; renamed the loop variable to `tile` and reran. neither failed run supplied performance evidence. the final harness adds explicit chain barriers, stage warmups, and a post-timing full-buffer parity check; all confirmation results use that evaluator.
