opus r11 dense: gemm_f16 tile table + fused shared-expert swiglu #
patch: /tmp/opus_r11_dense/opus_r11_dense.patch (git -C vendor/strata apply --check clean, 188 lines;
touches metal/kernels/dense_mm.metal, metal/tests/dense_mm_parity.cpp, metal/src/engine/window.cpp 3301-3326 only).
experimental/rejected: /tmp/opus_r11_dense/experimental_swiglu_and_26_tiles.patch (fused kernel + all 26 tile instantiations).
raw logs: /tmp/opus_r11_dense/tiles/ (sweep M_shape*.txt, A/B final_ab_*, quiet_ab_*).
scratch build: /tmp/opus_r11_dense/strata/build-metal (dense_mm_parity + strata_metal_kernels incl. window.cpp compile clean).
headline #
- every gemm_f16 tile is bitwise identical on M5. 26 tiles (TM 16..128, TN 32..256, SG 1..8) x 12 production (N, K)
shapes x M = 256/512/1024/1983/2048:
memcmp == 0against 128_64_4 in all cases (0xFF-poisoned Y, so unwritten elements would also fail). MPP's K reduction order doesn't depend on the tile. So tile choice is free, speed only. - new selector: 4 new instantiations (
16_64_2,32_64_2,32_128_2,64_32_2), selector rewritten for M > 128 (M <= 128 bit-for-bit the old choice):- N <= 64 (Wal/Wbe/Wsg): 16_64_2 for M < 1024, else 64_32_2 (old 64_32_4: 1.3-1.7x on Walbe)
- N <= 128 (Wik): 16_64_2 for M < 768, else 32_64_2 (1.1-1.3x)
- HC read (324 x 10240): old tile for M < 1536, 32_128_2 above (1.05-1.11x at 1983)
- N <= 512 (Wr/Wr2/Wk/Wv/Wiq) and Wshg/Wshu below M 384: 32_64_2 for M < 384, 32_128_2 above (1.02-1.2x)
- everything else (Wqkv, Wz, Wout, Wq, Wpv, Wshd, Wshg/u): 128_64_4 for M < 1536, 64_128_4 above. That's the old "bug #1" (64_128 only at M >= 2048). At M = 1983 128_64_4 falls off a cliff on Wout (K = 6144): 6.3-8.2 ms vs 4.4-4.7 ms in quiet runs (1.5-1.8x), and is 6-10% slower on the other big shapes. Below ~1792 the two tie (±1%), so the threshold is conservative at 1536.
- fused gemm_swiglu_f16: built as asked (two
get_destination_cooperative_tensoraccumulators,dynamic_length_vK, no threadgroup memory, no k0 loop, swiglu_h's exact expression in registers, direct half stores). Bitwise identical (memcmp == 0 on h) for all 10 tiles tried, but NOT faster: best fused 1016 us vs best unfused 976 us (2x gemm 64_64_2- swiglu_h) at M 1983. Two TMxTN accumulators double register pressure (fused 64_64_4: 1862 us). swiglu_h + the fp32 g/u traffic is only ~12-20 us/layer (<1 ms/chunk), so even a perfect fusion is capped at ~1 ms/chunk. Rejected, not in the patch. (One untested idea: dq Wshg/Wshu interleaved with perm=2 into one N=1280 B, then one op.run with the gemm_hc_up REG epilogue pairing adjacent columns. One accumulator, but the same ~1 ms ceiling. Not worth it.)
- Nn = 1 (Wsg): no dedicated row-dot. A hand dot can't match MPP's reduction bits, and at large M it's memory-bound on reading A (~85 us = A at ~120 GB/s). Tile changes give 1.3-1.45x at M <= 512, ~1.03x at 1983.
caveat: GPU contention #
A sibling agent was running moe_mm_parity (and the :8080 server was live) through most of the measurements, so absolute
times drift by up to 40% between runs. Within a process the rounds interleave old and new, so the paired ratio is the
reliable number. The first sweep (mostly quiet) gives the clearest absolute picture.
quiet-ish sweep, ms per 48-layer chunk (min of 1-2 runs/shape, 12 shapes x calls) #
| M | old selector | sweep-best per shape |
|---|---|---|
| 256 | 151.2 | 131.1 |
| 512 | 257.2 | 248.4 |
| 1024 | 515.5 | 508.1 |
| 1983 | 1215.8 (kprof said 1241.6) | 982.0 |
final paired A/B, shipped selector (dense_mm_parity --ab M, 5 runs x 9 rounds, contended) #
| M | old ms/chunk | new ms/chunk | speedup | saved ms/chunk |
|---|---|---|---|---|
| 256 | 143.5 | 140.9 | 1.018x | 2.5 |
| 512 | 283.2 | 279.2 | 1.014x | 4.0 |
| 1024 | 626.1 | 631.9 | 0.991x | -5.8 |
| 1983 | 1469.4 | 1401.6 | 1.048x | 67.8 |
| 2048 | 1465.2 | 1460.1 | 1.004x | 5.2 |
(At 1024 the shipped selector is identical to old for all big shapes; the -0.9% there comes from the earlier 1024-threshold version and from noise. Rerun after the switch to 1536: 1024 1.01x, 1536 1.01x, 1983 1.05x.) The contended runs understate the 1983 win. In quieter runs the 1983 per-chunk saving is ~140-230 ms (1.13-1.19x), mostly Wout (1.5-1.8x) plus Wqkv/Wz/Wq/hc_read (1.06-1.11x).
per-shape paired A/B at 1024 and 1983 (pre-1536-threshold run; at 1024 the big shapes are now old=new) #
| M | shape (N x K) | calls | old tile | old us | new tile | new us | paired speedup |
|---|---|---|---|---|---|---|---|
| 256 | hc_read (324 x 10240) | 96 | 64_64_4 | 194.0 | 64_64_4 | 195.2 | 1.000x |
| 256 | Wqkv (10240 x 2560) | 37 | 128_64_4 | 967.8 | 128_64_4 | 967.3 | 0.999x |
| 256 | Wz (6144 x 2560) | 36 | 128_64_4 | 607.9 | 128_64_4 | 604.2 | 1.003x |
| 256 | Wout (2560 x 6144) | 48 | 128_64_4 | 597.4 | 128_64_4 | 599.8 | 0.998x |
| 256 | Wq (12288 x 2560) | 12 | 128_64_4 | 1384.9 | 128_64_4 | 1385.9 | 1.000x |
| 256 | Wpv (2560 x 2560) | 1 | 128_64_4 | 298.5 | 128_64_4 | 297.1 | 1.001x |
| 256 | Wshgu (640 x 2560) | 96 | 128_64_4 | 71.3 | 32_64_2 | 69.5 | 1.080x |
| 256 | Wshd (2560 x 640) | 48 | 128_64_4 | 65.3 | 128_64_4 | 65.5 | 0.997x |
| 256 | W512 (512 x 2560) | 119 | 128_64_4 | 64.9 | 32_64_2 | 53.5 | 1.213x |
| 256 | Wik (128 x 2560) | 12 | 64_64_4 | 32.5 | 16_64_2 | 24.7 | 1.312x |
| 256 | Walbe (48 x 2560) | 72 | 64_32_4 | 29.8 | 16_64_2 | 21.4 | 1.388x |
| 256 | Wsg (1 x 2560) | 48 | 64_32_4 | 27.3 | 16_64_2 | 18.9 | 1.444x |
| 512 | hc_read (324 x 10240) | 96 | 64_64_4 | 294.5 | 64_64_4 | 295.4 | 0.998x |
| 512 | Wqkv (10240 x 2560) | 37 | 128_64_4 | 1912.6 | 128_64_4 | 1884.6 | 1.005x |
| 512 | Wz (6144 x 2560) | 36 | 128_64_4 | 1293.9 | 128_64_4 | 1286.1 | 1.002x |
| 512 | Wout (2560 x 6144) | 48 | 128_64_4 | 1360.1 | 128_64_4 | 1361.3 | 1.000x |
| 512 | Wq (12288 x 2560) | 12 | 128_64_4 | 2779.6 | 128_64_4 | 2767.6 | 1.000x |
| 512 | Wpv (2560 x 2560) | 1 | 128_64_4 | 575.6 | 128_64_4 | 576.5 | 1.001x |
| 512 | Wshgu (640 x 2560) | 96 | 128_64_4 | 145.6 | 128_64_4 | 145.6 | 0.999x |
| 512 | Wshd (2560 x 640) | 48 | 128_64_4 | 129.7 | 128_64_4 | 127.4 | 1.001x |
| 512 | W512 (512 x 2560) | 119 | 128_64_4 | 115.9 | 32_128_2 | 103.4 | 1.120x |
| 512 | Wik (128 x 2560) | 12 | 64_64_4 | 36.6 | 16_64_2 | 31.1 | 1.174x |
| 512 | Walbe (48 x 2560) | 72 | 64_32_4 | 34.5 | 16_64_2 | 25.4 | 1.362x |
| 512 | Wsg (1 x 2560) | 48 | 64_32_4 | 30.5 | 16_64_2 | 23.2 | 1.315x |
| 1024 | hc_read (324 x 10240) | 96 | 128_64_4 | 670.6 | 128_64_4 | 672.8 | 0.997x |
| 1024 | Wqkv (10240 x 2560) | 37 | 128_64_4 | 4440.1 | 64_128_4 | 4562.4 | 0.993x |
| 1024 | Wz (6144 x 2560) | 36 | 128_64_4 | 2784.0 | 64_128_4 | 2775.6 | 1.001x |
| 1024 | Wout (2560 x 6144) | 48 | 128_64_4 | 2938.9 | 64_128_4 | 3041.4 | 0.966x |
| 1024 | Wq (12288 x 2560) | 12 | 128_64_4 | 5928.4 | 64_128_4 | 5930.2 | 1.003x |
| 1024 | Wpv (2560 x 2560) | 1 | 128_64_4 | 1300.8 | 64_128_4 | 1290.5 | 1.006x |
| 1024 | Wshgu (640 x 2560) | 96 | 128_64_4 | 324.0 | 64_128_4 | 322.5 | 1.001x |
| 1024 | Wshd (2560 x 640) | 48 | 128_64_4 | 301.0 | 64_128_4 | 305.7 | 1.030x |
| 1024 | W512 (512 x 2560) | 119 | 128_64_4 | 263.3 | 32_128_2 | 247.4 | 1.064x |
| 1024 | Wik (128 x 2560) | 12 | 128_64_4 | 73.0 | 32_64_2 | 57.3 | 1.262x |
| 1024 | Walbe (48 x 2560) | 72 | 64_32_4 | 58.2 | 64_32_2 | 38.2 | 1.529x |
| 1024 | Wsg (1 x 2560) | 48 | 64_32_4 | 35.9 | 64_32_2 | 34.0 | 1.059x |
| 1983 | hc_read (324 x 10240) | 96 | 128_64_4 | 1497.2 | 32_128_2 | 1408.9 | 1.063x |
| 1983 | Wqkv (10240 x 2560) | 37 | 128_64_4 | 9938.0 | 64_128_4 | 9562.1 | 1.041x |
| 1983 | Wz (6144 x 2560) | 36 | 128_64_4 | 6553.3 | 64_128_4 | 6313.0 | 1.044x |
| 1983 | Wout (2560 x 6144) | 48 | 128_64_4 | 7152.7 | 64_128_4 | 6726.0 | 1.062x |
| 1983 | Wq (12288 x 2560) | 12 | 128_64_4 | 14676.2 | 64_128_4 | 13957.6 | 1.040x |
| 1983 | Wpv (2560 x 2560) | 1 | 128_64_4 | 3087.7 | 64_128_4 | 2953.1 | 1.051x |
| 1983 | Wshgu (640 x 2560) | 96 | 128_64_4 | 771.2 | 64_128_4 | 769.1 | 1.002x |
| 1983 | Wshd (2560 x 640) | 48 | 128_64_4 | 758.8 | 64_128_4 | 737.8 | 1.026x |
| 1983 | W512 (512 x 2560) | 119 | 128_64_4 | 600.6 | 32_128_2 | 592.2 | 1.015x |
| 1983 | Wik (128 x 2560) | 12 | 128_64_4 | 174.9 | 32_64_2 | 155.8 | 1.112x |
| 1983 | Walbe (48 x 2560) | 72 | 64_32_4 | 157.4 | 64_32_2 | 89.6 | 1.732x |
| 1983 | Wsg (1 x 2560) | 48 | 64_32_4 | 86.9 | 64_32_2 | 83.2 | 1.042x |
| 2048 | hc_read (324 x 10240) | 96 | 128_64_4 | 1527.5 | 32_128_2 | 1472.4 | 1.034x |
| 2048 | Wqkv (10240 x 2560) | 37 | 64_128_4 | 10085.9 | 64_128_4 | 10099.5 | 0.992x |
| 2048 | Wz (6144 x 2560) | 36 | 64_128_4 | 6538.5 | 64_128_4 | 6593.6 | 0.989x |
| 2048 | Wout (2560 x 6144) | 48 | 64_128_4 | 7122.7 | 64_128_4 | 7182.9 | 1.000x |
| 2048 | Wq (12288 x 2560) | 12 | 64_128_4 | 14137.9 | 64_128_4 | 14126.0 | 0.998x |
| 2048 | Wpv (2560 x 2560) | 1 | 64_128_4 | 3071.0 | 64_128_4 | 3067.0 | 1.000x |
| 2048 | Wshgu (640 x 2560) | 96 | 128_64_4 | 762.8 | 64_128_4 | 766.5 | 1.005x |
| 2048 | Wshd (2560 x 640) | 48 | 128_64_4 | 688.5 | 64_128_4 | 690.9 | 1.001x |
| 2048 | W512 (512 x 2560) | 119 | 128_64_4 | 602.7 | 32_128_2 | 596.9 | 1.011x |
| 2048 | Wik (128 x 2560) | 12 | 128_64_4 | 164.2 | 32_64_2 | 150.9 | 1.115x |
| 2048 | Walbe (48 x 2560) | 72 | 64_32_4 | 155.5 | 64_32_2 | 92.0 | 1.718x |
| 2048 | Wsg (1 x 2560) | 48 | 64_32_4 | 88.5 | 64_32_2 | 85.4 | 1.032x |
full sweep: best 3 tiles per shape (us/call, min over runs), all bitwise #
| M | shape | old tile (us) | fastest 3 (us) |
|---|---|---|---|
| 256 | hc_read | 64_64_4 (352) | 32_32_1 185, 64_32_2 189, 32_128_4 200 |
| 256 | Wqkv | 128_64_4 (960) | 128_128_8 945, 128_64_4 960, 64_128_8 965 |
| 256 | Wz | 128_64_4 (579) | 64_64_2 562, 32_128_2 571, 64_128_4 577 |
| 256 | Wout | 128_64_4 (559) | 64_256_8 553, 64_64_2 555, 64_128_4 556 |
| 256 | Wq | 128_64_4 (1122) | 128_64_4 1122, 64_64_2 1127, 64_128_4 1132 |
| 256 | Wpv | 128_64_4 (243) | 64_128_2 235, 64_256_4 236, 64_64_2 236 |
| 256 | Wshgu | 128_64_4 (65) | 64_64_4 60, 32_128_4 60, 64_32_2 60 |
| 256 | Wshd | 128_64_4 (65) | 128_256_8 63, 128_64_8 63, 64_256_4 63 |
| 256 | W512 | 128_64_4 (61) | 32_64_2 54, 64_32_2 54, 32_32_1 54 |
| 256 | Wik | 64_64_4 (33) | 16_64_2 25, 16_32_1 26, 16_128_4 26 |
| 256 | Walbe | 64_32_4 (31) | 16_128_4 21, 16_64_2 22, 16_32_1 23 |
| 256 | Wsg | 64_32_4 (27) | 16_32_1 18, 16_64_2 19, 16_128_4 21 |
| 512 | hc_read | 64_64_4 (335) | 32_128_4 295, 32_128_2 300, 64_64_2 300 |
| 512 | Wqkv | 128_64_4 (1872) | 128_128_8 1835, 128_64_4 1872, 64_128_4 1877 |
| 512 | Wz | 128_64_4 (1113) | 128_64_4 1113, 64_64_2 1118, 64_128_4 1119 |
| 512 | Wout | 128_64_4 (1111) | 64_128_4 1098, 64_64_2 1104, 128_128_8 1105 |
| 512 | Wq | 128_64_4 (2237) | 64_128_4 2226, 128_128_8 2236, 64_64_2 2236 |
| 512 | Wpv | 128_64_4 (463) | 64_64_2 462, 128_64_4 463, 128_128_8 463 |
| 512 | Wshgu | 128_64_4 (116) | 128_64_4 116, 128_128_8 116, 64_128_4 117 |
| 512 | Wshd | 128_64_4 (121) | 128_128_8 120, 128_64_4 121, 64_128_4 121 |
| 512 | W512 | 128_64_4 (116) | 32_128_2 101, 64_64_2 103, 32_64_2 106 |
| 512 | Wik | 64_64_4 (34) | 16_64_2 31, 16_32_1 32, 16_128_4 34 |
| 512 | Walbe | 64_32_4 (35) | 16_64_2 26, 16_32_1 26, 16_128_4 30 |
| 512 | Wsg | 64_32_4 (30) | 16_32_1 21, 16_64_2 23, 32_32_2 28 |
| 1024 | hc_read | 128_64_4 (660) | 128_64_4 660, 64_128_4 663, 64_64_2 669 |
| 1024 | Wqkv | 128_64_4 (3702) | 64_128_4 3694, 128_64_4 3702, 128_128_8 3703 |
| 1024 | Wz | 128_64_4 (2236) | 128_128_8 2232, 64_128_4 2233, 128_64_4 2236 |
| 1024 | Wout | 128_64_4 (2331) | 128_64_4 2331, 64_128_4 2343, 64_64_2 2353 |
| 1024 | Wq | 128_64_4 (4430) | 128_128_8 4412, 128_64_4 4430, 64_128_4 4431 |
| 1024 | Wpv | 128_64_4 (977) | 64_128_4 936, 64_64_2 967, 32_128_4 972 |
| 1024 | Wshgu | 128_64_4 (256) | 128_128_4 232, 64_64_2 236, 64_128_2 236 |
| 1024 | Wshd | 128_64_4 (248) | 128_128_8 236, 64_128_4 237, 64_256_8 237 |
| 1024 | W512 | 128_64_4 (214) | 64_64_2 198, 32_128_2 199, 64_64_4 206 |
| 1024 | Wik | 128_64_4 (62) | 32_64_2 53, 64_32_2 53, 32_32_1 54 |
| 1024 | Walbe | 64_32_4 (58) | 16_32_1 35, 16_64_2 36, 128_64_8 37 |
| 1024 | Wsg | 64_32_4 (36) | 16_32_1 34, 16_64_2 34, 64_32_2 34 |
| 1983 | hc_read | 128_64_4 (1314) | 32_128_2 1170, 64_64_2 1214, 64_128_4 1252 |
| 1983 | Wqkv | 128_64_4 (7729) | 64_128_4 7239, 32_128_2 7251, 32_256_4 7330 |
| 1983 | Wz | 128_64_4 (4645) | 64_128_4 4361, 32_128_2 4385, 64_64_2 4406 |
| 1983 | Wout | 128_64_4 (6311) | 32_256_4 4424, 32_128_2 4437, 64_128_4 4448 |
| 1983 | Wq | 128_64_4 (9301) | 64_128_4 8686, 32_128_2 8740, 64_64_2 8818 |
| 1983 | Wpv | 128_64_4 (1963) | 64_128_4 1824, 32_128_2 1842, 32_256_4 1855 |
| 1983 | Wshgu | 128_64_4 (486) | 32_128_2 475, 64_128_4 477, 64_64_2 480 |
| 1983 | Wshd | 128_64_4 (473) | 64_256_8 460, 64_128_4 463, 64_64_2 463 |
| 1983 | W512 | 128_64_4 (414) | 32_128_4 396, 32_128_2 401, 64_64_2 404 |
| 1983 | Wik | 128_64_4 (119) | 32_128_2 104, 64_64_2 106, 32_64_2 108 |
| 1983 | Walbe | 64_32_4 (116) | 64_32_2 85, 128_64_8 87, 16_64_2 87 |
| 1983 | Wsg | 64_32_4 (88) | 16_32_1 85, 16_64_2 85, 32_32_2 86 |
| 2048 | hc_read | 128_64_4 (1325) | 32_128_2 1196, 128_128_4 1220, 64_64_2 1265 |
| 2048 | Wqkv | 64_128_4 (7558) | 64_128_4 7558, 32_128_2 7560, 32_256_4 7616 |
| 2048 | Wz | 64_128_4 (4674) | 32_128_2 4647, 64_128_4 4674, 32_256_4 4686 |
| 2048 | Wout | 64_128_4 (4685) | 32_256_4 4668, 64_128_4 4685, 32_128_2 4733 |
| 2048 | Wq | 64_128_4 (9334) | 32_128_2 9299, 64_128_4 9334, 64_256_8 9371 |
| 2048 | Wpv | 64_128_4 (1936) | 64_128_4 1936, 32_128_2 1947, 32_256_4 1975 |
| 2048 | Wshgu | 128_64_4 (497) | 64_128_4 478, 64_64_2 492, 128_128_8 497 |
| 2048 | Wshd | 128_64_4 (475) | 128_64_4 475, 64_256_8 476, 64_128_4 480 |
| 2048 | W512 | 128_64_4 (421) | 64_128_4 410, 32_256_4 411, 32_128_2 412 |
| 2048 | Wik | 128_64_4 (119) | 32_128_2 107, 64_64_2 108, 32_64_2 109 |
| 2048 | Walbe | 64_32_4 (118) | 128_64_4 86, 64_32_2 90, 128_64_8 98 |
| 2048 | Wsg | 64_32_4 (92) | 16_64_2 91, 16_32_1 92, 32_32_2 92 |
verification #
dense_mm_parity(existing parity): ALL PASS with the patched kernels.dense_mm_parity --tiles M [shape]/--ab M [shape]: every run exits 0 with "ALL BITWISE" (memcmp vs 128_64_4). Each invocation runs < 5 s (max seen 4.2 s under contention, 1.5-2.7 s typically).- window.cpp compile-checked via
ninja strata_metal_kernelsin the scratch tree. No full-model run (per the rules), so the end-to-end kprof gain still needs to be measured by the parent. - Instantiation cost: 26 extra tiles added ~0.8-1 s to the runtime MSL compile, which is why only the 4 the selector uses ship.
new_tilein the test mirrors window.cpp by hand. If one changes, change the other.