Round 31 Track C Audit: dq_f16 (dense_mm.metal) & DW<F> (mma.metal) #
1. Summary & Scope #
- Owned files:
metal/kernels/dense_mm.metal(dq_f16lines 28–66)metal/kernels/mma.metal(DW<T_IQ4_NL>,DW<T_IQ4_XS>lines 26–69)metal/tests/dense_mm_parity.cpp(--dq-ab <base_dir> [type] [shape]CLI support & uncontended pair filter)
- Patch:
/tmp/argon_r31_dq/argon_r31_dq.patch(verified viagit -C vendor/strata apply --check) - Parity: 100% bitwise identical (
memcmp == 0) across all 14 formats and all 13 shapes indense_mm_parity --dq-ab, defaultdense_mm_parity, andmma_parity.
2. Key Findings & Shipped Optimizations #
-
DRAM Bandwidth Ceiling Diagnosis on Large Shapes (
2560x12288,2560x10240,2560x6144,6144x2560):- On M5 (153.6 GB/s theoretical peak unified memory bandwidth),
dq_f16on the large matrices already achieves 125–142 GB/s (81%–92%of theoretical DRAM peak), dominated by writing the 62.9 MB (2560x12288) / 52.4 MB (2560x10240) FP16 output bufferWh.
- On M5 (153.6 GB/s theoretical peak unified memory bandwidth),
-
Eliminating Divergent
constant-Memory Table Init indq_f16<T_IQ4_XS / T_IQ4_NL>(dense_mm.metal):load_tab<T_IQ4_NL>previously executed a dynamicfor (int i = tid; i < 256; i += nth)loop in every threadgroup (10,240+ threadgroups per matrix) with divergentconstant-memory readskvalues_iq4nl[i & 15]andkvalues_iq4nl[i >> 4], which serialize across SIMD lanes on Apple GPUs.- Because
nth.x >= 64for everydq_f16dispatch, threadstid < 64now populate all 256half2entries oftabin 4 unrolled bank-conflict-free stores (tab[tid + {0, 64, 128, 192}]) directly from immediate register constants (0xbfad9881u,0xf6eaddcfu,0x26190d01u,0x71594535u), sharing the invariant low nibbleh0across all 4 writes with zeroconstant-memory loads and zero loop overhead.
-
Guarded
a.perm& Inverse Row Mapping (ro = gid.y) for Sub-8-Bit Formats (dense_mm.metal):- Guarded the
a.permdivision/modulo insideif (a.perm)soa.perm == 0(~430 of 530 dispatches) executes zero integer divisions/modulos, and foldedk0 = 0intoDW<F>::run(..., u, 0, w, p0, p1, tab). - For
a.perm != 0(320x10240p4, 96 dispatches/chunk) on sub-8-bit formats (fmt<F>::BB * 16 < fmt<F>::QK * 17), inverted the bijection so output rowsro = gid.yare written contiguously inWhand input rowris computed via shift/mask(ro & 3u) * (a.n_out >> 2) + (ro >> 2)whena.perm == 4(1.03x–1.05xspeedup on320x10240p4forIQ4_NL,Q4_0,Q5_0,Q5_1).
- Guarded the
-
Decoupled Scale Load in
DW<T_IQ4_XS>andDW<T_IQ4_NL>(mma.metal):- Unpacking the 32
tabvalues intow[0..31]before loading/applying the block scaledhides the device memory latency of the block scale header (b[0..7]) behind the threadgroup table lookups, matching the two-phase pattern ofDW<F>.
- Unpacking the 32
3. Ablations Tested & Rejected (Not Shipped) #
- Dedicated
DW<T_Q8_0>&DW<T_IQ3_S>fusing* dinto the unpack loop: Slightly regressed6144x2560(0.98x) compared to genericDW<F>+deq<F>, becausedeq<F>already unrolls fixed trip counts (16and2) while deferringrdh(b)until after the integer unpack loop. DW<T_Q2_0>0x6400FP16 mantissa-OR trick &rd32: Bitwise identical (memcmp == 0) once byte-masked (q[g]), but0.99x(57.84 -> 58.15 ms) vsushortp[0], p[1]inDW<T_Q2_0>becauseDW<T_Q2_0>outputsfloat w[16](requiring an extrahalf-to-floatconversion per weight after thehalf4FMA).packed_uint4/uint* pinDW<T_IQ4_XS>:0.99xvsushort* pbecause wider 32/128-bit loads delay the firsttablookup and increase live register pressure.- 32x
simd_shufflenibble LUT inDW<T_IQ4_XS / T_IQ4_NL>:0.66xon2560x640/2560x512(32 shuffles per thread vs 16half2threadgroup loads).
4. Quiet-GPU Paired Benchmark Results (dense_mm_parity --dq-ab) #
IQ4_XS:58.83 -> 58.56 ms(memcmp=0on all shapes;6144x25601.01x,2560x6401.01x,2560x5121.01x,2560x481.03x,10240x41.07x,2560x11.03x)IQ4_NL:64.70 -> 64.50 ms(memcmp=0on all shapes;320x10240p444.9 -> 42.6 us1.05x,6144x25601.01x,2560x6401.01x,640x25601.01x,2560x5121.01x,2560x481.05x,10240x41.04x,2560x11.06x)IQ3_S:57.02 -> 56.92 ms(memcmp=0on all shapes;2560x12288610.0 -> 606.7 us1.01x,2560x1281.07x,2560x481.02x)320x10240p4(perm=4):Q4_043.8 -> 42.4 us(1.03x),Q5_046.2 -> 45.2 us(1.02x),Q5_147.1 -> 46.5 us(1.01x),IQ4_NL44.9 -> 42.6 us(1.05x), allmemcmp=0.