Conversation
Add D96/V64 vector kernels and make the NAX value tile width independent of the Q/K head dimension. Register the JIT and no-JIT kernels and cover dispatch, fallbacks, masks, and GQA in the SDPA tests.
wyanzhao
marked this pull request as ready for review
September 14, 2026 00:56
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.
Summary
Add Metal scaled dot-product attention support for
Dqk=96, Dv=64, theasymmetric head dimensions used by MiniCPM3. This adds one-pass and two-pass
vector kernels, lets NAX use an independent value tile width, and registers the
new kernels in JIT and no-JIT builds. Tests cover one- and two-pass MHA/GQA,
masks, sinks, non-contiguous inputs, interleaved value tensors, and the existing
D=96, V=96path.The existing short-query rules select the vector path. Full attention requires
NAX, and float32 also requires TF32; other cases keep the existing fallback.
MiniCPM3 performance
Measured on an M5 Max with
mlx-community/MiniCPM3-4B-4bit
snapshot
7a5f80ff3161be21f0a90e9be7137495633b2b99. The baseline kept bothnew D96/V64 kernels off. The candidate enabled NAX for prefill, Vector for
decode, or both for generation. Ratio is baseline time divided by candidate
time; each row uses nine alternating adjacent pairs.
NAX prefill measures materialized first-token logits with 512-token chunking.
Vector decode starts both configurations from the same baseline KV cache and
consumes the same eight tokens. Generation uses a fresh cache and includes
generation and detokenization, but not input tokenization.
Validation
Route tracing confirmed the NAX kernel during prefill and the vector kernel
during decode; configurations with those kernels off used the existing path.
First-token and decode logits stayed within the preset tolerance, top-1
predictions matched, and both configurations generated the same 16 tokens.
test_fast_sdpamodule passed 28 tests and skipped 2 by platform guards.on all six exact operator cells.
pre-commit run --all-filespassed.Quick reproduction
Build
mainand this PR in separate clean checkouts and virtual environments,then install MLX and
mlx-lmin each:python -m pip install -e . python -m pip install mlx-lmSave the script below as
bench_d96_v64.py. Run the same command with thePython executable from each checkout;
mainis the baseline and this PR is thecandidate.
Run all nine cells in both environments with no other GPU work active:
This is a quick workload comparison; results will vary with the machine and
build.
the diff and verified the results above.