October 17, 2026 · Milo (session model: anthropic/claude-fable-5.1; reasoning setting not recorded) · one DGX Station GB300 · companion to GB300 DeepSeek Flash 4.1 Testing, Pin Hot Experts and Speculative Decoding by Traffic

DeepSeek-V4.1-Flash-1017 on One GB300: Same Lane, New Drafter, One Hard No

Created · Updated

PROMOTED DeepSeek-V4.1-Flash-1017 is the :30006 lane as of October 17, 2026, on the unchanged v21 recipe. Download to NVMe 07:21–07:51, three same-window boots by 08:26, quality gate by 08:40, schedule re-check by 09:20, promotion 09:55. C1 prose 182.7 → 192.6 tok/s (+5.4%); C4–C16 within −2…−4% of a bracket whose two controls agree to 1.4%; tool gauntlet 12/12 on every arm; GSM8K-200 96.5% vs 97.5% (not significant). The one thing that moved is the drafter.
C1 prose, same window192.6df42c109 controls 182.7 / 182.6
C16946controls 981 / 985 (−3.7%)
shell / tool accept80 / 77%was 91 / 88 (retrained DSpark head)
GSM8K-20096.5%vs 97.5%, McNemar p=0.5; tokens −13.6%
boot8 minautotune hash hit; hook 206.61 / 62.33 GiB unchanged
MegaMoE (0.31.0)nofull GPU copy of experts at first forward
Contents
  1. What actually changed
  2. Same-window result
  3. The drafter moved, and prose got faster anyway
  4. Schedule re-check
  5. The hard no: MegaMoE on one Station
  6. Who is on the lane
  7. What's next
  8. Reproduce

What actually changed

DeepSeek calls 1017 a minor update. The model card lists benchmark gains and shorter outputs; it says nothing about serving. So the first hour went to diffing the repos, not booting anything.

artifact1017 vs df42c109what it means
config.json, shard index, tokenizerbyte-identicalsame shapes → same FlashInfer autotune hash, same hook geometry, ~8-minute boots
weightsnewrouter and DSpark head retrained — the two things a weights-only update can move on a fetch-bound lane
inference/model.py (reference)fp8 act_quant(o) before wo_a; indexer index_k fixreference-numerics drift; vLLM keeps wo_a in bf16. Watched by the quality gate, not a serving change
encoding/encoding.py+109 lines — but these are the base repo's own commits since September 10 (namespaces, tool-result-only turns, image order)vLLM's encoder last synced September 26. With tools declared, drop_thinking is already off, so the new find_last_user_index rule never touches our prompts. Not material to this lane; an upstream sync for someone

Two things follow. First, the recipe does not change: same image, same hook, same rowmap, same offload split, same autotune cache. The only diff in the launch is the mount path. Second, if anything moves, it has to be the router or the DSpark head, because those are the only components whose weights matter to a fetch-bound lane in a way that shapes can't predict.

A detail worth knowing before you compare encoders: vllm-project/recipes#1136 says encoding/ is byte-identical between the two HF repos, and it is — on HF main. Our staged copy was the September 10 pin, and the base repo took seven more commits after it, the namespace fix landing one hour after our SHA. The drift is between vLLM's encoder and DeepSeek's current reference, not between the two checkpoints.

Same-window result

Three boots, identical launch, only the model mount differs: control on df42c109, candidate on 1017, control on df42c109 again. Each arm runs the standard window (knee r1/r2, knee6, agent fixture, replay ×2), the twelve-task tool gauntlet, and a greedy parity capture. The two controls are greedy-identical on all 18 parity prompts, which is what makes the middle bar meaningful.

Same-window aggregate decode: df42c109 control, 1017 candidate, df42c109 control Grouped bars at C1, C2, C4, C8, C12, C16. CTL1 df42c109: 182.7, 286.3, 433.1, 685.6, 841.2, 980.9. 1017: 192.6, 288.9, 423.9, 672.6, 850.9, 946.1. CTL2 df42c109: 182.6, 286.3, 434.2, 686.0, 852.9, 984.9 tokens per second. The two controls agree to within 1.4 percent at every concurrency; 1017 is 5.4 percent faster at C1 and 2 to 4 percent slower at C4 through C16. Aggregate decode, tok/s — CTL1 (df42c109) · 1017 · CTL2 (df42c109), identical v21 launch, one window 200 400 600 800 1000 183 193 183 C1+5.4% 286 289 286 C2+0.9% 433 424 434 C4-2.2% 686 673 686 C8-1.9% 841 851 853 C12+0.5% 981 946 985 C16-3.7% df42c109 controls (CTL1, CTL2)1017 knee r2, 30 s cells, T=0; Δ = 1017 vs mean of the two controls; controls differ ≤1.4% at every point

Figure 1. Same-window bracket. The 1017 bar is read against the mean of its two neighbours; controls differ by at most 1.4% at any concurrency.

armC1C2C4C8C12C16
CTL1 (df42c109)182.7286.3433.1685.6841.2980.9
1017192.6288.9423.9672.6850.9946.1
CTL2 (df42c109)182.6286.3434.2686.0852.9984.9
Δ vs bracket+5.4%+0.9%-2.2%-1.9%+0.5%-3.7%

C1 is a real gain, C2 and C12 are a wash, and C4 through C16 give back two to four percent. The C16 loss is the only number near the edge of the drift band, and it is consistent with what Figure 2 shows: at high concurrency the k=1 band is in effect and per-token acceptance is lower on the new head, so each step yields slightly fewer tokens.

The drafter moved, and prose got faster anyway

The agent fixture runs five prompt classes at C1 and reports the DSpark acceptance rate alongside decode speed. Same lane, same window, two checkpoints:

DSpark draft acceptance by prompt class, df42c109 versus 1017, same window prose: 34.9 to 25.2 percent, 203 to 174 tok/s; structured: 52.5 to 48.9 percent, 246 to 232 tok/s; code: 66.1 to 69.6 percent, 244 to 251 tok/s; shell_ops: 90.8 to 80.0 percent, 277 to 261 tok/s; tool_json: 87.8 to 77.3 percent, 220 to 200 tok/s. Shell and tool JSON accept about ten points less on the retrained drafter; code accepts more; prose drops ten points yet C1 prose throughput rose. Draft acceptance per class (agent fixture, k=5 band) — df42c109 → 1017 0% 25% 50% 75% 100% prose 34.9% · 203 tok/s 25.2% · 174 tok/s (-9.7 pts) structured 52.5% · 246 tok/s 48.9% · 232 tok/s (-3.6 pts) code 66.1% · 244 tok/s 69.6% · 251 tok/s (+3.5 pts) shell_ops 90.8% · 277 tok/s 80.0% · 261 tok/s (-10.8 pts) tool_json 87.8% · 220 tok/s 77.3% · 200 tok/s (-10.5 pts) grey = df42c109 (CTL2), colour = 1017; tok/s is the same fixture's C1 decode for that class; weighted accept 62.8% → 58.3%

Figure 2. Acceptance per class. Shell and tool JSON accept about ten points less on the retrained head; code accepts more; prose drops ten points.

classdf42c1091017Δ ptsC1 tok/s
prose34.9%25.2%-9.7203 → 174
structured52.5%48.9%-3.6246 → 232
code66.1%69.6%+3.5244 → 251
shell_ops90.8%80.0%-10.8277 → 261
tool_json87.8%77.3%-10.5220 → 200

The prose row is the interesting one. Acceptance fell from 34.9% to 25.2% (1.75 → 1.26 accepted tokens per step), and C1 prose throughput rose. On this lane that is not a paradox. A verify step touches the union of experts selected by every draft position, and a quarter of those experts live in Grace memory. Fewer accepted drafts per step means fewer unique experts per step, which means fewer bytes over C2C. The knee is prose-shaped; the fixture's prose row is 174 vs 203 because it measures a different prompt at a different length — both are real, they are different instruments.

Two behaviour changes show up in the replay harness (real Hermes transcripts, four workers): the new checkpoint emits 14–17 tool turns where df42c109 emitted 5–6 on the same transcripts, and completion lengths are shorter — −13.6% on GSM8K-200 (214 vs 248 tokens), −24% on the needle-checksum run (161 vs 211). Both match the model card's claims. Quality held: GSM8K-200 193/200 vs 195/200 (two items flipped, McNemar p=0.5), needle-checksum 500/500 on both.

Schedule re-check

A drafter that accepts ten points less on shell and tool JSON is exactly the input that picked the k-schedule in September, so the schedule got a bracketed re-check. Two candidates against the reference, same window, controls before and after:

scheduleC1C4C8replay n=4 r1, r2verdict
[[1,4,5],[5,24,1]] (reference)192.0 / 191.5431.6 / 419.8671.4 / 668.8326, 411 / 273, 397keep
[[1,2,5],[3,24,1]] (k=5 only at C1–2)192.2475.1 (+12%)697.1 (+4%)251, 364 — below both controls on both repsfail (agent lane)
[[1,4,3],[5,24,1]] (k=3 at C1–4)110.8 (−42%)396.6567.5 (−15%)284, 419fail

Same story as September 15. The v13-style schedule wins the prose knee at C4 by 12% and loses real agent traffic at four workers on both replay reps, because agent text accepts 3.5–4 drafts per step at k=5 and k=1 caps the step at about 1.86. And a k=3 band inside a num_speculative_tokens=5 schedule is simply a mistake: it pays the full five-token verify shape for three tokens, acceptance percent rises and throughput collapses. The reference schedule stays.

Promotion waiver, stated plainly. The lane's promotion rule says DSpark acceptance must stay within 2 points. It was written for engine changes on fixed weights, after a nightly image flipped 9.6% of argmaxes on tool-shaped documents. A retrained drafter moving acceptance is a property of the checkpoint, not a lane defect: same image, same kernels, controls bit-identical. James waived the rule for this promotion; the waiver is in the box ledger, and the rule stands for engine changes.

The hard no: MegaMoE on one Station

With the lane settled, the morning's one speculative boot went to the biggest lever on the board. The v18 step profile puts the MoE expert GEMM at 34–43% of each decode step, sitting on a tile-quantization floor: FlashInfer's MXFP4 kernel runs a t128x8x256 tile over roughly six rows, so each layer costs about 81 µs where its bytes would take 20 µs at HBM bandwidth. vLLM 0.31.0 ships a different kernel for Blackwell, DeepGEMM's deep_gemm_mega_moe with an MXFP4 format, and the recipe uses it on B200/B300. Two boots, four minutes, no autotune spent:

  1. Attempt 1: NotImplementedError: DeepSeek V4 MegaMoE currently requires expert parallel. On one GPU that is EP=1 — a flag gate, not a topology.
  2. Attempt 2, with --enable-expert-parallel: OOM at construction with 248.76 GiB allocated. The experts were not offloaded.
Why vLLM 0.31.0 MegaMoE cannot fit on one Station: expert bytes versus HBM HBM capacity 249.8 GiB. Routed experts total 268.8 GiB in MXFP4. The FlashInfer lane splits them 206.61 GiB in HBM and 62.33 GiB pinned in Grace memory via UVA. MegaMoE transforms every expert into a GPU-resident interleaved copy at first forward and drops the originals, so all 268.8 GiB must sit in HBM, 19 GiB more than exists, before KV cache, graphs or activations. Routed experts (268.8 GiB MXFP4) against 249.8 GiB of HBM — two kernels, one wall ◀ HBM 249.8 GiB 0909 lane — FlashInfer MXFP4 + pin-hot hook (serving today) 206.61 GiB hot rows in HBM 62.33 GiB in Grace (UVA) HBM-resident experts 206.6 GiB (fits with ~10 GiB non-expert + KV); the 62.3 GiB in Grace is read in place over NVLink-C2C, never copied to HBM vLLM 0.31.0 deep_gemm_mega_moe first 249.8 GiB of a 268.8 GiB interleaved GPU copy (all experts, HBM only) +19 GiB over HBM transform_weights_for_mega_moe() at first forward builds this copy and drops the UVA-offloaded originals. 19 GiB over HBM before KV, graphs or activations → OOM at construction, 248.76 GiB allocated. scale 2 px per GiB · expert bytes 40 layers × 6.72 GiB · no pruning (all 384 experts stay)

Figure 3. Expert bytes against HBM. The serving lane splits experts across HBM and pinned Grace memory; MegaMoE needs all of them in HBM.

The reason is structural. DeepseekV4MegaMoEExperts registers w13_weight/w2_weight the same way the FlashInfer path does, so the UVA offloader can pin them to Grace — but at first forward it calls deep_gemm.transform_weights_for_mega_moe(), which produces a full interleaved GPU copy of every expert and then drops the loader parameters. Offloading the originals is irrelevant; the transformed 268.8 GiB has to live in HBM, and there are 249.8. With no pruning (all 384 experts stay — a standing rule here), MegaMoE cannot serve this model on one Station in 0.31.0. The reopen trigger is upstream: transform into pinned host memory, and then find out whether DeepGEMM's TMA path reads Grace at all.

Who is on the lane

The lane is a named thing — :30006 serving dsv41-flash-uva — and nothing upstream knows which checkpoint is behind the name. Three consumers now ride 1017: Milo's custom[dsv41] provider on the M4, Alyx's custom[alyx-local], and Echo on Forge, whose Hermes default moved from GLM-5.3-Flash on :30001 to this lane today. The September container is kept stopped as dsv41-vllm-v21-fused-split-RETIRED-REF; rollback is one docker stop and one docker start, about eight minutes.

What's next

Reproduce

Same launch as v21 with the model mount changed; the complete runners (Card A bracket, Card C schedule check, Card F spike) and every receipt are in the recipe results bundle.

docker run -d --name dsv41-vllm-1017-v21cfg-BOUND-REF --gpus all --ipc host --network host \
  --ulimit memlock=-1 --ulimit stack=67108864 --cap-add IPC_LOCK \
  -v /models/DeepSeek-V4.1-Flash-1017-1bae0e1088993b9b82de08f59de5be5a34e42170:/model:ro \
  -v /home/milo/dsv41/vllm-cache:/root/.cache/vllm \
  -v $HOOK/sitecustomize.py:/usr/lib/python3.12/sitecustomize.py:ro -v $HOOK:/w:ro \
  -e PIN_MODE=split -e PIN_HOOK=/w/pin_hot_experts_hook.py -e PIN_ROWMAP=/w/rowmap-static-v1.json \
  vllm/vllm-openai:deepseekv41-flash-0909 \
  --model /model --served-model-name dsv41-flash-uva --trust-remote-code \
  --offload-backend uva --cpu-offload-gb 60 --cpu-offload-params routed_experts.w13_weight routed_experts.w2_weight \
  --engram-config '{"cpu_offload": true}' --max-model-len 1048576 --max-num-seqs 24 --max-num-batched-tokens 8192 \
  --gpu-memory-utilization 0.97 --long-prefill-token-threshold 6144 \
  --tool-call-parser deepseek_v41 --reasoning-parser deepseek_v41 --enable-auto-tool-choice \
  --speculative-config '{"method":"dspark","num_speculative_tokens":5,"num_speculative_tokens_per_batch_size":[[1,4,5],[5,24,1]]}' \
  --cudagraph-capture-sizes 1 2 4 6 8 12 16 18 24 32 40 48 64 96 128 --host 0.0.0.0 --port 30006

Recipe and receipts: J-M-Recipes PR #78 (results/2026-10-17-ds1017-cardA-cardC-cardF/, results/2026-10-17-ds1017-cardD/). The vLLM recipes DGX Station row that this launch matches is vllm-project/recipes#1015; #1136 made 1017 the default variant there and notes the weights were not re-run — this is, as far as we can find, the first single-Station measurement of them.

Credits