Skip to content

perf(cuda/quant): sm_120/121 qmm CTA tile (M=128) for Blackwell prefill#723

Merged
inureyes merged 2 commits into
mainfrom
perf/637-sm121-qmm-tile
Jul 10, 2026
Merged

perf(cuda/quant): sm_120/121 qmm CTA tile (M=128) for Blackwell prefill#723
inureyes merged 2 commits into
mainfrom
perf/637-sm121-qmm-tile

Conversation

@inureyes

Copy link
Copy Markdown
Member

Summary

Raises the CUDA quantized-matmul (qmm_sm80) CTA tile M cap from 64 to 128 on Blackwell consumer GPUs (sm_120/121, cc_major >= 12), recovering +31-38% prefill throughput on GB10. Part of epic #623.

Closes #637

Root cause

qmm_sm80's make_cta_tiler caps tile_m at 64 (Ampere-tuned). On GB10 (sm_121) the Ampere tile leaves the SMs idle: ncu shows 35-47% SM / 52-68% memory throughput, and 4bit prefill runs at 0.55x the bf16 ceiling. (The sm_90 Hopper kernel isn't compiled for arch 121, so Blackwell falls to the Ampere qmm_sm80.)

Fix

Arch-gated make_cta_tiler (overlay qmm_sm80.cu): tile_m cap = 128 when device.compute_capability_major() >= 12, else 64 (stock). A tile sweep confirmed tile_m is the only safely-tunable axis (wider tile_n / deeper tile_k break the fixed-MMA shared-memory layout -> JIT failure). Adds MLXCEL_QMM_TILE_M/N/K env overrides as a tuning hatch.

Benchmark (GB10, full detail committed under docs/benchmark_results/)

model forced tile_m=64 arch-gated default (128) delta
llama-3.1-8b-4bit prefill @8192 2213 3054 +38%
qwen2.5-7b-4bit prefill @8192 2410 3168 +31%
  • Greedy parity: default(128) vs forced(64) generated tokens byte-identical.
  • Decode (m=1): unaffected (single-seq decode takes the qmv path, never reaches qmm_sm80).
  • 0.55x -> 0.72x of the bf16 ceiling.

Scope / not addressed

Validation

  • C++ overlay change only (no Rust touched). Validated by greedy parity + prefill benchmark (the right gates for a GEMM tile change).
  • make release-cuda builds clean. The arch gate uses runtime compute_capability_major(), so it works for both 121 and 121a builds (both cc 12.1 at runtime).

Acceptance criteria (#637)

  • Findings doc in docs/benchmark_results/ (achieved-vs-ceiling, sm_121 dispatch map, ncu roofline, go/no-go).
  • >= 25% prefill @8192 on llama-3.1-8b-4bit on GB10 (measured +38%), no decode regression, parity green.
  • Both 121/121a build (arch-gate is runtime cc, not compile-time arch string).

qmm_sm80 used the Ampere CTA tile (M=64), which underutilizes Blackwell SMs:
ncu shows 35-47% SM / 52-68% memory throughput on GB10 (sm_121), and 4bit
prefill ran at 0.55x the bf16 ceiling. Raising the CTA tile M cap to 128 on
sm_120/121 (cc major >= 12) fills the idle SMs.

The overlay make_cta_tiler gates the tile_m cap on compute_capability_major():
128 on Blackwell consumer parts, 64 (stock) on sm_90/sm_80. Small-M decode is
unaffected (min(cap, next_power_of_2(m)) stays small; m=1 takes the qmv path).
A tile sweep confirmed tile_m is the only safely-tunable axis: wider tile_n or
deeper tile_k break the fixed-MMA shared-memory layout (JIT failure).

GB10 measurements (arch-gated default vs forced tile_m=64):
- llama-3.1-8b-4bit prefill @8192: 2213 -> 3054 tok/s (+38%)
- qwen2.5-7b-4bit  prefill @8192: 2410 -> 3168 tok/s (+31%)
- greedy parity identical; decode unaffected.

Findings, dispatch map, and ncu evidence in docs/benchmark_results/. Small-M
batched decode still uses per-row qmv (no amortization) and is out of scope
here; it is the gap behind #714's --parallel 4 CUDA regression and needs a
dedicated small-M kernel upstream.
@inureyes inureyes added type:performance Performance improvements priority:medium Medium priority area:core mlxcel-core: MLX FFI, primitives, KV cache, layers labels Jul 10, 2026
…(review)

pr-reviewer LOW nits: the findings doc said MLX 0.32.0 but the pin is 0.32.1
(57c66cac), and the prefill deltas differed across the PR body, doc, and code
comment. Standardize on the same-session paired measurement (arch-gated default
tile_m=128 vs forced tile_m=64): +38% llama-3.1-8b-4bit, +31% qwen2.5-7b-4bit
@8192, and note the ~5% run-to-run variance of the tile_m=64 baseline. No code
behavior change.
@inureyes inureyes merged commit 4e8dbc6 into main Jul 10, 2026
5 checks passed
@inureyes inureyes deleted the perf/637-sm121-qmm-tile branch July 10, 2026 04:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core mlxcel-core: MLX FFI, primitives, KV cache, layers priority:medium Medium priority type:performance Performance improvements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perf(cuda/quant): sm_120/sm_121-tuned quantized GEMM for Blackwell prefill

1 participant