Child of #203. Two efficiency gaps inside llm_mmq_q4k_soa_acts_n16 (PR #202), measured on Qwen3-8B Q4_K_M @ 4070 Ti, N=8:
1. Grid starvation on the [4096 x cols] shapes (~2 ms/step): Q/O + the Q4_K ffn_down half run at BM=64 -> grid = 64 blocks on 60 SMs (avg 49.7 us vs 18.7 us floor for Q/O). Split the K-range across 2-4 blocks per row-strip with a deterministic second-pass reduce (split-K scratch + tiny reduce kernel — NO fp atomics; run-to-run determinism is load-bearing for the test oracles). Alternative: BM=32 tile (128 blocks, 4 warps) — simpler, no reduce pass, weight still read once.
2. cp.async staging (~1-1.5 ms/step): gate/up sits at 1.7x floor (95.7 us vs 56) and the K-loop is staging-latency-limited — the super-block K-step (8 mma/barrier pair) was the difference between losing to the WS matvec and +28% aggregate, but the global->register->shared round-trip still bounds the loop. The Track B helpers (sharpi_cp_async16/commit/wait) and a proven double-buffered kernel shape (llm_mmq_q8_0_soa_acts_cpa) already exist; the SoA quants are 16-B-aligned and cp.async-eligible. Note the Track B precedent: it was kernel-level +10-15% but e2e-NEUTRAL on prefill — decode N=8 is a different regime (the kernel IS 49% of the step), so measure e2e before defaulting.
Also fold in (low priority, ~1.7 ms combined): K/V [1024x4096] Q4_K (6.6x floor, WS fallback below the rows>=2048 threshold) and q6k attn_v (16x floor, 0.77 ms) — a BM=32 tile or a fused QKV matmul would cover them.
Expected combined: N=8 ~317 -> ~380-400 t/s with #204 still pending.
🤖 Generated with Claude Code
Child of #203. Two efficiency gaps inside
llm_mmq_q4k_soa_acts_n16(PR #202), measured on Qwen3-8B Q4_K_M @ 4070 Ti, N=8:1. Grid starvation on the [4096 x cols] shapes (~2 ms/step): Q/O + the Q4_K ffn_down half run at BM=64 -> grid = 64 blocks on 60 SMs (avg 49.7 us vs 18.7 us floor for Q/O). Split the K-range across 2-4 blocks per row-strip with a deterministic second-pass reduce (split-K scratch + tiny reduce kernel — NO fp atomics; run-to-run determinism is load-bearing for the test oracles). Alternative: BM=32 tile (128 blocks, 4 warps) — simpler, no reduce pass, weight still read once.
2. cp.async staging (~1-1.5 ms/step): gate/up sits at 1.7x floor (95.7 us vs 56) and the K-loop is staging-latency-limited — the super-block K-step (8 mma/barrier pair) was the difference between losing to the WS matvec and +28% aggregate, but the global->register->shared round-trip still bounds the loop. The Track B helpers (
sharpi_cp_async16/commit/wait) and a proven double-buffered kernel shape (llm_mmq_q8_0_soa_acts_cpa) already exist; the SoA quants are 16-B-aligned and cp.async-eligible. Note the Track B precedent: it was kernel-level +10-15% but e2e-NEUTRAL on prefill — decode N=8 is a different regime (the kernel IS 49% of the step), so measure e2e before defaulting.Also fold in (low priority, ~1.7 ms combined): K/V [1024x4096] Q4_K (6.6x floor, WS fallback below the rows>=2048 threshold) and q6k attn_v (16x floor, 0.77 ms) — a BM=32 tile or a fused QKV matmul would cover them.
Expected combined: N=8 ~317 -> ~380-400 t/s with #204 still pending.
🤖 Generated with Claude Code