Repository navigation
Add D512 support to Metal vector attention - #4459
Merged
Merged
Conversation
zcbenz
force-pushed
the
sdpa-vector-d512-pr
branch
from
September 7, 2026 23:46
5528d75 to
c56bf58
Compare
zcbenz
marked this pull request as ready for review
September 7, 2026 23:47
zcbenz
approved these changes
Sep 8, 2026
Instantiate the single- and two-pass Metal vector attention kernels for matching query and value dimensions of 512, and admit that dimension in the short-query fused dispatch. Add coverage for single- and two-pass lengths, float32/float16/bfloat16, query lengths 1 and 4, boolean and additive masks, non-contiguous masks, causal masking, batches, attention sinks, and force_fused engagement.
The D=512 cells run the generic (non-GQA) vector kernel, whose cost is dominated by reading K/V: the fused win over the unfused path grows with the key length and is only a few percent at short ones. Admit head dim 512 only at or above MLX_SDPA_D512_MIN_KL keys (default 1024, read per call) and keep the existing fallback below that. Cover the threshold in both tests: force_fused at a short key length raises with the new refusal, and the threshold override restores the 1-pass kernel at key lengths the default excludes.
zcbenz
force-pushed
the
sdpa-vector-d512-pr
branch
from
September 8, 2026 07:21
c56bf58 to
c5b35bf
Compare
zcbenz
force-pushed
the
sdpa-vector-d512-pr
branch
from
September 8, 2026 07:24
c5b35bf to
81d837b
Compare
aaishwarymishra
pushed a commit
to aaishwarymishra/mlx
that referenced
this pull request
Sep 11, 2026
Co-authored-by: Cheng <git@zcbenz.com>
rabbitson87
added a commit
to rabbitson87/mlx
that referenced
this pull request
Oct 9, 2026
sdpa_vector at head_dim 512 was opt-in (LUMEN_SDPA_VECTOR_D512=1) after it lost 66% to the matmul fallback at 128 keys. That loss is short-context only: from MLX_SDPA_D512_MIN_KL keys (default 8192) the two-pass vector kernel wins. lumen-rs task 021, M3 Max, Gemma 4 26B-A4B global layers: - per layer: -11% at 16K keys, -17% at 32K; 1K-8K within noise - end to end decode: -10.8% at 64K (forced on earlier: -8.5% at 32K) - against an f32 reference: cos 0.999998 (vector, 9K keys) vs 0.999992 (fallback, 600 keys) Upstream ml-explore/mlx ml-explore#4459 gates D=512 vector attention on key length the same way. LUMEN_SDPA_VECTOR_D512=1 still forces it at every length; =0 turns it off.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
D512 attention can now use the existing single-pass and two-pass Metal vector kernels. In the five cases below, this is 1.11–1.23× faster than the unfused fallback on M5 Max.
Potential users include Gemma 4 31B, 26B-A4B, and E2B, whose global attention layers use head dimension 512 and a query-to-KV head ratio of 8. Single-token decoding with at least 1024 keys and no array mask can take this path.
I've enabled it by default for one query, GQA factor 8, no array mask and at least 1024 keys. Other supported D512 vector shapes can use it with
MLX_SDPA_D512_MIN_KL=0; setting the threshold above the key length selects the fallback.I measured
c6dc20d95. The final head5528d75b9only adds test changes and has the same runtime source, but I haven't retimed it.All rows use D=512, one query, 32 query / 4 KV heads and 1024 keys. Speedups are paired geometric means of fallback time / fused time.
With TF32 disabled,
test_fast_sdpapassed 22 tests with 2 skipped. These cover both kernels and the default dispatch restrictions. I haven't measured model performance; the results here are for the five operator cases above.Controls and reproduction
At D=512 and kL=1024, the excluded fp16 controls were: GQA4 (
B1, qL1, hq32/hk8) 1.0400 [1.0179, 1.0627], with improvement magnitude unresolved; MHA (B1, qL1, hq8/hk8) 0.9555 [0.9382, 0.9732]; and multi-query GQA8 (B1, qL4, hq32/hk4) 1.0277 [1.0208, 1.0346]. They retain the fallback by default. Array-mask calibration failed the drift check, so no timing claim is made for it.Device: M5 Max, 128 GB. The local release build adds 102,944 bytes to
mlx.metallib. Each reported cell passed A/A calibration and the 5% drift check.Measured binary:
libmlx.dylib:1bec8a11f32a+mlx.metallib:ba939f89e04b, atc6dc20d9575bbde819ae731b73b49a53cafcf5b0. The final-head test build has the same library hashes.Build the reviewed commit from source and run the tests:
Save the script below as
repro_4459.pyin the built checkout root. Use the same Python environment used to build MLX, and stop other GPU work before measuring.--smokechecks outputs and exercises short timing loops without estimating speedups. Use--helpto select a single case. Output files must be new names.The script includes the five reported cases and the three excluded controls. It uses seed 0, 250 batches of eight dependent calls, and checks fused admission with
force_fused. The table used adaptive A/B sampling (9–12 pairs); this script uses 15 fixed pairs.This is an independent reproducer of the workloads, not the driver used for the existing table. It warms both arms, runs 15 alternating A/A pairs, then 15 alternating A/B pairs. Each stage gets 60 seconds of saturation, with a 90-second cooldown before A/B. It reports the geometric mean of OFF/ON time ratios and a Student-t 95% interval in log space. A/A must include 1 in its interval; either stage stops on more than 5% within-arm drift, retaining the rejected result without retrying. It does not implement the original sign-test, Wilcoxon or A/A-noise significance checks.
The JSON includes raw sample times, input shapes, numerical checks, source revision, device and binary hashes. New estimates depend on the machine and build; they do not replace the measurements above.