diff --git a/docs/shader-tuning.md b/docs/shader-tuning.md index 60651ec..85a9fa9 100644 --- a/docs/shader-tuning.md +++ b/docs/shader-tuning.md @@ -47,3 +47,17 @@ capped occupancy ~640 threads; bigger tiles lower shared-per-thread (shared∝BM → ~768 threads → +10%. Progression so far: **5.8 (broken) → 23.4 (i1) → 26.2 (i3)**, ~30% of roofline. Iter4: aggressive BM=128 (BLOCK 512, ~1024 threads) to saturate the register ceiling; then the only remaining lever is a mul_mm.comp register-pressure cut (f16 acc / fewer dequant temporaries). + +## Iteration 4 — BK_STEP 4→2 (2× occupancy via halved shared) — L path WINS, M regressed +Fable course-corrected: the Q4_K prefill shader is `mul_mmq.comp` (int-dot), shared = +BK_STEP·(20·BM+36·BN). Under BK_STEP=4 iter3 was the warptile occupancy ceiling (2 wg/512 threads); +BK_STEP=2 → 4 wg/1024 threads (2× occupancy). +| ubatch | iter3 | iter4 | | +|--------|-------|-------|--| +| 64 (M) | 20.6 | **15.5** | ↓ (M shares mul_mmq.comp; extra barriers hurt the saturated M path) | +| 128 (L) | 25.8 | **28.1 (+9%)** | ↑ tls→{16,48} | +| 256 (L) | 26.2 | **28.5 (+9%)** | ↑ | +BK_STEP is a global `#define` → hit M too. Fix: scope BK_STEP to the L pipeline (spec constant / +per-variant define). Then BK_STEP=1 for full 2048-thread occupancy. Progression (L path): +**5.8 → 23.4 → 26.2 → 28.1 t/s** (~4.8× from broken, ~35% of roofline). Then f16-acc (iter5) to +break the register band toward 2048 threads.