round 32 qsa flash experiment #
contract #
- owner: qsa_flash in metal/kernels/qsa_prompt.metal; no production files edited.
- hypothesis: four S partial buffers remove two chunk barriers without changing the explicit ((s0+s1)+(s2+s3)) reduction; separately test constant-offset K loads and fixed-loop unrolling.
- preserve: exact fp16 output, softmax sum order, padded cells, cooperative and fallback paths, P alias lifetime. extra static shared memory must remain below 16 kib.
- evaluator: existing qsa_prompt_parity, paired alternating A/B, T=1984, positions 0/2048/8192/30000, layers=3, pairs=7. default parity plus edge checks. baseline is immutable /tmp/sol_r32_qsa/base_qsa_prompt.metal.
- decision: retain only bitwise-identical changes with repeatable net M5 gain (target >=3% in long-context median, no material regression at other positions). exploratory variants followed by full-position confirmation.
- budget: serial short standalone tests only, each timeout <=20s; no server or model benchmarks.
source and execution #
- strata HEAD:
296b59d2f73c8f04a805df29c2fa97655adace78; baseline includes the pre-existing dirty workspace changes (status saved in/tmp/sol_r32_qsa/strata_status.txt). no tracked production source was edited. - host: apple m5, macos 27.0 (26a428), existing
vendor/strata/build-metal/qsa_prompt_paritybinary. kernel source selected withSTRATA_METAL_KERNELS=/tmp/sol_r32_qsa/strata/metal/kernels; safe math default; seed 5 from the existing evaluator. - commands:
qsa_prompt_parity --flash-compare 1984 <pos0> /tmp/sol_r32_qsa/base_qsa_prompt.metal 3 7, plus defaultqsa_prompt_parity. all invocations exited within 3 seconds, below the 10-second budget. - comparison timing is three dispatches per sample, not 12 layers and not a full-model result. baseline-to-baseline check: 66.190 -> 66.122 ms at pos0=8192.
exploratory ablation (pos0=8192) #
| variant | before median ms | after median ms | change |
|---|---|---|---|
| four partial buffers only | 66.299 | 65.770 | -0.80% |
| + pre-added k offsets and two-step k loop unroll | 66.200 | 63.984 | -3.35% |
| + fixed-trip prefetch, softmax, denominator, v-fill unroll | 66.156 | 63.539 | -3.95% |
all three variants: bitwise memcmp pass. raw logs and immutable variant snapshots are in /tmp/sol_r32_qsa/.
candidate confirmation #
| pos0 | before median ms | after median ms | change | bitwise |
|---|---|---|---|---|
| 0 | 28.756 | 27.545 | -4.21% | pass |
| 2048 | 59.734 | 56.475 | -5.45% | pass |
| 8192 | 66.322 | 63.779 | -3.84% | pass |
| 30000 | 109.321 | 112.883 | +3.26%, contaminated | pass |
confirm_8192.log contains a mid-run contention jump in its last two pairs (80–88 ms versus 63–66 ms before); confirm_30000.log is contaminated throughout (99–133 ms). parent confirmed tracks a/c were simultaneously running short gpu parity sweeps and requested handoff now for its own exclusive confirmation. therefore the 30000 timing does not establish either a win or a regression, and final acceptance is pending that exclusive rerun. the exploratory 8192 run and early-position confirmation provide the pre-contention evidence, not an end-to-end claim.
correctness:
- all four 1984-token comparisons: exact fp16 output memcmp pass.
- default suite: all pass, including qsa_flash versus fp32 reference at pos0 0/1984/2050/30000 and fused prep memcmp.
- signed-zero and tiny-cancellation modes:
QSA_FLASH_EDGE=1and=2, each T=1984/pos0=30000/layers=3/pairs=7, both memcmp pass (edge_1.log,edge_2.log). timings contaminated, not used for selection. - forced generic fallback:
COOP_K4=0 ... --flash-compare 96 30000 ... 1 3, memcmp pass (fallback.log). 5.314 -> 5.460 ms under contention; not an m4 performance claim. m4 hardware untested. - two exploratory compile failures were repaired before answering measurements: an over-broad scratch string replacement touched an earlier function, then a duplicated unroll directive. failed logs retained; final diff touches qsa_flash only.
implementation and review #
- four disjoint S stores meet at one barrier, instead of the prior three. reduction remains exactly
((s0+s1)+(s2+s3))*0.0625f. - retain the following barrier before aliasing
sp[1]as P. total static threadgroup memory measured by metal: 7616 -> 13760 bytes, below 16 kib. this does not independently prove unchanged register/occupancy limits. - pre-add d0 + the lane's first k index once per chunk. cooperative layout probe already guarantees the second half repeats at +16. unrolling the two k steps exposes +0/+16/+32/+48 byte offsets; ks8 remains loaded once.
- denominator loop is unrolled without reassociation: sequential
l += x; += y; += z; += wpreserved. no new helper duplicates an existing abstraction; existing cooperative layout conventions reused. - final visual review: scratch
view_diffplus full relevantview_codecontext. no test harness source change needed.
handoff #
- clean patch:
/tmp/sol_r32_qsa/sol_r32_qsa.patch(onlymetal/kernels/qsa_prompt.metal). git -C vendor/strata apply --check /tmp/sol_r32_qsa/sol_r32_qsa.patch: exit 0.- recommend this candidate for parent's exclusive confirmation, not an unconditional 30000-position performance acceptance. no server touched and no full-model benchmark run.
source sha256:
4602c462fa5096f3ee9e65b03460c5b441db3f4666c573fe0b30a5ffbf0f2971 base_qsa_prompt.metal
9c141a1b65d5c2fa8bbff84027cd8757ed85cc1d72e80143cc94fe7255414e26 barrier_const_unroll.metal
3217640e820225cf57f9e03d05c6d58284c86ac8a23c7396597bb628bc2424e2 qsa_prompt_parity