DeepSeek Flash 1017: TensorFold vs SGLang vs vLLM on 4 DGX Sparks

October 19, 2026 · Milo (benchmarks and draft: anthropic/claude-opus-5-5 workers; orchestration: anthropic/claude-fable-5; Round 2 section and corrections October 20: anthropic/claude-fable-5-1; reasoning settings not recorded) · eight DGX Sparks, three four-node lanes on two node sets · extends TensorFold vs SGLang on 4 DGX Sparks

Created · Updated

NOW 4-WAY October 21, 2026: NVIDIA's Flare joined as Lane D on the same Sparks and harness. See the 4-way post.
A · TENSORFOLD EXL3B · SGLANG NATIVEC · vLLM FORK NATIVE
Same model, same switch, same harness, a third engine. Tony Dimitriou's 89.8 tok/s on TensorFold against 48.5 on vLLM made the 1.85× headline that started this. Our A/B showed SGLang closing most of that gap. This post adds the lane the headline was really about: vLLM on the native checkpoint. It is not stock vLLM, because stock vLLM cannot serve V4.1 on the Spark's SM121 GPU today. It is Christopher Owen's spark-ds41f r6x build of a vLLM fork with TileLang kernels, built locally and run on Sparks 5–8. Results: Then we ran nine tuning arms. None of them beat Owen's shipped defaults.
C1 decode, agg100 · 85 · 92A · B · C — A leads, C +9% on B
C6 decode, agg223 · 263 · 287C leads, +9% on B, +29% on A
Prefill @93K2.6K · 6.2K · 6.0KB 6,219 and C 5,993 = Round-2 3-rep cold medians; B read 5.3K on Oct 18
300K needle— · 63–86 s · 55 sRound 2: B 86 / 74 / 63 s, C 55 / 55 / 55 s, all found
Hermes 20-hop30.4 · 22.4 · 19.5 s3/3 all; C on a fresh harness home
qeval 75 · KV pool71 · 71 · 71same 4 misses · C pool 6.54M vs B 4.85M

Verdict

Eight DGX Sparks on October 19, 2026: Lane A on Sparks 1 to 4, Lane C serving on Sparks 5 to 8, Lane B stopped and keptSparks 1 to 4 still run TensorFold with the EXL3 pack on port 8001. Sparks 5 to 8 now run the vLLM fork with TileLang kernels on port 8002. The SGLang lane B shares Sparks 5 to 8, so it is stopped with its containers kept and a restore bundle archived. The Forge gateway catalog entry for Lane C is pending.Sparks 1–4 keep Lane A; Sparks 5–8 now serve Lane CLane B shares Sparks 5–8 with Lane C, so it is stopped and kept — only one of the two can run at a timeMikroTik CRS804 · 200 GbE · RoCE v2, MTU 9000two rails per node · rail 1 10.0.0.0/24 · rail 2 10.0.1.0/24Lane A · TensorFoldLibertAI EXL3 3.5 bpw · unchanged since October 18spark1rank0 · headGB10 · 128 GBspark2rank1GB10 · 128 GBspark3rank2GB10 · 128 GBspark4rank3GB10 · 128 GBLane C · vLLM fork + TileLang · servingnative 1017 · spark-ds41f r6x · image c1c93733e852spark5rank0 · headGB10 · 128 GBspark6rank1GB10 · 128 GBspark7rank2GB10 · 128 GBspark8rank3GB10 · 128 GBspark1 :8001 · Lane A servingOpenAI-compatible · 1,048,576 ctx · 16 seqs · thinking offspark5 :8002 · deepseek-v4.1-flash-spark-vllm1,048,576 ctx · 16 seqs · KV pool 6.54M tokens · thinking offLane B · SGLang :8888 · stopped, keptcontainers intact · restore bundle archivedForge · lab-inference gateway :4000 (LiteLLM)catalog update for Lane C pending — not listed yetpending

Figure 1. State on October 19, 2026 (superseded on October 20: Lane A stopped and kept, Lane B now on Sparks 1–4; see Serving now). Sparks 1–4 still run Lane A (TensorFold, EXL3) on :8001, unchanged since the A/B post. Sparks 5–8 now run Lane C (vLLM fork, TileLang) on :8002. Lane B (SGLang, :8888) uses the same four nodes, so it is stopped with its containers intact and its restore bundle archived. Lane C is not on the Forge gateway yet; that catalog update is pending.

Round 2 (October 19–20): SGLang tuned too, same day, disjoint nodes

Update, October 20, 2026 at 5:40 AM CDT. Three of the caveats in What this is not are now closed: B has been tuned (six arms), B and C were benched the same day on disjoint node sets, and we tested whether two pods on one switch slow each other's prefill (they do not). The headline moves in one place: B's cold prefill at 93K is 6.2K tok/s, not 5.3K, which makes the two native engines level on prefill. Decode is unchanged: C still leads B by about 10% at C1 and C6. Nothing in either ladder beat its author's shipped config by more than 2%, so both lanes publish the authors' configs as recipes: SGLang TP4 (RoCEnante) and vLLM fork TP4 (sparknet).

What changed on the rack. Lane A (TensorFold) was stopped and kept on Sparks 1–4 with its restore script archived. Lane B moved from Sparks 5–8 to Sparks 1–4 (same checkpoint, byte-verified 48/48 shards; same image) and got the host hygiene Lane C had on October 19 and B did not on October 18: compact_memory, drop_caches, and a MemAvailable gate (≥100 GiB) before every boot. Lane C stayed on Sparks 5–8. Both lanes ran the same instrument (v41bench v1 at T=0 with thinking off, three cold prefill repeats per length, long_check needles with 300K ×3, the mixed-traffic probe, the 75-task qeval) one change per boot, with the same KEEP rule as the first ladder.

B, October 18 (Sparks 5–8, no hygiene)B, Round 2 (Sparks 1–4, six boots)C, Round 2 (Sparks 5–8, four boots)
C1 decode, aggregate (per stream)84.878.7–83.7 (B1a 80.0 / 82.4; 95–96 per stream)93.0 (104.3 per stream)
C6 decode, aggregate262.8250–262 (B1a 252.8)288.9
Cold prefill 93,335 tok, 3-rep median5,346 (single shot)6,080–6,187; quiet-fabric recheck 6,2195,998; recheck 5,993
300K needle, runs 1 / 2 / 31,791 / 2,648 / 3,0293,484 / 4,029 / 4,741 (B1a); run 1 is the slow one on every arm5,462 / 5,471 / 5,453
Short-chat TTFT behind a cold 93K prefillnot run1.7–1.9 s (B1a, B2, B3); 3.2–4.5 s (B0ctl)3.0 s
KV pool, tokens4.85M4.52M–5.93M, varies by boot6,537,516, fixed
qeval 757171 on every arm71 on every arm

B rows are ranges across the six Round-2 boots (B0, B0ctl, B1a, B1b, B2, B3); the B1a figure is the config now serving. C Round 2 is C0ctl2, the warm-reboot control of the unchanged FINAL. Prefill and needle numbers are tok/s. Receipts in the two recipes' results/ bundles.

Corrections to this post.

Cross-pod contention: tested, negative. One B boot (B0ctl, 20:04–20:21 CDT) read 3,304 tok/s at 93K while the vLLM ladder was prefilling on Sparks 5–8 through the same CRS804. The hypothesis that concurrent prefill on the sibling pod halves prefill was cheap to test, so at 05:00 on October 20, with both lanes idle, we ran three cold 93K prefills on each lane alone, then three on each lane while the other lane prefilled 93K prompts back to back. B: 6,219 alone, 6,242 contended. C: 5,993 alone, 6,009 contended. Every request landed within 1% of its quiet time. Two TP4 pods on one switch do not slow each other's single-stream long prefill, and same-day benching of two pods on one CRS804 is valid at that load. We did not test both pods under concurrent multi-stream needle traffic. The B0ctl window stays what it is: one unexplained slow boot, reported, not averaged in.

What the SGLang ladder found. No decode lever: C1 78.7–83.7 and C6 250–262 across six boots is the noise band, and nothing moved it. Two findings about memory, not speed: lowering the indexer-logits budget from 2 GiB to 1 GiB (DSV41_INDEXER_LOGITS_BUDGET_BYTES=1073741824, arm B1a) lifts the head node's MemAvailable floor during a 300K prefill from 11.8 to 14.9 GiB and gives the best short-chat TTFT behind a long prefill, at no measurable decode cost; --swa-prefix-tails 64 (B2) adds 9% to the KV pool and changes no speed. --prefill-decode-interval 4 (B3) cost 13% at 93K and 30% at 300K; a 4 GiB indexer budget (B1b) gained nothing. The NV_ERR_NO_MEMORY bursts we saw in B's first boot's needle window (271 kernel events on the head) did not recur in later arms. B1a is the config now serving on Sparks 1–4, published as the v2.4 block plus that one env override: a robustness default, not a speed win.

What the second vLLM round found. Owen's CED non-final-row-skip overlay (arm C7ced) is bit-identical in output and gives +0.9% at 93K, +1.6% at 300K and +4.1% at the 131K needle on TP4; Owen measured +10.6–11.1% on his TP3 ring, so the gain here is smaller than there and under our 2% bar. --max-parallel-prefills 2 cost 8–11% at C4/C6 and did not improve the short-chat TTFT it was meant to fix. The NVFP4 draft head and Markov table gave +1.7% at C1 and −2.8% at C6. All reverted; FINAL restored and re-verified identical (greedy 40/40, qeval 75/75 against the control).

Incident, for the record. The first B1a boot appended its env override onto a file whose last line had no trailing newline, so SKIP_PREPARE=1 stopped parsing as such and the lane's boot.py ran its default download of the old V4.1-Flash revision into spark1's 1017 checkpoint directory, overwriting shards 41–46 and removing 47–48. Nothing was served from it. The directory was restored from spark5 and all 48 shards sha256-verified before the next boot; the chain now asserts the rendered env and the shard count before any boot. If you run that stack: any env that un-sets SKIP_PREPARE destroys a 1017 checkpoint dir.

What Lane C actually is

Label: vLLM (fork: Local Inference Lab karmic-kraken-beta + TileLang kernels + sparknet, Christopher Owen's spark-ds41f r6x, local build). We pinned it to Owen's r6x promotion commit fc3ee36d659e and built image vllm-ds41f-kkref:19f2c20ed4d6-r6x. The image ID is c1c93733e852, the same on all four nodes. Because we rebuilt it locally, that ID will not match Owen's. The engine reports v0.29.1rc1.dev187+gaf1c01499. This is not a vLLM release, and you cannot pip install or docker pull it.

The build has six pinned trees. Each one matched the recipe's expected_labels on the built image:

The base image is a vllm/vllm-openai nightly at af1c01499b28. The serving path runs:

Why it has to be a fork. Stock upstream vLLM cannot serve V4.1 on SM121 (GB10) today:

Every working GB10 vLLM lane we found is a fork.

What we changed from Owen's recipe. Owen qualifies r6x on a switchless four-node ring with kernel 7.0, 64 KiB pages and memory-saver. Our fleet runs a switch with kernel 6.17 and 4 KiB pages, the fixed condition of the A/B post. We put the changes in a git-ignored local shape (tp4-switch-4k) and left the published config untouched:

Owen's topology checker refuses oneshot-direct on four nodes, so we launched the rendered docker run directly. That command is in Reproduce.

Why Tony's vLLM number was 48.5

Tony's 48.5 tok/s C1, 165.7 C6 and 2,201 tok/s prefill at 93K are often read as "vLLM untuned". That reading is wrong, and the A/B post leaned on it too. His lane, boot1017g, was a carefully laddered day-0 FlashInfer-path fork: image vllm-dsv41:exl3b-roce on dsv41-feat@e47aa780 with FlashInfer 0.7.0rc1, five boots of one-change-per-boot tuning, and CUDA graphs on. The gap is the kernel family, plus three cheap misses. Ranked by size, from our research pass:

#causeevidencewhat Lane C does
1Kernel family. FlashInfer SM120 sparse-MLA decode and FlashInfer/CUTLASS MXFP4 MoE on SM121Tony's own step budget: 63–71 ms/step. The 88 all-reduces cost about 5 ms and Engram staging 2–3 ms. In his words, "the rest is the MoE and attention kernels" [A]TileLang attention, linear and MoE kernels
2Probabilistic draft at T=0neko-legends measured greedy draft at +30% C1 on the same image lineage (54.5 → 70.8) [A]draft_sample_method: greedy
3Adaptive verification offOff because of a FlashInfer hang with padded FULL graphs (#5015), so prose pays the full k=5 verify shape [A]On, row_cost 0.035. Our sweep found 0.035 sits on a flat optimum
4One RoCE railNCCL_IB_HCA=rocep1s0f0. The vLLM lane was never re-run on two rails [A]Two rails. Our single-rail diagnostic arm says the second rail is worth about +16% cold prefill on this lane
5Engram rows on disk in the step pathSynchronous preadv per step. Threads 32→128 bought +18% prefill at 8K [A]Asynchronous rows, column-sharded per rank, on local NVMe on every rank
—Not causesNot eager mode (graphs were on), not the loader (it only affects boot time), not prefix caching (all cells are cold) [A]—

The table blames the kernel family, with greedy drafting, two rails and adaptive verification as the cheap misses on top. Flag tuning is not the story. The ladder below agrees: once the kernel family and those three are in place, every flag we tried moved the numbers by about 2% or less. On the same prompt set, Lane C runs 92.2 / 286.6 / 5,889 against Tony's 48.5 / 165.7 / 2,201. His lane and ours ran on different boxes, so the ratio (1.9× at C1) is directional. Tony's lane also ran at 300K context with 8 sequences, while all three of our lanes run 1M context with 16.

Setup

Lane A — TensorFoldLane B — SGLang (knapcio v2.4)Lane C — vLLM fork (spark-ds41f r6x)
Nodesspark1–4, head spark1, :8001spark5–8, head spark5, :8888spark5–8, head spark5, :8002 (shares nodes with B; one at a time)
WeightsLibertAIDAI/DeepSeek-V4.1-Flash-1017-EXL3-3.5bpw (240 GB, every matrix EXL3; Engram tables from the base shards 47/48)deepseek-ai/DeepSeek-V4.1-Flash-1017 rev 1bae0e1 as shipped (476 GB; MXFP4 experts, FP8/BF16 rest)Same native checkpoint as B, hash-verified on each node's local NVMe
Enginebertholomus/TensorFold deepseek-v41-zig-tp4 + Tony's patches 0001–0004 (RDMA fat-bin, Zig 0.17), kit v0.1.1, built in nvcr.io/nvidia/pytorch:26.07-py3SGLang dsv4.1 canary @ f80c91a4b + rhys101 RoCEnante overlay + knapcio adapters; image built from sha-verified sources, EP_SIZE=1LIL vLLM karmic-kraken-beta @ 19f2c20ed4d6 + TileLang + sparknet; image c1c93733e852, local build
Recipe sourcetonyd2wild runs/2026-10-17-1017-tensorfold, config.fleet-8000-rail2-p4.envknapcio .env.tp4.example v2.4 production block, MODEL_DIR pointed at the local 1017 checkoutOwen tp4-ring r6x at fc3ee36d659e, adapted as a local tp4-switch-4k shape (above)
FabricCRS804, MTU 9000, RoCE v2, GID index 3 on both HCAs; both rails in use on all three lanes (rocep1s0f0,roceP2p1s0f0). Kernel 6.17.0-1031-nvidia, driver 595.84, 4 KiB pages, GPU clock cap 2200 MHz (C measured ≤ 2190 MHz under load). No kernel or driver change for any lane.
Context / streams1,048,576 / 161,048,576 / 16 (KV pool 4.85M tokens)1,048,576 / 16 (KV pool 6,537,516 tokens at 8 GiB/rank)
Weights per rank71.87 GiB~85 GiB72.19 GiB (engine log, rank 0)
Boot to first token103.0 s with prebuilt rank caches (one-time ~6 min per rank)~8 min (fast-load path)351 s first boot with a cold JIT cache; 100 s on every warm reboot

The harness is the one the A/B post used:

For prefill, Lane C adds three cold repeats per length: exact-length token-id prompts with a unique leading nonce. A and B numbers on this page are the published October 18 values, quoted unchanged. All C numbers come from the final battery on a fresh boot of the shipped config, unless a row says otherwise. In the final battery, preemptions stayed at 0 and host MemAvailable never fell below 24.01 GiB on any node.

Decode

Aggregate decode throughput versus concurrency, three lanesA TensorFold: 100, 146, 175, 199, 210, 223 tok/s at C1 to C6. B SGLang: 85, 123, 170, 208, 237, 263. C vLLM fork: 92, 151, 177, 230, 258, 287. C leads B at every level; A leads only at C1; C3 is within about one percent between A and C.Aggregate decode, tok/s — C leads B at every level; A leads only at C1Tony prompt set v1 · T=0 · thinking off · A and B as published October 18 · C final battery October 19050100150200250300C1C2C3C4C5C6C 287B 263A 223A100146175199210223B85123170208237263C92151177230258287ringed =column bestA · TensorFold, EXL3 3.5 bpw (Sparks 1–4)B · SGLang, native (Sparks 5–8)C · vLLM fork + TileLang, native

Figure 2. Aggregate decode throughput over all eight categories. C leads B at every level. A leads only at C1 (100 vs 92). At C3, A and C are within about 1%. At C6, C delivers 287 tok/s to B's 263 and A's 223. TTFT at C1: A 0.17 s, B 0.22 s, C 0.16 s.

streamsA aggB aggC aggA / B / C per-streamA / B / C TTFTC / AC / B
C1100.284.892.2*113.2 / 98.6 / 103.80.17 / 0.22 / 0.16 s0.92×1.09×
C2146.2122.6150.685.5 / 74.5 / 85.60.25 / 0.31 / 0.19 s1.03×1.23×
C3175.2170.3177.270.3 / 67.9 / 70.30.34 / 0.29 / 0.22 s1.01×1.04×
C4199.1207.7229.560.9 / 61.8 / 67.40.43 / 0.33 / 0.25 s1.15×1.11×
C5210.3236.7257.651.9 / 57.4 / 60.10.52 / 0.34 / 0.26 s1.22×1.09×
C6223.0262.8286.646.1 / 53.5 / 56.60.61 / 0.33 / 0.30 s1.29×1.09×

* C1 for C is the mean of two separate invocations, 92.06 and 92.40. The per-stream and TTFT values in that row come from the second. All other C rows are single runs from the same battery.

Single stream by prompt category

categoryA C1B C1C C1C / AC / BC draft acceptanceC tokens/step
coding148.3134.5132.90.90×0.99×0.8335.13
format151.9130.2148.40.98×1.14×0.9095.53
math134.1127.5126.90.95×1.00×0.6954.30
json119.7105.3103.90.87×0.99×0.5593.57
reasoning108.689.6100.50.93×1.12×0.5393.31
prose88.174.377.40.88×1.04×0.4132.22
summary78.865.678.61.00×1.20×0.3902.27
narrative76.561.661.50.80×1.00×0.3141.76

Why the curves look like this. At one stream, A still wins on bytes per token. C's single-stream speed comes almost entirely from speculative decoding, and the acceptance column shows it: the drafter is right on 83–91% of code and format tokens (5.1–5.5 tokens per step) but only 31–41% on narrative and prose (1.8–2.2 per step). Across the whole C1–C6 battery, C accepted 0.715 of drafted tokens, or 3.34 tokens per step. Where acceptance is low, C runs level with B (narrative 61.5 vs 61.6). Where it is high, C pulls ahead. As streams stack, C keeps its per-stream rate better than either other lane: 56.6 per stream at C6, against B's 53.5 and A's 46.1. We read that as greedy drafting plus adaptive verification plus sparknet's one-shot collectives. We did not isolate the three: the ladder only perturbed them around the shipped point, and nothing moved more than about 2%.

Prefill

Cold prefill throughput by prompt length, three lanesPrefill tok/s for A, B, C: 11.6K 2696, 4289, 5697; 46.8K 2743, 3624, 6048; 93.3K 2642, 5346, 5889; 20K needle 2404, 5883, 5575; 131K needle 2557, 4565, 5842; 300K needle 2368, 2648, 5411. C is fastest at five of six lengths; B is fastest at the 20K needle. 300K values are medians of three runs.Cold prefill, tok/s — C fastest at five of six lengths, 2.2× A at 93Kunique prompts, 1-token reply · v41bench ladder single shots and long_check needles · 300K = median of three runs01,0002,0003,0004,0005,0006,0007,0002.7K4.3K5.7K11.6KC/B 1.33×C/A 2.1×2.7K3.6K6.0K46.8KC/B 1.67×C/A 2.2×2.6K5.3K5.9K93.3KC/B 1.10×C/A 2.2×2.4K5.9K5.6K20K needleC/B 0.95×C/A 2.3×2.6K4.6K5.8K131K needleC/B 1.28×C/A 2.3×2.4K2.6K5.4K300K needleC/B 2.04×C/A 2.3×A · TensorFold, EXL3 3.5 bpwB · SGLang, native (Oct 18)C · vLLM fork + TileLang (Oct 19)

Figure 3. Cold prefill on unique prompts with a 1-token reply. Each bar uses the same instrument as the A/B post: v41bench ladder single shots and long_check needles. C is fastest at five of six lengths and roughly flat at 5.4–6.0K tok/s from 11.6K to 300K tokens. B wins the 20K needle (5,883 vs 5,575). The 300K bars are medians of three: B 1,791 / 2,648 / 3,029 and C 5,413 / 5,411 / 5,339.

prompt tokensA tok/sB tok/sC tok/sC / BC / A
2,950 †2,2713,654 (cold first run)3,9781.09×1.75×
11,5922,6964,2895,6971.33×2.11×
46,8102,7433,6246,0481.67×2.20×
93,3352,6425,346 (Oct 18) · 6,219 (Round 2)5,889 · 5,993 (Round 2)1.10× → 0.96×2.23×
20,000 (needle)2,4045,8835,5750.95×2.32×
131,073 (needle)2,5574,5655,8421.28×2.28×
300,000 (needle, ×3 on B and C)2,3681,791, 2,648, 3,029 (median 2,648)5,413, 5,411, 5,339 (median 5,411)2.04×2.29×

† Do not compare the 2,950-token row carelessly. In B's full ladder that prompt had already been seen, and the prefix cache returned 12,542 tok/s. That is a cache hit, not prefill, which is why the A/B post (and this table) uses B's cold first run, 3,654. C's 3,978 is cold. A has no prefix cache.

Cold repeats. v41bench takes one shot per length, so for Lane C we also ran three cold repeats at exact token lengths. Medians: 2,000 tokens 4,598 tok/s, 8,000 tokens 6,018, 32,000 tokens 6,140, 64,000 tokens 6,096, 93,335 tokens 5,977. At 93,335 tokens the three repeats spread 5,940–6,015, about 1.3%. The headline 6.0K at 93K is that 5,977 median; the v41bench single shot at the same length read 5,889.

In seconds:

All needles were found on all three lanes in every run.

Round 2 note. B's rows in this table are the October 18 lane without host hygiene. Re-measured on October 19–20 with hygiene, B's 93K prefill is 6.1–6.2K (three-rep medians across four boots) and its 300K needle is 4.0–4.9K on warm runs of the shipped config; the cross-pod contention test was negative. Details in Round 2.

Quality

On knapcio's qeval.py (75 auto-scored tasks, T=0), all three lanes score 71/75 with the same four misses: code_interval_intersect, json_escape, json_count and math_m9. Two paired comparisons back this up:

The KL comparison between lanes is still open, as in the A/B post. We did not run a logprob or greedy-agreement pass on C, so this is a correctness gate, not a divergence measurement.

What an agent sees

A wall / agentB wall / agentC wall / agent
rep 130.4 s / 28.3 s22.9 s / 20.5 s19.7 s / 17.9 s
rep 232.0 s / 29.9 s22.4 s / 20.8 s19.5 s / 17.6 s
rep 329.9 s / 27.9 s21.2 s / 19.1 s18.5 s / 17.1 s
median wall30.4 s22.4 s19.5 s
Instrument caveat: treat these deltas as directional. All three lanes ran the same gauntlet_reps.py, unchanged, and all three were correct 3/3 with exactly 20 tool calls. The harness setup did differ: That makes "C 13% sooner than B, 36% sooner than A" a direction, not an instrument-identical delta.

The direction matches the rest of the data and the GLM-5.3 lesson: each Hermes turn pays a several-thousand-token system prompt plus a growing transcript. Prefill speed and short-turn TTFT decide agent latency, and decode at C1 does not.

Tuning ladder: nine arms, nothing kept

The method was one change per boot on top of Owen's r6x defaults. Each boot started from drop_caches and the MemAvailable gate. The KEEP rule: keep an arm if C1 (mean of two runs), C6 or prefill at 93K improves by more than 2%, none of the three regresses by more than 2%, and all gates hold. After the first two arms, every arm was judged against C0ctl, a warm reboot of the unchanged config in the same window. Prefill at 93K was judged on the three-rep cold median.

armchangeC1 (mean)C4C6prefill 93K repsKV tokensdecision
C0 (window)Owen r6x on our shape, first boot, cold JIT92.97 (−0.2%)230.97282.68 (−1.4%)5,483 single shot (−7.7%)6,537,516baseline; read low, see below
C1railsingle rail (rocep1s0f0 only)92.38 (−0.8%)227.31281.38 (−1.9%)5,101 single shot (−14.1%)6,537,516diagnostic, never kept
C2mergeNCCL_IB_MERGE_NICS=1, NCCL_CROSS_NIC=193.18 (+0.0%)231.26286.42 (−0.1%)5,913 single shot (−0.5%)6,537,516provisional KEEP vs C0, reverted vs C0ctl
C0ctlC0 config, warm reboot (control)93.15230.63286.696,0436,537,516reference
C3ch8NCCL channels 4 → 893.09 (−0.1%)230.87288.54 (+0.6%)5,983 (−1.0%)6,537,516revert
C3buf16NCCL_BUFFSIZE 4 → 16 MiB93.01 (−0.2%)230.44285.60 (−0.4%)5,991 (−0.9%)6,537,516revert
C4kv86KV 8 → 8.6 GiB/rank92.82 (−0.4%)229.80284.21 (−0.9%)5,987 (−0.9%)7,027,904revert (capacity only)
C4kv92KV 8 → 9.2 GiB/rank92.70 (−0.5%)230.52288.29 (+0.6%)6,032 (−0.2%)7,518,118revert; offered as an option
C5mnbt16k--max-num-batched-tokens 8192 → 1638492.93 (−0.2%)229.27287.04 (+0.1%)5,997 (−0.8%)4,962,013 (−24%)revert (cost KV, no gain)
C6rc050adaptive-verify row cost 0.035 → 0.0592.65 (−0.5%)231.30280.77 (−2.1%)6,027 (−0.3%)6,537,516revert (C6 regressed)
C6rc020adaptive-verify row cost 0.035 → 0.0293.19 (+0.0%)230.16283.20 (−1.2%)6,007 (−0.6%)6,537,516revert
FINALC0 config, fresh boot, full battery92.23 (−1.0%)229.54286.61 (−0.0%)5,977 (−1.1%)6,537,516what this post reports

Percentages are against C0ctl. The C0, C1rail and C2merge boots ran before the cold-repeat tool existed, so their 93K cells are v41bench single shots; C0ctl's own single shot was 5,941. Each arm is one boot with two C1 runs and one run of every other cell.

KV capacity: a free option we did not adopt

At 8 GiB per rank the pool holds 6,537,516 tokens, about 1,314 bytes per token per rank on TileLang's FP4 main-KV. That is 6.23 full 1M windows. Raising the reservation was speed-neutral:

Every speed cell stayed within ±1% of the control, and the 300K needle was found at both sizes. Host MemAvailable never came close to the guard. At 9.2 GiB the worst node, spark5, bottomed out at 22.91 GiB through a 300K prefill and the mixed probe, against 24.29 GiB at 8 GiB. The ~23 GiB host floor held. The arm did not clear the KEEP rule, which only counts speed, so adopted: no. If you want the bigger pool, pass --kv-cache-memory-bytes 9878424781 (9.2 GiB) for 7.52M tokens. The ladder did not probe beyond 9.2 GiB.

Mixed traffic: a long prefill still stalls short chats

The probe sends a short chat request 1 s after a cold 93K-token prefill starts:

We tried --max-num-batched-tokens 16384 (arm C5mnbt16k) to see whether a bigger step budget helps. It did not: TTFT was 3.07 / 2.97 s against 2.94 / 2.95 s at 8192. The reason is that --long-prefill-token-threshold 8096 still caps each step's chunk of a single long prefill, and --max-parallel-prefills is 1. The bigger budget cost 24% of the KV pool and about 2.4 GiB of host memory per node, for nothing. If you serve interactive users next to long-document jobs on this lane, the threshold is the knob to study next. Batch size is not. A and B were not run through this probe, so there is no cross-lane comparison here.

Against the published numbers

sourcewhatC1C6prefillnote
Tony (vLLM 1017g, native)his day-0 FlashInfer-path fork48.5165.72,201 @93Kour Lane C on the same prompt set: 92.2 / 286.6 / 5,889. The gap is kernel family plus greedy draft, second rail and adaptive verify (see above)
Tony (TensorFold, dual rail)same pack, same engine as A93.6213.82,215 @93Kour Lane A: 100.2 / 223.0 / 2,642 (A/B post)
Christopher Owen (spark-ds41f r6x)same engine as C, his harness, switchless ring, kernel 7.0 64K74.1 greedy [A]—5,982 @32K (r6v) [A]Different prompt set, so not comparable cell by cell. Our 32K cold-rep median is 6,140, on 6.17, 4 KiB pages and a switch
vedcsolution (Owen r6n, switched)independent reproduction, 4 KiB pages74.8 tg [R]—4,738 pp [R]the only other switched, 4 KiB TP4 run we found; it told us the fabric change was viable

What this comparison is not

Serving now

As of October 20, 2026 at 5:40 AM CDT:

Neither lane is on the Forge gateway yet; that catalog update is still pending. The A/B post's gateway alias (deepseek-v4.1-flash-spark-native) has no backend until it is repointed at Sparks 1–4.

History: on October 19 at 3:08 AM CDT this section read Lane A on 1–4, Lane C on 5–8, Lane B stopped.

Reproduce

Lanes A and B: see the A/B post's Reproduce block.

Added October 19, 2026 (recipe request from Ash): the exact TensorFold version and command, read from the live lane on spark1.

# Lane A engine: bertholomus/TensorFold fork, dsv41 four-node branch, checkout 225f7005 (2026-10-18).
# Binary reports: tensorfold-native 1.0.2. Built with Zig 0.17.0 inside nvcr.io/nvidia/pytorch:26.07-py3
# (CUDA 13.3 toolkit, kernel driver 595.84). rdma_gather.fatbin ships beside the binary in engine-bin-p4/.
# Kernel kit v0.1.1: MANIFEST pins 356 files, 708,157,701 bytes, per-file sha256 (verify_kit.sh checks all).
# Site config /home/milo/tf1017/config.rail2.env: both rails NCCL_IB_HCA=TF_RDMA_DEVICES=rocep1s0f0,roceP2p1s0f0,
#   NCCL_SOCKET_IFNAME=enp1s0f0np0, GID 3 (TF_RDMA_GID=3, NCCL_IB_GID_INDEX=3), context 1,048,576,
#   KV pool/window 2,097,152, 16 streams. Engine env: TF_SEED_RANDOM=1 TF_TOP_K_MAX=56 TF_DS_2D_GU=parity
#   TF_DS_ROUND_ROWS=48 TF_DS_STREAMS=16 TF_DS_SERVED_K=solo TF_DS_WINDOW=2097152
# serve.sh start launches ranks 3,2,1 (tf-dsv41-lanes) then rank 0, each in container tp4-lane on its node.
# Rank 0 runs exactly (from docker inspect of the live container):
/engine/tensorfold-native serve /model --host 0.0.0.0 --port 8001 --name deepseek-v4.1-flash \
  --context 1048576 --parallel 16 --no-thinking --max-tokens 32768 \
  --alias glm-5.3-flash --temperature 1.0 --top-p 1.0 --top-k 20 --backend cuda
# Note for recipe checks: the lane's serving default is T=1.0 / top-p 1.0 / top-k 20;
# every benchmark request in these posts set temperature=0 explicitly per request.

Lane C, rank 0, exactly as launched:

# Lane C — vLLM fork (spark5 head, :8002). Ranks 1–3 (spark6–8) run the same command with
# --node-rank r --headless, their own VLLM_HOST_IP and SPARKNET_ROCE_PEER_HCAS; launch workers first, head last.
git clone https://github.com/christopherowen/spark-ds41f && cd spark-ds41f && git checkout fc3ee36d659e
bin/spark build prepare && bin/spark build check
bin/spark --cluster-config config/cluster.json build image      # plan only: prints the buildx command (see pitfalls)
docker image inspect vllm-ds41f-kkref:19f2c20ed4d6-r6x --format '{{.Id}}'   # ours: sha256:c1c93733e852...
sync; echo 3 | sudo tee /proc/sys/vm/drop_caches                 # every node; gate MemAvailable >= 100 GiB

docker run -d --name dsv41-karmic-kraken --restart=no --network=host --ipc=host --gpus=all \
    --device=/dev/infiniband:/dev/infiniband:rwm --cap-add=IPC_LOCK --ulimit=memlock=-1:-1 \
    --ulimit=stack=67108864:67108864 --ulimit=core=0 --security-opt=seccomp=unconfined --security-opt=label=disable \
    --shm-size=16g --memory=112g --memory-swap=112g \
    --mount=type=bind,source=/dev/dri/by-path/pci-000f:01:00.0-card,target=/dev/dri/card0 \
    '--device-cgroup-rule=c 226:* rw' --cpuset-cpus=5-9,15-19 --env B12X_AUTOTUNE=0 \
    --env B12X_BIND_COMPILE_WORKERS=2 --env B12X_COMPILE_CACHE_DIR=/cache/kkref/jit/b12x/compile \
    --env B12X_CUTE_COMPILE_CACHE_DIR=/cache/kkref/jit/b12x/cute --env B12X_DENSE_SPLITK_TURBO=1 \
    --env B12X_STATE_COMPILE_WORKERS=2 --env B12X_W4A8_TINY_DECODE=0 --env B12X_WEIGHTS_COMPILE_WORKERS=4 \
    --env CUDA_CACHE_MAXSIZE=4294967296 --env CUDA_CACHE_PATH=/cache/kkref/jit/nv-compute \
    --env CUDA_DEVICE_MAX_CONNECTIONS=32 --env CUTE_DSL_ARCH=sm_121a \
    --env CUTE_DSL_CACHE_DIR=/cache/kkref/jit/cute-dsl --env FLASHINFER_WORKSPACE_BASE=/cache/kkref/flashinfer \
    --env GLOO_SOCKET_IFNAME=enp1s0f0np0 --env HF_HOME=/cache/huggingface --env HF_HUB_OFFLINE=1 --env MAX_JOBS=2 \
    --env NCCL_ALGO=Ring --env NCCL_BUFFSIZE=4194304 --env NCCL_COLLNET_ENABLE=0 --env NCCL_CROSS_NIC=0 \
    --env NCCL_CUMEM_ENABLE=1 --env NCCL_CUMEM_HOST_ENABLE=0 --env NCCL_DEBUG=WARN --env NCCL_DEBUG_SUBSYS=INIT \
    --env NCCL_GIN_ENABLE=0 --env NCCL_IB_ADDR_FAMILY=AF_INET --env NCCL_IB_DISABLE=0 --env NCCL_IB_GID_INDEX=3 \
    --env NCCL_IB_HCA=rocep1s0f0,roceP2p1s0f0 --env NCCL_IB_MERGE_NICS=0 --env NCCL_IB_ROCE_VERSION_NUM=2 \
    --env NCCL_IB_SUBNET_AWARE_ROUTING=1 --env NCCL_IB_SUBNET_PREFIX_LEN=24 --env NCCL_IGNORE_CPU_AFFINITY=1 \
    --env NCCL_LL128_BUFFSIZE=262144 --env NCCL_MAX_NCHANNELS=4 --env NCCL_MIN_NCHANNELS=4 \
    --env NCCL_MIN_TRAFFIC_PER_CHANNEL=512 --env NCCL_MNNVL_ENABLE=0 --env NCCL_NET=IB --env NCCL_NET_PLUGIN=none \
    --env NCCL_NVLS_ENABLE=0 --env NCCL_P2P_DISABLE=1 --env NCCL_PAT_ENABLE=0 --env 'NCCL_PROTO=^LL128' \
    --env NCCL_RMA_DISABLE=1 --env NCCL_RUNTIME_CONNECT=1 --env NCCL_SHM_DISABLE=1 \
    --env NCCL_SOCKET_IFNAME=enp1s0f0np0 --env 'NCCL_THREAD_THRESHOLDS=-2 -2 -2 1 1 1' --env OMP_NUM_THREADS=8 \
    --env PYTHONPATH=/opt/spark3/candidate/vllm:/opt/spark3/candidate/b12x \
    --env PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True,garbage_collection_threshold:0.6 \
    --env SAFETENSORS_FAST_GPU=1 --env SPARK3_DISPLAY_CARVEOUT_WEIGHTS=0 \
    --env SPARK3_DSPARK_COST_DIR=/cache/kkref/dspark-costs/build-c6c07d0b62663ebf \
    --env SPARK3_DSPARK_DEAD_ROWS_TAU=0.2 --env SPARK3_DSPARK_MAIN_PROJ_TP=1 \
    --env SPARK3_DSPARK_PROFILE_TOKENS=random --env SPARK3_DSPARK_VOCAB_PARALLEL=1 \
    --env SPARKNET_ROCE_ALLGATHER_MAX_BYTES=2097152 --env SPARKNET_ROCE_ALLREDUCE_CAPACITY_BYTES=2097152 \
    --env SPARKNET_ROCE_ALLREDUCE_DISPATCH_MAX_BYTES=2097152 --env SPARKNET_ROCE_GID_INDEX=3 \
    --env 'SPARKNET_ROCE_PEER_HCAS={"1":["rocep1s0f0","roceP2p1s0f0"],"2":["rocep1s0f0","roceP2p1s0f0"],"3":["rocep1s0f0","roceP2p1s0f0"]}' \
    --env SPARKNET_ROCE_PROXY_CPU=big --env SPARKNET_ROCE_TIMEOUT_MS=10000 --env SPARKNET_ROCE_TOPOLOGY=direct \
    --env TILELANG_CACHE_DIR=/cache/kkref/jit/tilelang --env TORCHINDUCTOR_CACHE_DIR=/cache/kkref/jit/torchinductor \
    --env TORCH_NCCL_ASYNC_ERROR_HANDLING=1 --env TP_SOCKET_IFNAME=enp1s0f0np0 --env TRANSFORMERS_OFFLINE=1 \
    --env TRITON_CACHE_DIR=/cache/kkref/jit/triton --env VLLM_ALLOW_LONG_MAX_MODEL_LEN=1 \
    --env VLLM_CACHE_DIR=/cache/kkref/jit/vllm-r5o --env VLLM_CACHE_ROOT=/cache/kkref/jit/vllm-r5o \
    --env VLLM_DISABLE_PYNCCL=0 --env VLLM_DS41_ATTENTION_COMPUTE=auto --env VLLM_DS41_DRAFT_NVFP4_HEAD=0 \
    --env VLLM_DS41_HEAD_DTYPE=float32 --env VLLM_DS41_KERNEL_BACKEND=tilelang --env VLLM_DS41_MARKOV_NVFP4=0 \
    --env VLLM_DS41_PACKED_BF16_LM_HEAD=1 --env VLLM_ENABLE_PCIE_ALLREDUCE=0 --env VLLM_ENGINE_READY_TIMEOUT_S=3600 \
    --env VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS=900 --env VLLM_HOST_IP=192.168.1.55 --env VLLM_L2_PREFETCH_GRID=2 \
    --env VLLM_L2_PREFETCH_KERNELS=tilelang --env VLLM_MEMORY_PROFILER_ESTIMATE_CUDAGRAPHS=1 \
    --env VLLM_MULTI_STREAM_GEMM_TOKEN_THRESHOLD=1024 --env VLLM_PLUGINS=b12x_loader --env VLLM_USE_AOT_COMPILE=1 \
    --env VLLM_USE_BREAKABLE_CUDAGRAPH=0 --env VLLM_USE_FLASHINFER_SAMPLER=1 --env VLLM_USE_MEGA_AOT_ARTIFACT=1 \
    --env VLLM_USE_STANDALONE_COMPILE=1 --env VLLM_USE_V2_MODEL_RUNNER=1 --env XDG_CACHE_HOME=/cache/kkref/jit \
    --volume /home/milo/models/DeepSeek-V4.1-Flash-1017:/models:ro \
    --volume /home/milo/models/DeepSeek-V4.1-Flash-1017:/blobs:ro \
    --volume /home/milo/spark-ds41f/cache:/cache:rw vllm-ds41f-kkref:19f2c20ed4d6-r6x /models \
    --served-model-name deepseek-v4.1-flash-spark-vllm --host 0.0.0.0 --port 8002 --tokenizer-mode deepseek_v41 \
    --dtype bfloat16 --tensor-parallel-size 4 --load-format b12x --block-size 256 --enable-prefix-caching \
    --enable-chunked-prefill --max-model-len 1048576 --max-num-seqs 16 --max-parallel-prefills 1 \
    --gpu-memory-utilization 0.82 --max-num-batched-tokens 8192 --kv-cache-memory-bytes 8589934592 \
    --max-cudagraph-capture-size 96 --generation-config vllm \
    --override-generation-config '{"temperature":1.0,"top_p":1.0}' \
    --engram-config '{"table_memory":"disk","disk_resident_scales":false,"projection_tp":true}' \
    --attention-backend TILELANG --linear-backend tilelang --moe-backend tilelang \
    --speculative-config '{"method":"dspark","num_speculative_tokens":5,"draft_tensor_parallel_size":4,"attention_backend":"TILELANG","draft_sample_method":"greedy","rejection_sample_method":"block","enable_adaptive_verification":true,"adaptive_verification_row_cost":0.035}' \
    --jit-monitor-mode warn \
    --compilation-config '{"cudagraph_mode":"FULL_AND_PIECEWISE","custom_ops":["all"],"cudagraph_capture_sizes":[1,2,3,4,6,8,12,16,20,24,28,32,40,48,56,64,72,80,88,96]}' \
    --prefix-cache-retention-interval 8192 --long-prefill-token-threshold 8096 --async-scheduling \
    --reasoning-parser deepseek_v41 --tool-call-parser deepseek_v41 --enable-auto-tool-choice \
    --distributed-executor-backend mp --nnodes 4 --no-scheduler-reserve-full-isl --no-enable-flashinfer-autotune \
    --limit-mm-per-prompt '{"image":4}' --mm-processor-cache-gb 0 \
    --default-chat-template-kwargs '{"thinking":false}' --node-rank 0 --master-addr 10.0.0.5 --master-port 9999

# Bench — same instrument as lanes A/B
python3 v41bench_tf.py --base http://127.0.0.1:8002/v1 --model deepseek-v4.1-flash-spark-vllm \
    --levels 1,2,3,4,5,6 --prefill 2000,8000,32000,64000          # 64000 target = the 93,335-token prompt
python3 prefill_reps.py http://127.0.0.1:8002 deepseek-v4.1-flash-spark-vllm 3 2000 8000 32000 64000 93335
python3 long_check.py http://127.0.0.1:8002 20000 131073 300000 300000 300000
python3 qeval.py run C --url http://127.0.0.1:8002/v1/chat/completions

Pitfalls that cost us time: