# opus_r12_moemm — Round 12 Track A: mm_gu3 TG-memory shrink + Q6_K hoisted path + IQ2_XS sub-block hoist

Scratch: /tmp/opus_r12_moemm/{base,vU,vQ6,vJ,vP,vP0,vQ6J,vNoH,pad1k..pad4k}/ (kernel dirs, runtime-compiled).
Harness: prebuilt `vendor/strata/build-metal/moe_mm_parity --v3 --gu F --dn Q2_0 --tile 16,32,32,4 --dtile 64,32,2
--gu-bench --T T --ab DIR` with `STRATA_METAL_KERNELS=<A dir>`; 512 experts, M5. No change to moe_mm_parity.cpp was needed.
Each run took ~1.6–5 s, mostly compile. GPU check: only the idle :8080 server was running. During my runs I gave
sol-qsa-topk-and-flash its exclusive window on request.

## Patch: /tmp/opus_r12_moemm/opus_r12_moemm.patch (1 line, `git -C vendor/strata apply --check` OK)
Drops `F != T_Q6_K` from mm_gu3's pointer-hoisted fast-path guard, so Q6_K now takes the hoisted loop.
W8<T_Q6_K> is unchanged (the ushort loads stay). With QK=256: boff=(k0>>8)*210, j_in=(k0+kl)&255.

**Merge recommendation: conditional, see the T≈1k regression below.**

### Bitwise parity
- `--dump` (HS + y, T=256, 64 experts) for base vs patched kernels: memcmp == 0 on all 11 mm_gu3 formats
  (Q4_K Q5_K Q6_K Q8_0 IQ4_XS Q2_0 IQ3_XXS IQ2_XXS IQ2_XS IQ2_S IQ3_S).
- Full reference parity on the patched kernels: Q6_K PASS (rel L2 1.99e-05 vs the fp16-operand reference); IQ2_XS PASS (rel L2 1.79e-05, base and patched identical).

### Q6_K gate/up timing, paired --ab (A = base, B = patched; median µs per dispatch)
| T | A | B | diff |
|---|---|---|---|
| 512 | 24431 | 22894 | **-6.3%** (repeat -5.1%) |
| 768 | 27974 | 27430 | -1.9% |
| 960 | 26754 | 32031 | **+19.7%** |
| 1000 | 23644 | 29115 | **+23.1%** |
| 1024 | ~24–26k | ~27–31k | **+10…+24%** (8 paired runs, forward and swapped A/B, plus separate non-AB runs: base 24.6/23.9 ms, patched 30.8/32.0 ms) |
| 1100 | 24662 | 24902 | +1.0% (noise) |
| 1280 | 26705 | 25394 | -4.9% |
| 1536 | 29572 | 28345 | -4.2% |
| 1983 | 37259 | 35660 | **-4.3%** |
| 2048 | 38059 | 36456 | **-4.2%** (repeats -4.0%, -4.2%) |

The patch is a solid -4% at the kprof point (T=1983; ≈ -10 ms per 2k-token prompt across the 12 Q6_K layers).
But it reproducibly loses 10–23% for T≈960–1100 (≈2 tiles per expert at 512 experts). The base kernel is also
bimodal and non-monotonic in that band (T=512 24 ms, T=1000 23.6 ms, T=768 28 ms), which looks like
cache/scheduling sensitivity between the 2 tiles of an expert that read the same weights. I did not root-cause it.
Unroll_count(2) on the hoisted loop is far worse (+73% / +18%), and the j_in mask hoist (vQ6J) doesn't change it.
- Merge if prompt chunks are mostly ≥1280 or ≤768 tokens.
- Otherwise gate it host-side by T (not my file), or leave it out.

## Target 1 — TG-memory union/shrink: FALSIFIED (no occupancy limit at 12,416 B)
Padding probe: extra live threadgroup memory added to the base IQ2_XS mm_gu3 (T=2048, paired):
- +4 KB → 16.6 KB: +0.03%
- +8 KB → 20.6 KB: +0.01%
- +12 KB → 24.6 KB: -0.01%
- +16 KB → 28.6 KB: **+4.0%**

If 32 KB per core bounded residency, 16.6 KB would already drop it from 2 to 1 TG per core and cost a lot.
It costs nothing. So per-core TG memory is larger than 32 KB, and at 12.4 KB something else (registers) limits occupancy.
Shrinking below 12.4 KB can't add TGs.

Variant U (tab ∪ ot union = 8,192 B, plus a threadgroup barrier before `accg.store`), paired:
- IQ2_XS: +0.16% (T=512), +0.26/+0.27% (T=2048). Slower because of the barrier.
- Q6_K: -0.06% / +0.16%. Neutral.
- IQ3_S: -0.38/-0.32/-0.10% (T=2048), -0.47% (T=1024). Tiny; only 2 layers (≈0.2 ms).
- Not shipped.

Variant H (half or 2-round float `ot`): not built. The padding probe already shows TG memory isn't the limiter,
so neither H nor U has occupancy to gain.

## Target 2b — W8<T_IQ2_XS> sub-block hoisting: no gain
Measured on top of vQ6, paired at T=512/2048:
- vJ (j_in = (k0&255)|(kl&31), which makes l, hh and the in-group offset provably loop-invariant): -0.31%/-0.03%.
  Noise; the compiler already hoists them.
- popcount signs instead of the ksg table lookup, IQ2_XS (vP0, vP): **+1.8…+2.6% slower**. Rejected.
- Control: forcing IQ2_XS off the hoisted path costs +2.0…+2.2% at T=1000/1024/2048, so the existing IQ2_XS fast path is
  confirmed good and stable at T≈1k (unlike Q6_K).
