You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Option A from the M1 decision: bnb_mps_gemm_4bit (csrc/mps_ops.mm) encodes, on ONE
command buffer with ONE commit + ONE blocking wait, (1) a chunked 4-bit dequant kernel
(dequantize_4bit_chunked_fp32/fp16: one thread per 32-element uint4 chunk, writing
(T)(code[nib]*absmax) into a growable private scratch MTLBuffer -- the same rounding as
the oracle's B_dq.to(dtype)), (2) a shape-cached MPSMatrixMultiplication computing
A[M,K] . B_dq[N,K]^T (row-major, transposeRight), and (3) an optional bias epilogue
kernel out[m,n] += bias[n]. The single sync is the structural win: the previous
dequant + F.linear tail paid the ~0.15-0.25ms cross-queue sync twice per call.
bf16 is excluded by the Python router: MPSMatrixMultiplication hard-asserts on anything
but fp32/fp16/int8/int16 (probed on macOS 26.4.1), so bf16 keeps the existing
dequant + F.linear fallback verbatim. Other guards mirror the fused gemv (K % 32 == 0,
power-of-two blocksize >= 32, packed-size check, hasattr guard for stale dylibs); nested
absmax is still unpacked to plain fp32 absmax before routing, unchanged.
Parity: tests/test_mps_parity.py -k "gemm_4bit or gemv" = 69 passed under
BNB_MPS_REQUIRE_NATIVE=1. Native path asserted via spy (fp32/fp16 x nf4/fp4 x +/-bias x
+/-nested absmax); bf16 and K%32!=0 fallbacks asserted; graceful fallback with the
native handle off covered. fp32 vs MPSMatMul accumulation stays within the documented
1e-5 atol at the calibrated K <= 256 (the one trip found was in the PURE-TORCH fallback
composition at K=256/M=4; the fallback test pins K=64 like the main gemm test -- the
documented tolerance is unchanged).
Speedup vs the dequant+F.linear fallback (nf4/bs64, N=K=4096, 30 iters,
benchmarks_wip/bench_gemm_baseline.py): fp16 2.5x (M=8), 1.5x (M=64/512), 1.08x
(M=2048); fp32 1.6x/1.5x (M=8/64), 1.1x (M=512), ~1.0x (M=2048). Win = one sync + a
much faster chunked dequant at small/medium M; flat at M=2048 where the GEMM dominates
and MPSMatMul ~= F.linear.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
0 commit comments