Pack gated_delta_seq: 8 value rows per SIMD-group - #4409
Open
wyanzhao wants to merge 1 commit into
Open
Conversation
tpegolotti
force-pushed
the
gated-delta-update
branch
from
August 26, 2026 14:29
24ce3d9 to
60af0df
Compare
wyanzhao
force-pushed
the
packed-gdn-seq-4020
branch
from
August 26, 2026 16:44
42d1178 to
10c6a84
Compare
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
This ports the packed sequential layout from
ml-explore/mlx-lm#1559
into
gated_delta_seqon this branch, at @tpegolotti's invitation on#4020. The change is one kernel plus the two dispatch lines in
eval_gpu: eight value rows share a SIMD-group (four lanes per row,32 contiguous state elements per lane); the two full-SIMD
simd_sumsbecome an in-lane three-level local tree plus
simd_shuffle_xor(1, 2);the grid / threadgroup go from
(32, Dv, B*Hv)/(32, 4, 1)to(32, Dv/8, B*Hv)/(32, 2, 1).static_assert(Dk == 128)andDv % 8 == 0are added; the API, ABI, instantiations, chunk-8 kernel,and NAX-16 kernel are untouched.
Bitwise equivalence
Each four-element partial keeps the original lane's sequential
accumulation, so the local work matches the unpacked per-lane loop.
IEEE addition is commutative, which lets the first three xor-tree
levels (
1,2,4) fold into that in-lane tree without changingbits; the last two levels (
8,16) becomesimd_shuffle_xor(1)andsimd_shuffle_xor(2)inside the four-lane row group. The associationis therefore the same butterfly the current
simd_sumimplements onthis toolchain.
Validation (M5 Max, two builds of this branch at 285be14 — base vs
patched):
mx.array_equalon bothyand the final state — heads {(24,24),(32,32),(16,32),(16,48)} ×{bf16, fp16, fp32} × T ∈ {1, 7, 16, 257, 1111} × B ∈ {1, 2} (B=2 for
the (16,32) pair), plus chunk-8 and NAX-16 control cases (also
bitwise-equal, i.e. untouched).
python/tests/test_fast_gated_delta.pyon the patched build: 6tests, OK (1 skipped: torch not installed).
explicit xor-tree: 0 mismatches.
uvx pre-commit run --all-files: all hooks pass, no reformatting.Performance
Absolute latency drifted by up to ~90% across rounds on this chassis
(thermal), so the numbers below are within-round paired ratios, not
absolute times. Five interleaved rounds, with the starting side
swapped (three rounds base-first, two patched-first) to cancel order
bias.
benchmarks/python/gated_delta_bench.py, sequential kernel(
GATED_DELTA_CHUNK=0). First three rows: median (min–max) ofper-round paired ratios
seq_base / seq_patched. Rows 4–5: range ofper-cell medians.
Default T ≤ 16 path (decode / short prefill), separate interleaved
probe: B=1 T=1/4/16 faster by ~7–24%, B=8 T=16 ~15%; no case
regressed beyond noise.
This port speeds up the sequential kernel that is the denominator of
the speedup plots on #4020, so those figures will need to be
recomputed or annotated once this lands.
Reproduction
gdn_seq_bitwise_ab.py
Out of scope (pre-existing, noted for completeness)
use_fallbackdoes not yet admit the newly instantiated (16,16) / (16,64)head pairs, so those instantiations are currently unreachable.
g.ndimguard: a 4-D (vector) gate that passes the shape checks would beread by the kernels as
[B, T, Hv].