Round 9 Track C Audit: load_tab<T_IQ3_S> Dead load_signs Removal + Barrier-Free TAB == 1 in moe_gu / moe_down + Decode moe.metal NE == 1 dn_body Specialization #
1. Summary of Changes (metal/kernels/quant.metal & metal/kernels/moe.metal) #
- Patch file:
/tmp/argon_r9_quant/argon_r9_quant.patch(verified clean viagit -C vendor/strata apply --check). - Files touched: Only
metal/kernels/quant.metalandmetal/kernels/moe.metalin scratch copy/tmp/argon_r9_quant/strata(trackedvendor/strata/untouched).
Change 1: Remove Dead load_signs from load_tab<T_IQ3_S> & Shrink fmt<T_IQ3_S>::TAB (quant.metal lines 41 & 100–103) #
- Every
T_IQ3_Sdecoder across the Metal codebase (deq<T_IQ3_S>inquant.metal,DW<T_IQ3_S>inmma.metal,W4<T_IQ3_S>andW8<T_IQ3_S>inmoe_mm.metal) reads raw 8-bit sign bytes directly from the block (b[74 + 4 * ib + l]) and never accessesksigns_iq2xs(tab + 512). - Changed
fmt<T_IQ3_S>::TABfrom512 + 32(2176 B) to512(2048 B) and removedload_signs(tab, 512, tid, nth);fromload_tab<T_IQ3_S>. - Saves
128 Bof threadgroup memory and128byte loads/stores per threadgroup acrossgemv,moe_gu,mm_gu*,dq_f16, andmtp_chain.
Change 2: Eliminate Redundant threadgroup_barrier When fmt<F>::TAB == 1 in moe_gu & moe_down (moe.metal lines 331 & 421) #
- Guarded
load_tab<F>+threadgroup_barrier(mem_flags::mem_threadgroup)withif (fmt<F>::TAB > 1 && moe_probe != 1)inmoe_guandif (fmt<F>::TAB > 1)inmoe_down(matchingdq_f16indense_mm.metal). - Since
fmt<F>::TABis a compile-time enum constant, the Metal compiler dead-code eliminates the barrier in allTAB == 1specializations (T_Q2_0,T_Q2_0R,T_Q3_K,T_Q4_K,T_Q5_K,T_Q6_K,T_Q8_0,T_Q5_1,T_Q4_0,T_Q5_0,T_BF16,T_F32). - Eliminates 48
moe_down(T_Q2_0/T_Q2_0R) and 12moe_gu(T_Q6_K) unconditional threadgroup barriers per decode / MTP verify step.
Change 3: Specialize NE == 1 in dn_body (moe.metal lines 373–382) #
- Mirrored
gu_body'sif (NE == 1)branch intodn_body: for single-entry expert groups (NE == 1, which is 100% of groups atntok = 1and the majority of groups inntok = 2..4verify windows),X<F>::load(h + c * int(a.n_ff), ...)is hoisted outside thefor (int r = 0; r < NR; ++r)loop instead of reloadingxs[16]inside therloop (forQ2_0/Q2_0RwithNR = 2) or allocatingfloat q[NR][UNIT]across rows before loadingx. - Ruled out: Manual unrolling of
for (int l2 = 0; l2 < 2; ++l2)indeq<T_IQ2_XS>(quant.metallines 182–193) was benchmarked and rejected because it increased live register pressure acrossNRrows ingu_bodyon Apple M5, regressingmoe_guIQ2_XS(QUANT_CTAB=1) from100.7 us(94.0 GB/s) to105.2 us(90.0 GB/s) atntok = 1and from283.1 us(98.2 GB/s) to293.4 us(94.8 GB/s) atntok = 3. Keeping the loop form indeq<T_IQ2_XS>preserves optimal register allocation.
2. Bitwise Parity Verification (100% Bit-for-Bit Identical) #
expert_parity(48 configurations):- Tested all combinations of
guin{IQ3_S, IQ2_XS, Q6_K, Q2_0},dnin{Q2_0, Q8_0, IQ4_NL}, andntokin{1, 2, 3, 4}. - Verified 64-bit FNV-1a hashes of GPU output buffers
h(4 * ntok * TOPK * N_FFbytes) andy(4 * ntok * N_EMBDbytes) between baseline and patched kernels: 48 / 48 configurations matched with 0 bit differences (mismatches: 0).
- Tested all combinations of
moe_mm_parity --v3 --tile 16,64,32,2 --gu IQ3_S --dn Q2_0 --dump:- Verified
6,512,640-byte dump ofHSandy: 100% byte-for-byte identical (b1 == b2).
- Verified
gemv_parity:- All 13 formats (
F32,BF16,Q8_0,Q4_0,Q5_0,IQ4_NL,IQ4_XS,Q2_0,IQ3_S,Q3_K,Q4_K,Q5_K,Q6_K),n_inin{2560, 640, 10240},ntokin{1, 3, 8}across 20 tile configs:ALL PASS(bitwise window == one token).
- All 13 formats (
dense_mm_parity:- All formats including
IQ3_S(2560 -> 100,2560 -> 48,6144 -> 100,6144 -> 48):ALL PASS(memcmp(wh)=0, memcmp(xh)=0).
- All formats including
3. Decode expert_parity --bench Results on Apple M5 (ntok = 1, 2, 3, 4) #
3-of-3 median GPU timings (--layers 4 --reps 48 --share 0.3, production tile configs: gu = 32,1,4 for ntok = 1,2 and 16,1,4 for ntok = 3,4; dn = 8,2,4):
Config (gu / dn) |
ntok |
Base gu (us) |
Patched gu (us) |
Base dn (us) |
Patched dn (us) |
dn Speedup |
Base layer (us) |
Patched layer (us) |
|---|---|---|---|---|---|---|---|---|
IQ3_S / Q2_0 |
1 | 116.1 | 116.1 | 46.9 | 46.7 | +0.4% | 167.4 | 167.3 |
IQ3_S / Q2_0 |
2 | 205.0 | 206.1 | 81.6 | 81.1 | +0.6% | 293.1 | 292.8 |
IQ3_S / Q2_0 |
3 | 289.9 | 288.9 | 116.9 | 115.9 | +0.9% | 414.7 | 412.1 |
IQ3_S / Q2_0 |
4 | 375.8 | 374.2 | 152.2 | 150.8 | +0.9% | 536.9 | 534.2 |
Q6_K / Q2_0 |
1 | 206.3 | 205.7 | 46.8 | 46.8 | 0.0% | 258.2 | 257.8 |
Q6_K / Q2_0 |
2 | 351.2 | 350.9 | 81.7 | 81.1 | +0.7% | 443.2 | 442.0 |
Q6_K / Q2_0 |
3 | 507.6 | 508.1 | 117.8 | 116.5 | +1.1% | 639.9 | 638.9 |
Q6_K / Q2_0 |
4 | 651.3 | 652.0 | 153.3 | 152.3 | +0.7% | 823.5 | 822.6 |
IQ2_XS / Q2_0 (ctab) |
1 | 100.8 | 100.7 | 47.0 | 47.0 | 0.0% | 151.6 | 151.5 |
IQ2_XS / Q2_0 (ctab) |
2 | 179.1 | 179.0 | 81.9 | 81.3 | +0.7% | 266.0 | 265.3 |
IQ2_XS / Q2_0 (ctab) |
3 | 247.1 | 247.0 | 117.0 | 116.1 | +0.8% | 371.0 | 370.1 |
IQ2_XS / Q2_0 (ctab) |
4 | 320.9 | 320.5 | 152.4 | 151.1 | +0.9% | 481.4 | 479.9 |
IQ2_XS / Q2_0 (tg tab) |
1 | 103.1 | 103.0 | 46.9 | 46.9 | 0.0% | 154.4 | 154.3 |
IQ2_XS / Q2_0 (tg tab) |
2 | 184.1 | 183.8 | 81.8 | 81.3 | +0.6% | 271.5 | 270.9 |
IQ2_XS / Q2_0 (tg tab) |
3 | 245.3 | 245.2 | 117.3 | 116.3 | +0.9% | 369.9 | 368.7 |
IQ2_XS / Q2_0 (tg tab) |
4 | 319.1 | 318.8 | 152.7 | 151.2 | +1.0% | 480.7 | 478.7 |