ARGONAUT LABS← Research overview

DEEPSEEK V4.1 FLASH Q4 / 27 SEPTEMBER 2026

Start the expert reads
before the GPU is done.

20.30 to 22.74 steady tok/s at 512 prompt / 512 generated, +12.0% over our published 21 September engine on the same three drives, with byte-identical output on every arm.

What was tested

DeepSeek V4.1 Flash Q4, 518,596,067,328 bytes, SHA-256 a5e2e2c3ada4b2e9…. M5 Max, 128 GB, the internal SSD plus two Thunderbolt 5 NVMe enclosures holding byte-identical verified copies, prefill reads split 10:5:5 and decode reads 10:6:6. Control: the binary built from our published tag v41-router-qualified-20260921 with its profile and 4,200 cached experts, the 20.375 tok/s champion of 21 September. Candidate: the same source tree plus three opt-in engine paths and 4,600 cached experts, profile v41-stack-20260927. Same drives, same model copies, same prompt, same session. This page is not a comparison with upstream ds4; the 15 September page has that.

Where a token's 49.6 ms went

Before this session the champion spent, per generated token, about 34 ms inside the GPU command buffers, 13.1 expert-cache misses at about 0.8 ms each, and around 5 ms of fixed CPU work. The large matrix-vector kernels already ran at 69 to 100% of the roughly 570 GB/s memory roofline and the three drives were at their read ceiling for six experts a token, so the remaining software was in the seams: how soon the miss reads start after the router has chosen, how many experts stay resident, and about 1,300 small dispatches a token.

The levers

Each lever was screened alone with interleaved pairs before the stack was qualified as a whole. A is the lever off, B is on; screens ran on the candidate binary with the 21 September profile unless noted.

Single-lever screens, steady tok/s, medians of two runs each
LeverWhat it changesScreenA → BGainPair deltas
Flag readbackA small kernel inside the running command buffer publishes the router's selected expert ids, a checksum and a sequence number to a shared mailbox. The CPU commits without waiting, polls the mailbox, and starts the cache check and the miss reads as soon as the ids land, while the shared-expert work, encoded into the same buffer before the commit, keeps the GPU busy. Falls back to the blocking readback after 50 ms; 8,000 polls, no fallback recorded.512/200 ABAB20.05 → 21.37+6.6%+1.28, +1.37
4,600 cached experts400 more resident experts than the 4,200 of the published profile. Steady misses per token fall 13.09 → 11.06 at 512 generated and 14.32 → 12.22 at 200. Measured on the published binary.512/512 BAAB20.25 → 20.85+3.0%+0.60, +0.60
4,600 cached expertsSame setting, 200-token screen.512/200 ABAB19.89 → 20.56+3.3%+0.72, +0.61
BF16 epiloguesThree standalone BF16 rounding passes (attention-output low projection and expansion, routed plus shared add) fold into the kernels that produce their inputs, keeping the same round-to-nearest-even boundary.512/200 ABAB20.04 → 20.13+0.5%+0.09, +0.09
Pipeline cacheA pointer-keyed front cache in front of the Metal pipeline-state lookup.512/200 ABAB19.96 → 20.12+0.8%+0.16, +0.17
Environment cacheCached getenv results; the decode loop consulted the environment thousands of times a token. Now the code default.512/200 ABAB19.92 → 20.01+0.5%+0.03, +0.15

Measurement

512 prompt tokens, 512 or 200 generated, greedy, context allocation 4,096, a fresh process and empty expert cache per arm, a physical-device sampler and a swap guard. Every arm must reproduce the published reference output hash for its length (f14f5a1dcb74… at 512, 8182ab832dcc… at 200), keep the requested cache, add no swap, and hold at least 1,600 MHz active GPU clock through decode as recorded by an independent powermetrics collector. No build ran and no second engine process existed during any arm. Prompt processing is prefill tokens divided by prefill seconds; generation excludes prefill and startup; steady also excludes the first decode step.

The qualification: eight arms in the order they ran; no observation dropped
ArmSidePrompt processingGenerationSteadyFirst step msGPU MHzOutput SHA-256
stack512-1Bstack: flag readback + 4,600 experts + BF16 epilogues + pipeline cache43.9022.2322.654611620f14f5a1dcb…
stack512-2Apublished 21 Sep binary, router profile, 4,200 experts44.8420.0020.263711620f14f5a1dcb…
stack512-3Apublished 21 Sep binary, router profile, 4,200 experts45.0620.0820.343701620f14f5a1dcb…
stack512-4Bstack: flag readback + 4,600 experts + BF16 epilogues + pipeline cache44.1422.4222.834441620f14f5a1dcb…
stack200-1Apublished 21 Sep binary, router profile, 4,200 experts45.2619.3219.9436916208182ab832d…
stack200-2Bstack: flag readback + 4,600 experts + BF16 epilogues + pipeline cache44.4321.5222.5044416208182ab832d…
stack200-3Bstack: flag readback + 4,600 experts + BF16 epilogues + pipeline cache44.0721.4822.4845516208182ab832d…
stack200-4Apublished 21 Sep binary, router profile, 4,200 experts45.1619.3119.9940116208182ab832d…

512 generated, BAAB: steady 20.30 → 22.74 tok/s (+12.02%, pairs +2.39 and +2.49), generation 20.04 → 22.33 (+11.40%). 200 generated, ABBA: steady 19.96 → 22.49 (+12.65%, pairs +2.56 and +2.49), generation 19.31 → 21.50 (+11.31%). Against the medians published on 21 September, 20.375 steady and 20.115 generation-inclusive, the new medians are +11.6% and +11.0%. All eight arms produced the reference output for their length.

Which change earns which number

Flag readback is most of it. The larger cache is the next third, and the three small items are under one percent each. The single screens sum to about 11.6%; the stack measured 12.0% and 12.6% as a whole, so the levers neither cancel nor compound much. The costs are real and small: prompt processing 44.95 → 44.02 tok/s (−2.1%) and a first decode step of 452 instead of 371 ms, both because the larger cache seeds more expert slabs after prefill. Decode drew about 3 W more.

Every other arm

The single-lever screens and the first attempt at the qualification, which the clock gate stopped: the published binary's control arms ran at 762 and 907 MHz, a low-clock mode this machine enters occasionally and whose cause is unestablished. The passing groups above were rerun after a probe arm held 1,620 MHz; nothing in the source or the settings changed between the attempts, and the faster stack arm from the rejected group was not substituted.

Screens and excluded arms, in run order within each group
ArmSidePrompt processingGenerationSteadyFirst step msGPU MHzDisposition
flag-readback-1Acandidate binary, flag readback off45.3319.4820.103661620single-lever screen, passed gates
flag-readback-2Bcandidate binary, flag readback on45.3320.6021.383981620single-lever screen, passed gates
flag-readback-3Acandidate binary, flag readback off45.0319.3719.993671620single-lever screen, passed gates
flag-readback-4Bcandidate binary, flag readback on45.0620.6521.363691620single-lever screen, passed gates
cache4600-512-1Bpublished binary, 4,600 experts on44.4220.5220.864491620single-lever screen, passed gates
cache4600-512-2Apublished binary, 4,600 experts off45.4920.0020.263671620single-lever screen, passed gates
cache4600-512-3Apublished binary, 4,600 experts off45.2119.9820.233631620single-lever screen, passed gates
cache4600-512-4Bpublished binary, 4,600 experts on44.1620.4920.834541620single-lever screen, passed gates
cache4600-200-1Acandidate binary, 4,600 experts off45.3419.1619.854051620single-lever screen, passed gates
cache4600-200-2Bcandidate binary, 4,600 experts on44.2219.6620.574931620single-lever screen, passed gates
cache4600-200-3Acandidate binary, 4,600 experts off44.9519.2419.934091620single-lever screen, passed gates
cache4600-200-4Bcandidate binary, 4,600 experts on44.1119.7120.544521620single-lever screen, passed gates
bf16-epilogues-1Acandidate binary, BF16 epilogues off44.9719.3920.033751620single-lever screen, passed gates
bf16-epilogues-2Bcandidate binary, BF16 epilogues on45.3919.3320.124511620single-lever screen, passed gates
bf16-epilogues-3Acandidate binary, BF16 epilogues off45.1719.4220.043661620single-lever screen, passed gates
bf16-epilogues-4Bcandidate binary, BF16 epilogues on45.0619.4420.133991620single-lever screen, passed gates
pipeline-cache-1Acandidate binary, pipeline cache off45.3419.3419.953661620single-lever screen, passed gates
pipeline-cache-2Bcandidate binary, pipeline cache on45.7419.4920.113621620single-lever screen, passed gates
pipeline-cache-3Acandidate binary, pipeline cache off45.3219.3319.963721620single-lever screen, passed gates
pipeline-cache-4Bcandidate binary, pipeline cache on44.9219.5120.133631620single-lever screen, passed gates
env-cache-1Aenvironment cache off45.0619.2219.914081620single-lever screen, passed gates
env-cache-2Benvironment cache on (code default)45.2319.2619.944011620single-lever screen, passed gates
env-cache-3Aenvironment cache off45.2419.3119.923661620single-lever screen, passed gates
env-cache-4Benvironment cache on (code default)45.0619.4520.073651620single-lever screen, passed gates
stack512-excluded-1Bstack: flag readback + 4,600 experts + BF16 epilogues + pipeline cache44.6822.3222.774861620excluded with its group: clock gate
stack512-excluded-2Apublished 21 Sep binary, router profile, 4,200 experts44.6813.8614.00409762excluded: clock gate
stack200-excluded-1Apublished 21 Sep binary, router profile, 4,200 experts45.1414.8015.14364907excluded: clock gate

What did not work

Caches of 4,750 and 4,900 experts exceeded the swap-growth guard on this 128 GB machine, with and without deferring the growth until the prefill staging pool is released. Turning the CPU keep-alive off on top of the stack measured −0.09%, inside the noise, so the setting stays. Memory-touching keep-alive kernels, read-pool spinning, completion polling, Q8 row tiling and SIMD-group variants, eighteen reader threads and pipeline pre-warming were each screened with interleaved pairs and rejected; the knobs remain in the source, off by default. Pre-warming experts, staging the release of the prefill pool, static buffer views and warming the slabs did not shorten the first decode step, which remains a separate target.

Against upstream, one to three drives

The stack above was qualified against our own previous engine. To place it against upstream, the same evening ran one interleaved session: pinned upstream ds4 bd66c40 (the base this fork is built on, phase markers only, same toolchain) on the internal SSD, against the fork's v41-stack-20260927 profile on the internal SSD alone, with one enclosure and with two. 512-token prompt, 200 generated, sequence U S1 U S2 U S3 S3 U S2 U S1 U, so every fork rung is bracketed by upstream arms. Upstream sizes its expert cache automatically (3,688); the fork requests 4,600.

Medians: upstream of 6 arms, each fork rung of two; multipliers against the upstream median
RungRunsPrompt processingSteady decodeGeneration incl. first step
upstream ds4 bd66c40, internal SSD only617.9610.189.90
this fork, internal SSD only (no replicas)230.98 (1.72×)18.66 (1.83×)17.45 (1.76×)
this fork, internal + one enclosure236.88 (2.05×)21.05 (2.07×)19.54 (1.97×)
this fork, internal + two enclosures242.41 (2.36×)22.25 (2.19×)20.74 (2.10×)

The one-drive rung is the portable result: same laptop, same single SSD, same model file, no replicas, 10.18 → 18.66 steady tok/s (1.83×) and 17.96 → 30.98 prompt processing (1.72×). Output is byte-identical to upstream on all 12 arms. Fork arms held 1,620 MHz with zero swap growth; upstream has no keep-alive and its active-weighted GPU clock is recorded rather than gated. The earlier ladder of 15 to 19 September (14.38 on one drive, 18.45 on three, against a 10.6 control) is superseded by this session for the chart on the front page.

Every arm of the ladder session, in run order; no observation dropped
ArmRungPrompt processingGenerationSteadyFirst step msGPU MHzOutput SHA-256
ladder-0927-1Uupstream ds4 bd66c40, internal SSD only17.769.9210.2064515238182ab832d…
ladder-0927-2S1this fork, internal SSD only (no replicas)31.1017.3618.5981216208182ab832d…
ladder-0927-3Uupstream ds4 bd66c40, internal SSD only17.919.7710.0667514918182ab832d…
ladder-0927-4S2this fork, internal + one enclosure36.8919.5221.0478416208182ab832d…
ladder-0927-5Uupstream ds4 bd66c40, internal SSD only18.009.9410.2264715228182ab832d…
ladder-0927-6S3this fork, internal + two enclosures41.2320.4722.1578216208182ab832d…
ladder-0927-7S3this fork, internal + two enclosures43.6021.0222.3560716208182ab832d…
ladder-0927-8Uupstream ds4 bd66c40, internal SSD only18.099.8210.1166115248182ab832d…
ladder-0927-9S2this fork, internal + one enclosure36.8719.5621.0777716208182ab832d…
ladder-0927-10Uupstream ds4 bd66c40, internal SSD only17.939.8810.1563515278182ab832d…
ladder-0927-11S1this fork, internal SSD only (no replicas)30.8717.5418.7478616208182ab832d…
ladder-0927-12Uupstream ds4 bd66c40, internal SSD only18.049.9610.2464015308182ab832d…

28 September follow-up: what four more hours bought

The next morning set out for another 10% and got 1.5%. Two opt-in changes qualified against the 27 September binary built from its tag, same session, same profile and cache, output identical on every arm: a post-MoE flush, which commits a layer's routed work as soon as it is encoded when that layer waited for expert reads so the GPU starts it while the CPU encodes the next layer, and an eviction pre-scan, which ranks cache victims while the CPU is idle on the router mailbox instead of scanning 4,600 entries on the critical path. Profile v41-stack-20260928.

Qualification against the published 27 September binary; steady / generation-inclusive medians of two runs
ShapeOrderPublished stackNew profileSteady gain (pairs)Inclusive gain
512 prompt / 512 generatedBAAB22.920 / 22.47023.260 / 22.755+1.48% (+0.30, +0.38)+1.27%
512 prompt / 200 generatedABBA22.555 / 21.48522.940 / 21.810+1.71% (+0.33, +0.44)+1.51%

Next-layer prediction, the only lever that could hide the expert-read waits, was re-tested through the flag-readback mailbox and lost in every form: top-2 prediction costs about 1.2 ms of GPU per token and buys 0.85 misses; reading only the predicted non-resident experts cuts misses by a third but the speculative reads share the drives with demand reads and displace resident experts. A token is now about 43 ms: 34 ms of kernels at 69 to 100% of the memory roofline, 5 to 6 ms of exposed read waits, 3 to 5 ms of command-buffer boundaries, 2 ms of CPU. The next real step is a GPU-driven expert gather that removes the per-layer command buffers, which is a multi-day change.

Same-day screens, interleaved ABAB at 512/200 on the candidate binary
LeverSteady A → BGainPair deltas
Post-MoE flush, layers that waited for reads22.41 → 22.72+1.38%+0.32, +0.30
Post-MoE flush, every layer (first control arm ran right after a swap incident)21.95 → 22.70+3.39%+1.26, +0.23
Eviction pre-scan (both arms with the flush)22.73 → 22.84+0.48%+0.19, +0.03
Next-layer prediction through the mailbox, top 222.28 → 22.06-0.99%-0.21, -0.23
Prediction, speculative reads for non-resident experts only, top 3 (one pair)22.42 → 20.59-8.16%-1.83

Evidence: the 28 September package, 35 arms with a verifier, release v41-stack-20260928. The drive ladder above was measured with the 27 September profile and is not re-run.

30 September follow-up: prefill

Prefill on this configuration staged only the experts the router selected, which cannot start before the layer’s router has run; the drives therefore idled while the GPU computed the previous layer, about 147 ms of reads alternating with 142 ms of compute per layer. Two opt-in changes, profile v41-stack-20260930: read-ahead staging reads the next layer’s likeliest experts during that window, ranked by a hotlist built from 19 other prompts, and tops up only what did not land; the two-wave routed MoE computes the experts already on hand before the top-up read and the rest after it, bit-exact because every (token, slot) row is written once and the slot sum is a separate fixed-order kernel.

Qualification against the published 28 September binary, both sides at a 4,200-expert cache; prompt tokens/s, medians of two
ShapeOrderPublished stackPrefill profileGain (pairs)Steady decode
512 prompt / 512 generatedBAAB45.5568.59+50.6% (+23.01, +23.05)21.08 / 20.93 (-0.71%)
512 prompt / 200 generatedABBA45.5068.09+49.6% (+21.93, +23.24)20.88 / 20.76 (-0.60%)
Drive ladder with the prefill profile, one interleaved session on 30 September; fork rungs at a 3,600-expert cache; tok/s and multiplier against the same-session upstream
MeasureUpstream, one driveOurs, one driveOurs, + 1 enclosureOurs, + 2 enclosures
Prompt processing16.1243.75 (2.71×)58.13 (3.61×)68.29 (4.24×)
Steady decode10.1116.77 (1.66×)18.67 (1.85×)20.02 (1.98×)

Conditions: the 4,600-expert champion cache tripped the swap-growth guard on a machine with fifteen days of uptime, and the session was run without a reboot; the qualification therefore used 4,200 and the ladder 3,600 on both sides, the ladder accepted swap growth of at most 64 MB per arm (largest observed 0.0 MB, every value recorded), and decode rungs sit below the 27 September ladder, which remains the decode reference. Output matched the published reference hash on all 30 qualification and screen arms and on all 12 ladder arms. Evidence: the 30 September qualification package, the ladder package, release v41-stack-20260930.

4 October follow-up: continuous reader, early Engram reads, conditional prior

Three more read-scheduling changes on top of the 30 September staging, all opt-in and all bit-exact. CPU-side stage marks found that at 512 prompt tokens the two Engram row tables were read synchronously at layers 1 and 14, about half a second each, because upstream overlaps those reads with the sweep only from 1,024 tokens and the drives were busy with expert staging; the profile lowers that threshold and issues the 12,288 random row reads per table from 128 threads so the first table lands inside layer 0. A continuous reader keeps the read-ahead lanes alive across layers instead of starting a thread per layer. And the hotlist is replaced by a conditional prior: how often each expert at layer L+1 followed each expert at layer L in 19 other prompts, applied to this prompt’s own layer-L routing counts (precision of the first 130 experts read ahead on the benchmark prompt: 76.7% → 82.9%; oracle 99.8%).

Qualification against the published 30 September binary, both sides at a 3,600-expert cache; prompt tokens/s, medians of two
ShapeOrder30 September4 OctoberGainSteady decode (30 Sep / 4 Oct)
512 prompt / 512 generatedBAAB68.8573.27+6.4% (+4.30, +4.53)20.12 / 20.12
512 prompt / 200 generatedABBA68.6773.16+6.5% (+3.93, +5.04)19.61 / 19.20
Drive ladder with the 4 October profile, one interleaved session; fork rungs at a 3,600-expert cache; tok/s and multiplier against the same-session upstream
MeasureUpstream, one driveOurs, one driveOurs, + 1 enclosureOurs, + 2 enclosures
Prompt processing17.3047.71 (2.76×)62.99 (3.64×)75.06 (4.34×)
Steady decode10.1716.94 (1.67×)18.64 (1.83×)19.31 (1.90×)

What did not work: a split router (attention and router in two token halves so the first half’s expert ids start the reads early) was made bit-exact and measured +1.5%: the three-drive prefill is drive-bound (177 GB read per 512-token prompt at the 25.5 GB/s ceiling), so certain reads merely replaced useful guesses while the extra drain lengthened the GPU window. Conditions: the 4,600-expert champion cache tripped the swap-growth guard on a machine with twenty days of uptime, and the session was run without a reboot; every arm here used 3,600 on both sides, the ladder accepted swap growth of at most 64 MB per arm (largest 0.0 MB), and GPU clocks held 1,620 MHz during decode on every fork arm. Qualification package · Ladder package · Release.

Limits

One machine, one model, one prompt at two generated lengths. The stack qualification is against our own published binary in the same session; the ladder is against the pinned upstream base of this fork, not current upstream ds4, and two runs per configuration give an observed range rather than a confidence interval. The 4,600-expert cache needs the headroom of a 128 GB machine. Keep-alive remains enabled and raises power draw. Identical output on every arm is regression evidence, not model-quality validation, and chat or server throughput was not measured.

Download sanitized measurements ↓

Transcribed from the results package in the engine repository, which carries every timing CSV with its hash, the phase accounting and 75 source checksums; a verifier script checks the package without rerunning inference. The JSON excludes machine paths, serials, prompts and model files.

Code. Release v41-stack-20260927 of our fork of antirez/ds4, with the results package and the profile notes. The engine paths are opt-in; the defaults and the earlier profiles are unchanged. Tooling: ArgoDrive.