Add D256 to GQA-8 two-pass vector attention - #4477
Open
wyanzhao wants to merge 2 commits into
Open
Conversation
wyanzhao
marked this pull request as ready for review
September 8, 2026 20:00
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.
The GQA-8 two-pass kernel already shares K/V loads across query heads for D64 and D128. D256 can use the same kernel with two query heads per simdgroup: its float threadgroup arrays take 16,512 bytes, while four heads would exceed 32 KiB.
This adds that specialization for single-token decode with at least 8192 keys, matching query/value dimensions, no array mask and no sinks. It fits the D256, 16/2-head full-attention layers in Qwen3.5-35B-A3B when using an ordinary floating-point KV cache. Quantized KV cache uses a separate path.
The six measured cells below are faster on M5 Max. Other Metal GPUs and whole-model throughput have not been measured. Tests cover all three dtypes, odd key lengths, batch 2 and sliced KV. From
python/tests,MLX_ENABLE_TF32=0 python -m unittest -v test_fast_sdpapassed (28 tests, 2 skipped);uvx pre-commit run --all-filespassed.Times are arm medians; ratios are paired geometric means of main/PR time, with 95% CIs (
MLX_ENABLE_TF32=0). The table was measured atb04aea2a7against5778a97c0; merge commit288906a50was correctness-tested but not retimed.Benchmark reproduction
Measured builds: main
5778a97c0, candidateb04aea2a7; Apple M5 Max, 128 GiB, macOS 27.0 (26A5425a). Build separate source checkouts with identical Release settings and Python bindings under each checkout'spython/directory. Save the script below assdpa_microbench.py.Set
--dtype,--q,--k,--hq,--hkand--batchfor each row. The script reports seconds per call using a four-call dependent chain. Keep other GPU work idle; thermal-limit telemetry was unavailable during these measurements.Use 30 fixed main/main calibration pairs followed by 30 main/PR pairs, alternating package order, with 60 seconds of preconditioning and 30 seconds between batches. The float32 cells used a separate session with five minutes of initial cooling.
Reject arm-median drift above 5% between session halves; allow one retry after 120 seconds of cooling with doubled preconditioning. The float32 k32768 A/B used that retry. Retain all pairs, including flagged outliers, in the paired log-ratio mean and Student-t 95% interval. Do not pool sessions; treat overlap with the calibration interval as unresolved. All six comparisons passed calibration and drift checks.