Fix/speculative shape invariance - #4279
Conversation
|
Ran this on an M5 Pro (macOS 27.0) at They do. Quantized matmul, over the grid your third test uses (K=4096, N=512, M=2..4, transposed): SDPA, your first test's scenario with Lk in {1028, 8196, 16388, 65540}: Dense matmul at your M=4 shape fails
Cost when enabled, median of 11 alternating A/B reps at K=N=4096 bf16, M=1 kept as a control since the gate needs M>1: Positive every run, but the magnitude moves around, so 10-25% at these shapes rather than a single number. The matmul gate needs |
|
@eyupcanakman Thanks for your tests. The natural-layout B exclusion wasn’t intentional. I’ve fixed the canonical GEMV dispatch to support both B orientations and added coverage test. It reproduces before the fix and passes afterward on my M2. I do not have an M5 unfortunately 😀 a rerun on your M5 would be appreciated. |
zcbenz
left a comment
There was a problem hiding this comment.
It was brought up several times before about providing a way to get deterministic results with different batch size, but we do not plan to provide any of such support. It would complicate kernel dispatching a lot, and anyone needing that would definitely turn to other specialized inference engine in the end.
Proposed changes
Related issue: ml-explore/mlx-swift-lm#542
Opt-in batch-invariant execution mode for selected Metal inference kernels. It is intended for workloads such as greedy speculative decoding, where evaluating a token independently and evaluating the same token inside a short verification block must produce identical results.
Problem
Autoregressive decoding normally evaluates one token at a time, while speculative decoding verifies several tokens in one target-model invocation.
Although these computations are mathematically equivalent, their shapes currently select different Metal execution paths:
gemv_wideor GEMM.Floating-point reductions are not associative. Consequently, the different kernels can produce slightly different BF16 results for the same logical token. Across many transformer layers and decoding rounds, these differences can eventually change the ordering of two close logits and cause greedy
argmaxdecoding to diverge.This is especially visible with quantized models and long contexts, but the underlying problem is shape-dependent floating-point reduction order rather than quantized matrix multiplication itself.
Changes
This PR introduces
MLX_METAL_BATCH_INVARIANT_LIMIT, with matching runtime APIs:When the relevant leading/query dimension is within the configured limit:
The setting is process-wide and stored atomically. It defaults to
0, meaning disabled, so existing execution and performance are unchanged unless an application explicitly opts in. Long prefills and dimensions above the configured limit continue using the existing optimized paths.The mode is intentionally opt-in because selecting canonical reduction kernels can reduce performance. Applications only need to cover their maximum speculative verification width.
Checklist
Put an
xin the boxes that apply.pre-commit run --all-filesto format my code / installed pre-commit prior to committing changes