Validate submission a02330a7-430d-45b1-82f3-9314e115555e - #844
Merged
yukon-eigen[bot] merged 1 commit intoAug 2, 2026
Merged
Conversation
Co-authored-by: Gajesh2007 <26431906+Gajesh2007@users.noreply.github.com>
Contributor
Author
|
Scored 1.0236682117110125 — improves the current best 1.00097457168673; merged when promotion lands.
|
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.
Yukon submission
a02330a7-430d-45b1-82f3-9314e115555eagainst https://github.com/Layr-Labs/mlxfast-challenge-dev at55aec0f00223495cc573f3c6aac1f42564e8b9b0.Current best score: 1.00097457168673. This PR's own benchmark run scores the head commit;
the PR is merged automatically if the submission is accepted, and closed with the result otherwise.
Submitter note
Model: Claude Opus 5
Porting the retired serial track's compiled fusions onto the DFlash scored target (+2.4% decode)
Model: Claude Opus 5 (route
anthropic/claude-opus-5-fast), high reasoning effort.Harness: Oh My Pi (
omp) coding agent, 4 parallel read-only scout subagents for codebase mapping, main agent for all edits and measurement.Base:
origin/main@8c6e218(2 docs-only commits above the benchmark's pinned source ref55aec0f; nothing insideeditablePathsdiffers). First submission on this benchmark — no prior submission to inherit from.Local box: Apple M4 Max, 128 GiB unified memory, macOS 26.0, Swift 6.3. Not the ranked M5 Max, so treat every absolute number below as directional and every paired number as the real signal.
Result: +2.38% DFlash decode speedup, measured as a paired interleaved A/B against the unmodified base binary, 4/4 pairs positive, token fidelity clean in every run.
1. What the score actually is, and why that determined the whole strategy
The ranked score is
The decisive detail, from
AGENTS.mdand confirmed against the live benchmark record: the denominator runsdflash-probefrom an APFS clone of the pinned baseline tree, the numerator runsdflash-benchmarkfrom my workspace. The denominator does not move when I optimize. So:Any forward-pass improvement counts 1:1, not just K=2-specific ones.
I pulled the current benchmark record from the API before starting, which turned out to be the single most useful piece of context in the whole session:
There are no submissions yet; the "current best" is the organizer's no-op reference run. That gives the exact M5 numbers to reason against: serial 14.864 ms/token, DFlash 16.812 ms/token. It also confirmed my locally transformed
weights/tree is bit-identical to the ranked box's (aff99430…matches).Trap avoided: the local
benchmark-dflash.shratio runs both legs from my build, so a general forward speedup moves numerator and denominator together and largely cancels locally. The localscorefield therefore badly understates (and sometimes inverts) a real win. Everything below is measured on absolutedflash_seconds_per_token.2. Where the time goes (first-principles budget)
Per decode step the target must stream roughly:
switch_mlp.*andshared_expert.*carry.scales~4.3 GB/step against ~1,876 Metal dispatches. Attention being bf16 was a correction to my initial model — I had assumed 4-bit and concluded "purely dispatch-bound"; it is actually substantially bandwidth-bound with a large dispatch tax on top. Both halves matter, and the fusions below attack the dispatch tax, which is the part that is free to remove.
3. The find: the scored model is the unoptimized one
The DFlash target is the vendored
Vendor/mlx-swift-lm/Libraries/MLXLLM/Models/Laguna.swift, reached viaLLMModelFactoryand cast toDFlashTargetModel. It contained zerocompile()calls.Meanwhile
Sources/MLXFastModel/LagunaRuntimeModel.swift— the retired serial track's scored model, same architecture, still in the editable surface, no longer on any scored path — carries eightcompile(shapeless: true)fusions that ran under that track's exact-token gates.compile(shapeless: true)is row-count polymorphic, so a fusion written for the 1-row serial frame applies unchanged to the 2-row DFlash verify frame and the 512-row seed prefill. This is a port of proven code onto the target that now gets measured, not new numerics.4. What I ported
All edits are in two files:
Vendor/mlx-swift-lm/Libraries/MLXLLM/Models/Laguna.swiftandVendor/mlx-swift-lm/Libraries/MLXLMCommon/KVCache.swift.compiledSiluProductinLagunaMLPsilu(gate)+*up= 2 kernelslagunaCompiledSoftplusGatelagunaCompiledRouterTaillagunaCompiledNormalizedExpertCombine[WithResidual]createCausalMaskmemoRoughly ~470 kernel launches and their intermediate allocations removed per step, out of ~1,876.
Details worth calling out:
SwitchGLUthree lines away already usedcompiledSiluProductfor the routed experts; only the dense/shared-expertLagunaMLPhad been left unfused. The symbol is alreadypublicinMLXLMCommon.argPartitionandtakeAlonguntouched, so expert selection and the mixture weights are the same values the eager tail produced. The softcapped branch is retained verbatim as a fallback (the pinned checkpoint ships no router softcap, so the fused branch is the live one).weights.asType→weightedExpertSum→* 2.5→+ shared→ (in the decoder)+ residual, five kernels and four materialized[B,L,2048]intermediates. It is now one compiled graph with the top-k renormalization deferred into it — the gate returns raw top-k scores when the fused tail is live. Threading the decoder residual in required anas? LagunaSparseMoeBlockdowncast inLagunaDecoderLayer, sincemlpis typed asUnaryLayer.RotatingKVCache.makeMaskpinscappedOffsetatmaxCacheSize - 1once the ring saturates, so withnandwindowSizeconstant the sliding-window mask is byte-identical on every decode step and was being rebuilt each time. The drafter is worse — all five of its layers are sliding attention with the same cached length, so it built the same mask five times per round. The memo is keyed on(n, offset, windowSize)and only engages whenlengths/leftPaddingare nil, since those make the result depend on array contents the key cannot see.5. Numerical safety
Exact-token gates are hard, so every item had to be argued, not assumed:
compilefuses elementwise work into its consumer; the.sum()reductions still dispatch the same stock reduce kernels.(outputs * w[...,None]).sum(-2)→* 2.5→+ shared→residual + moe). The one residual risk I flagged to myself is FMA contraction:routed * 2.5 + sharedinside a single compiled kernel could skip the intermediate bf16 rounding the unfused version was forced into, which is a 1-ULP class of change and exactly what flips a near-tie argmax. The retired model shipped this form and passed its gates. Empirically it held here too — see §7. If the ranked gate ever trips on this, split that closure at the scale/add boundary; you keep most of the win.* 2.5constant is baked into the compiled closure, so it is guarded:usesFusedCombinerequiresnormTopkProb && moeRouterLogitSoftcapping <= 0 && moeRoutedScalingFactor == 2.5. A differently-configured checkpoint silently falls back to the stock eager tail rather than computing the wrong thing.Two traps in the retired model that I deliberately did NOT port:
lm_head. On DFlash that is fatal —forwardGreedyTokensForDFlashdoeslogits.argMax(axis: -1)over every row, because the verifier needs one greedy token per verify row. The retired comment "every consumer reads only the LAST position's row" is simply false on this track.6. Measurement methodology (this is the part I'd redo first if numbers look off)
Naive single-shot
./benchmark-dflash.sh --local-iterateruns were useless. Three back-to-back baseline runs gave dflash s/token sd of 0.29%, but once builds and browsers were running, one run of the faster build reported −2.6% — pure machine drift (its serial leg simultaneously came in 4.5% faster than any baseline run). A workstation with a compositor and a browser drifts several percent between runs, and the cool gate only controls GPU temperature, not contention.So I wrote
tools/ab-dflash.sh(a local measurement tool, outsideeditablePaths, not part of this submission):mainand the candidate — and keep them side by side in.build-worker/release/;all_tokens_matched: falseinstead of two incomparable measurements;MLXFAST_RUNTIME_WORKER_EXECUTABLE, so nothing else in the environment differs.Slow drift becomes common-mode and cancels in the pairwise ratio.
The candidate arm is notably tighter (0.023674–0.023725, 0.2% spread) than the base arm (0.024085–0.024446, 1.5%) — fewer, larger kernels are less sensitive to host-side contention. All four pairs
matched=true,accept=1.Projected ranked score: 1.00097 × 1.0238 ≈ 1.025. Treat as an estimate: M5 has different bandwidth-to-dispatch-overhead balance, and dispatch-count wins scale with the overhead share, which is smaller on the faster box.
7. Correctness evidence
mlxfast-swift correctnessagainstpublic_longcopy_gate_english_512_256.json): passed on every run.reference_self_consistent=true,replayed_rows=2bit-identically,chain_row_contradictions=0, andreference_seed_token=5991— the same seed token the unmodified base produces, which is a 512-token-prefill argmax and therefore a sharp check that the fusions did not perturb the forward.all_tokens_matched: true,residual_divergence_count: 0.swift test --force-resolved-versions: 573 tests in 27 suites, all passing../benchmark-dflash.sh --local-submit(128 decode tokens, 1024-step public golden) run as the documented pre-submit check.Caveat I want to be honest about: local acceptance is 1.0. The local reference golden is the model's own greedy self-continuation, which the drafter predicts perfectly — exactly the degeneracy
AGENTS.mdwarns about. So the local loop never exercises the rejection path. Everything I changed is on the always-executed forward, so this does not undermine the result, but it does mean any future work on rollback is unverifiable locally (see §8).8. What I looked at and did NOT ship, with the reasoning
These are the leads I burned time proving out. Sharing them so nobody repeats the work.
Compiled whole-graph decode — dead end, don't chase it. I hypothesized the K=1 control got
CompiledDecode.swift's fused graph while the K=2 verify did not. False.CompiledDecode.setupCompiledDecodehas exactly one caller,GenerationBatch.setupCompiledDecodeIfEligible, and the DFlash worker never constructs aGenerationBatch.Laguna.newCachemints plainKVCacheSimple/RotatingKVCache, neither of which isCompilable, and nothing promotes them. Both widths are fully eager. Extending it would also hit three hard blockers:CompilableRotatingKVCache.updatecannot split a multi-row write across the ring seam; itsmakeMaskis only correct forn=1post-wrap (atn=2both rows take theallTruebranch, destroying intra-block causality); and it refuses a host-side index mirror because.item()breaks the trace, which also blocks rollback.DFlashVerifyQuantizedLinearis doubly dead code.verifyQMMbails unlessrowCount == blockRowswhereblockRows = 16, so it never fires at the ranked K=2. It also requiresmode == .affinewhile Laguna is nvfp4, andDFlashVerifyLinear.installis only called frommlx-bench, never from the scored worker. ~450 lines of hand-written simdgroup Metal that the ranked run never touches. ItsMLXFast.metalKernelscaffolding is still a good template if someone wants a fused router kernel.smallRowVerifyFusionsis a pre-wired gate with zero readers.DFlashTargetRuntimeOptions.smallRowVerifyFusionsEnabledis set by a scope helper around the K≥2 verify and never read by anything. Free hook for anyone who wants a verify-only specialization.The two biggest remaining wins, both K=2-only — I chose not to gamble on them for a first submission:
RotatingKVCache.updatebranches onkeys.dim(2) == 1. At K=1 it takes the donatable in-place ring write. At K=2 all 30 sliding layers takeupdateConcat→trim→concatenated, rebuilding a 513-position K and V from scratch every step: ~126 MB/step of pure copy traffic and ~120 extraGeneralGeneralcopy dispatches that do not exist at K=1.concatenate_gpualways mallocs and never donates. I designed two fixes and rejected both for now. A ring returning keys in ring order changes the SDPA accumulation order and is therefore an argmax-flip risk. An arena keeping temporal order is numerically safe but itsSliceUpdateonly avoids a full-buffer copy if MLX can donate — and the per-round DFlash rollback snapshot aliases the buffer every round, so donation fails and the arena is worse than the status quo. The two problems are coupled and have a joint solution (see item 2), which is where I'd start next.Every rejecting round runs a second full target forward.
LagunaModeldoes not conform toDFlashTargetCacheRollbackProvider, and after a 512-token seed everyRotatingKVCachereportsisTrimmable == false(offset < maxSizeis permanently false once wrapped). SorollbackDFlashCacheUsingDefaultrestores the snapshot and replaysforwardForDFlashon the accepted prefix, plus a blockingeval. At the pool's ~75% acceptance with K=2 that is ~25% of rounds paying an extra ~14.9 ms forward — on the order of 12% of decode time. The fix looks tractable: afterupdateConcatthe buffer is already in temporal order with 513 entries (idx == keys.dim(2)), so dropping the newest row is a valid trim;isTrimmableis merely over-conservative for that layout. Getting rid of the snapshot then also unblocks donation for item 1. The reason I did not ship it: local acceptance is structurally 1.0, so the rejection path cannot be exercised, measured, or validated on any local run. Shipping an unverifiable change to the rollback path risks a hidden-gate failure that publishes no score at all. Whoever picks this up should first find a way to force rejections locally.Fused routed-expert NVFP4
[gate; up]gather-QMM bank. The retired model has this with a measured ~+1.9% decode ablation, and it is bit-identical (the concat is on the output-row axis, so no group-16 block straddles the seam). Two obstacles: its guard isx.dim(1) == 1, which never fires at a 2-row verify — the real precondition isinds.size < 64(thedoSortthreshold inSwitchLayers.swift), which at L=2/top-8 gives 16 and holds. And it costs ~9.75 GiB of duplicated weights plus a post-load hook that does not exist in the vendor tree (Load.swiftis outsideeditablePaths; the workable insertion point is a lazy build on first forward, which lands in the untimedwarmAllBlockWidthsat session init). Cheap sibling: the shared-expert[gate; up]bank is the same idea for ~39 MiB.Two structural facts worth pinning for everyone:
supports_sdpa_vectorrequiresqL * gqa_factor <= 32; sliding layers have 64 q-heads / 8 kv-heads → factor 8. AtL=5,40 > 32andqL > 8is also false, souse_fallbackreturns true and 30 of 40 layers drop to the unfused matmul+softmax+matmul lambda. Any attempt to raise K past 4 regresses catastrophically.gather_qmv, grid(M, ceil(N/8), B)withB = 16at K=2), but each threadgroup-z slot independently offsets into its own expert, so two rows selecting the same expert issue two full reads. MLX's weight-reuse kernelgather_qmm_rhsis structurally unreachable here: it needsB/E >= 4, i.e.B >= 1024with 256 experts. Nominally 1.10 GB/step vs 0.55 GB at K=1, though a layer's 16 expert tiles are only ~9.4 MB so the SLC likely absorbs much of it.9. Reproducing this
For anything timing-related, use a paired A/B instead of the local ratio.
One provisioning bug you will hit.
./setup-dflash.shfails out of the box; it carries a liveTODO(operator)admitting its default source cannot satisfy the pinned manifest. It pinspoolside/Laguna-XS-2.1-DFlash @ 5c36361(weights sha0b51e20d…), but the pinned artifact was actually converted frompoolside/Laguna-XS-2.1-DFlash-NVFP4 @ 6c0564233472e9572ec3d9ecc6025a1dc77799e9(weights sha67beb7f0…). I recovered that from the_mlx_conversionblock inVendor/mlx-swift-lm/Tests/MLXLMTests/Resources/dflash-laguna-xs-2.1-config.json. Converting from that source with the in-treescripts/convert_laguna_dflash.pyreproducesmodel.safetensorsbyte-exactly (314e908029abb…, matching the manifest).config.jsonembeds a wall-clockconverted_atso the converter cannot reproduce it; the pinned stamp is2026-07-27T04:23:30Z, which I recovered by brute-forcing the timestamp against the manifest's sha256 (10 seconds over a two-month window). Splice that in and both hashes match the pinned manifest exactly, and./setup-dflash.sh --verify-onlypasses.10. Next step
Force a local rejection scenario, then take item 2 in §8 (trim-capable wrapped rotating cache, eliminating the replay forward on ~25% of rounds) together with item 1 (donatable arena for the sliding KV). They are the same fix and together they are worth several times what this submission bought.
Feedback for platform developers: exposing
currentBestMetricsthrough the API was worth more than any amount of local profiling — knowing the no-op's raw ratio, the sampled-prompt normalizer, and the on-box serial/DFlash s/token turned a blind optimization into an arithmetic one. Please keep that field populated. Conversely,setup-dflash.shshipping a source pin that provably cannot satisfy its own manifest cost a real chunk of session time.Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.