Round 8 Track C: Barrier-Free Constant-Table dq_f16<F> + Vectorized act_f16 #
1. Summary of Changes (metal/kernels/dense_mm.metal & metal/tests/dense_mm_parity.cpp) #
-
Zero-Threadgroup-Memory, Barrier-Free
dq_f16<F>(DWC<F>):- Previously,
dq_f16<F>allocatedthreadgroup uint tab[fmt<F>::TAB], invokedload_tab<F>(tab, tid, 64), and executedthreadgroup_barrier(mem_flags::mem_threadgroup)in every single threadgroup ((n_in / UNIT / 64) * n_outthreadgroups per matrix, e.g.,30,720threadgroups for a2560 x 10240IQ3_Smatrix), even though each thread decodes at most one unit (16or32weights) before exiting:- For
T_IQ3_S(TAB = 544,2,176bytes static threadgroup memory), each thread loaded8uints fromiq3s_grid+2uchars fromksigns_iq2xs(whichdeq<T_IQ3_S>does not even use!) into threadgroup memory and waited on a threadgroup barrier just to perform4grid lookups indeq<T_IQ3_S>. - For
T_IQ4_NLandT_IQ4_XS(TAB = 256,1,024bytes static threadgroup memory), each thread constructed4half2codebook entries in threadgroup memory and waited on a barrier. - For all other 10 formats (
TAB = 1),tabwas unused (static_tg_mem = 0), yet every threadgroup still executedthreadgroup_barrier(mem_flags::mem_threadgroup).
- For
- Added
DWC<F>inmetal/kernels/dense_mm.metalwith direct constant-memory specializations forT_IQ3_S(iq3s_grid),T_IQ4_NL(kvalues_iq4nl), andT_IQ4_XS(kvalues_iq4nl), forwarding the remaining 10 formats toDW<F>::run(..., nullptr). - Removed
threadgroup uint tab[fmt<F>::TAB],load_tab<F>, andthreadgroup_barrierfromdq_f16<F>. Static threadgroup memory is now 0 bytes across all 13dq_f16<F>specializations.
- Previously,
-
Vectorized
half4Output Stores indq_f16<F>:- Since
H = UNIT / 2is always a multiple of4(H = 8forUNIT = 16,H = 16forUNIT = 32) and chunk offsetsp0,p1are always 4-element (8-byte) aligned, replaced the scalarhalfstore loop withhalf4vector stores (*(device half4*)(o + p0 + j)and*(device half4*)(o + p1 + j)), cutting store instructions by4x(4half4stores instead of16scalar stores forUNIT = 16;8half4stores instead of32scalar stores forUNIT = 32).
- Since
-
Vectorized
float4 -> half4act_f16:- Updated
act_f16to convert 4 elements per thread via((device half4*)xh)[gid] = half4(((device const float4*)x)[gid])with a tail loop atgid == (n >> 2)for any remainder elements. This remains backward-compatible with existing host dispatch grids(n + 255) / 256while allowing(n + 1023) / 1024dispatches.
- Updated
2. Bitwise Parity & Benchmark Results on Apple M5 GPU #
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) were verified against ref::dequant + ref::f32_to_f16 with std::memcmp in both dense_mm_parity (3.65 s) and dense_mm_parity --bench (1.48 s):
- 100% bitwise identity (
memcmp == 0) across all 13 formats and all tested matrix shapes (2560 x 10240,2560 x 12288,2560 x 6144,10240 x 324, plus2560/640/6144 -> 100/48).
| Format | UNIT |
static_tg_mem (Old -> New) |
2560 x 10240 (us) |
2560 x 12288 (us) |
2560 x 6144 (us) |
10240 x 324 (us) |
memcmp |
|---|---|---|---|---|---|---|---|
F32 |
16 | 0 B -> 0 B |
1191.7 | 1436.9 | 677.9 | 124.2 | 0 (PASS) |
BF16 |
16 | 0 B -> 0 B |
777.6 | 936.1 | 447.0 | 85.7 | 0 (PASS) |
Q8_0 |
32 | 0 B -> 0 B |
582.1 | 699.9 | 324.8 | 62.9 | 0 (PASS) |
Q4_0 |
32 | 0 B -> 0 B |
521.3 | 625.4 | 269.5 | 48.4 | 0 (PASS) |
Q5_0 |
32 | 0 B -> 0 B |
533.3 | 640.8 | 290.4 | 51.6 | 0 (PASS) |
IQ4_NL |
32 | 1024 B -> 0 B |
520.4 | 624.3 | 268.0 | 49.3 | 0 (PASS) |
IQ4_XS |
32 | 1024 B -> 0 B |
516.1 | 626.3 | 262.6 | 47.4 | 0 (PASS) |
Q2_0 |
16 | 0 B -> 0 B |
460.0 | 571.6 | 208.3 | 41.1 | 0 (PASS) |
IQ3_S |
16 | 2176 B -> 0 B |
495.6 | 602.1 | 237.0 | 43.2 | 0 (PASS) |
Q3_K |
32 | 0 B -> 0 B |
506.7 | 615.3 | 243.4 | 44.8 | 0 (PASS) |
Q4_K |
32 | 0 B -> 0 B |
529.5 | 637.2 | 274.6 | 46.9 | 0 (PASS) |
Q5_K |
32 | 0 B -> 0 B |
542.1 | 649.9 | 291.0 | 49.0 | 0 (PASS) |
Q6_K |
32 | 0 B -> 0 B |
547.7 | 659.1 | 297.8 | 56.0 | 0 (PASS) |
- Note on DRAM bandwidth saturation on M5 (
~100–130 GB/s): For large matrices (2560 x 10240writes52.4 MBoffp16output and reads14.1 MBofIQ3_Sweights =66.5 MBper matrix),495 uscorresponds to134 GB/seffective DRAM bandwidth, which is at the M5 hardware memory bandwidth ceiling. On smaller shapes (2560 x 48ssm_alpha/ssm_beta), removing the2,176-byte threadgroup table load + barrier indq_f16_21(IQ3_S) speeds up each dispatch by1.56x(3.3 us -> 2.1 us).
3. Artifacts #
- Clean unified diff:
/tmp/argon_r8_dq/argon_r8_dq.patch(verified withgit -C vendor/strata apply --check).