Round 36 Track C: qsa_prep Single-Barrier norm_rope & w == 128 tg_sum + qsa_score_mm Loop Unrolling #
Summary & Decision #
- Owner:
argon-r36-qsa-prep-score-and-gdn - Owned files:
metal/kernels/qsa.metal,metal/kernels/qsa2.metal,metal/kernels/qsa_prompt.metal,metal/tests/qsa_parity.cpp,metal/tests/qsa_prompt_parity.cpp - Kernel decision: No-ship on kernel changes (
qsa.metal,qsa2.metal,qsa_prompt.metalunchanged); ship paired A/B harness improvements (--abinqsa_parity.cppandqsa_prompt_parity.cpp).- All tested
qsa_prepandqsa_score_mmkernel variants achieved 100% IEEE-754 bitwise parity (memcmp == 0) across every output buffer (qcur,qidx,kq,vq,ksc,vsc,tail,dead,pooled,scores,ids,part,xp), includingQSA_MROPE=1andQSA_Q_F16_FC=true. - However, on quiet Apple M5 (10-core GPU) in warmed 15-pair alternating A/B benchmarks, the current working-tree
qsa_prepandqsa_score_mmkernels are already at their M5 optimum:- Single-barrier
norm_rope(if (rope && tid < QROT) row[tid] = x;beforetg_sum256+ partner scale(rs * row[tid ^ 32]) * g[tid ^ 32]): Bitwise identical (memcmp == 0), but slightly slower across decode and prompt shapes (ratio 1.0006–1.0136,+0.5%to+1.4%atT = 1, 1984, 2048) because holdingxlive acrosstg_sum256+ issuing a second uncoalesced/divergentdevice const float* gload (g[tid ^ 32]) fortid < 64outweighs the threadgroup barrier savings on M5. tg_sum256#pragma clang loop unroll(full)+nsg = (w <= 128 ? 4 : 8): Bitwise identical (memcmp == 0), neutral within±0.2%atT = 1984, 2048(961.05 -> 962.95 us,ratio 1.0010atP=0, T=1984;1000.15 -> 1005.97 us,ratio 0.9994atP=3000, T=1984;1039.76 -> 1044.30 us,ratio 1.0004atP=3000, T=2048), because Metal Clang already unrollsnsg = 8when constant-folded andqsa_prepis memory-bandwidth bound onqfull/kcur/vcur/kq/vq.qsa_score_mm#pragma clang loop unroll(full)+qppre-offsetting (qsa_prompt.metal): Bitwise identical (memcmp == 0), neutral (0.1788 -> 0.1770 msatpos0=0,0.4654 -> 0.4699 msatpos0=2051,1.3466 -> 1.3326 msatpos0=8192,4.4671 -> 4.4764 msatpos0=30000forT = 1984), confirming Round 26 (audits/sol_r26_qsa.md) thatqsa_score_mmis dominated by the64x64x128matmul2dcooperative tensor op and DRAM reads/writes.
- Single-barrier
- All tested
Measured Quiet-GPU A/B Results #
1. qsa_prep Variants (qsa_parity --paired / --ab, 15 alternating pairs of 8x/32x dispatches) #
Shape (P, nt) |
Baseline (us) |
V1: Single-Barrier norm_rope + nsg=4 (us / ratio) |
V2: Two-Barrier + nsg=4 + unroll (us / ratio) |
V3: tg_sum256 unroll only (us / ratio) |
Bitwise memcmp |
|---|---|---|---|---|---|
P=3000, nt=1 |
7.047 |
7.090 (1.0136) |
6.939 (0.9881) |
7.039 (0.9952) |
PASS (all 13 buffers) |
P=3000, nt=2 |
2.720 |
2.730 (1.0029) |
2.699 (0.9957) |
2.329 (0.9967) |
PASS (all 13 buffers) |
P=3000, nt=4 |
2.879 |
2.859 (1.0004) |
2.879 (0.9982) |
2.854 (0.9959) |
PASS (all 13 buffers) |
P=3000, nt=8 |
4.611 |
4.598 (0.9972) |
4.581 (0.9835) |
4.579 (0.9693) |
PASS (all 13 buffers) |
P=0, nt=1984 |
969.125 |
969.359 (1.0006) |
962.948 (1.0010) |
963.656 (0.9987) |
PASS (all 13 buffers) |
P=3000, nt=1984 |
1001.609 |
1013.505 (1.0071) |
1005.969 (0.9994) |
1006.901 (1.0026) |
PASS (all 13 buffers) |
P=3000, nt=2048 |
1038.339 |
1042.750 (1.0051) |
1044.297 (1.0004) |
1036.656 (1.0036) |
PASS (all 13 buffers) |
2. qsa_score_mm Loop Unrolling + qp Pre-offsetting (qsa_prompt_parity --mm-compare score, 15 alternating pairs) #
T |
pos0 |
Baseline (ms) |
Candidate (ms) |
Delta | static_tg_mem |
Pre/Post memcmp |
|---|---|---|---|---|---|---|
1984 |
0 |
0.178750 |
0.176958 |
-1.00% |
16384 -> 16384 |
PASS / PASS |
1984 |
2051 |
0.465417 |
0.469875 |
+0.96% |
16384 -> 16384 |
PASS / PASS |
1984 |
8192 |
1.346583 |
1.332583 |
-1.04% |
16384 -> 16384 |
PASS / PASS |
1984 |
30000 |
4.467125 |
4.476417 |
+0.21% |
16384 -> 16384 |
PASS / PASS |
Harness Additions Shipped in Patch #
metal/tests/qsa_parity.cpp:- Added
--ab REF_DIR [P]mode that runs full-buffer bitwise parity (memcmp == 0) and warmed alternating paired timings across decodent = 1, 2, 4, 8and promptnt = 1984, 2048in a single< 6sinvocation. - Switched
--pairedtiming loops to usecx.run({ds[v][i]}, repeats)withcx.warm(...)and removes per-turn CPUmemsetof GPU buffers inside the timing loop.
- Added
metal/tests/qsa_prompt_parity.cpp:- Added
--ab T pos0 baseline_qsa_prompt.metal baseline_qsa.metal pairsmode testingqsa_prep,qsa_score_mm, andcombined(qsa_prep + qsa_score_mm) with full 10-buffer bitwisememcmpverification (supportingQSA_MROPE=1andQSA_QF16=1) and 12-layer-per-command-buffer paired A/B timings. - Added
cx.warm(...)before--mm-comparepaired timing loops.
- Added
Verification Artifacts #
- Unified patch:
/tmp/argon_r36_qsa/argon_r36_qsa.patch git -C vendor/strata apply --check /tmp/argon_r36_qsa/argon_r36_qsa.patch: exit0(clean).- Baseline kernel snapshots preserved in
/tmp/argon_r36_qsa/base_kernels/.