J&M Labs Blog by Milo

Building the future, locally

← Back to Home

Kimi-K3 on the same pod, two engines: why TensorFold decodes 2.4–3.8× faster

In plain English

What is compared. One machine, one set of weights, two programs. The machine is our Kimi-K3 pod: a DGX Station GB300 with an RTX PRO 6000 inside it, plus eight DGX Sparks on a RoCE fabric. The weights are NVIDIA's NVFP4 Kimi-K3, 1,499 GiB, unmodified. Program one is our build: stock vLLM 0.31.0 with an external hook, a real OpenAI-style server. Program two is TensorFold, Ash Hart's Zig + CUDA engine, which runs Kimi-K3 on the same pod as a test runner. Ash handed it over on November 1; we reran it ourselves the same morning.

Who is faster at what. At writing text, TensorFold. On the same four prompts it decodes one stream at 44.4–69.7 tokens per second to our 18.4, which is 2.4–3.8×. With eight streams at once it does 74.4 tokens per second in total to our 42.2, which is 1.76×. Those are matched numbers: same prompts, same token ids, measured today on both engines.

Why, in four reasons. One: each Spark reads one-eighth of every Spark-tier expert instead of whole experts, so all eight Sparks finish a layer together instead of waiting for the one with the most work; a slice is 2.32 MB and takes about 9.4 µs to read, a whole expert about 77 µs. Two: the 104 most-used experts of each layer sit in the GPU's own HBM, where reading one costs about 3 µs, instead of Grace memory at about 48 µs; our placement is uniform and random. Three: it is one native program with no Python anywhere on the per-token path. Reasons one to three together take plain greedy decoding from our 54.0 ms per token to about 40.6, a 1.33× engine advantage. Four, the big multiplier: a small 4.5 GB drafter guesses up to 7 tokens ahead and Kimi-K3 checks them all in a single pass, and a pass of r rows costs only about 38 + 4.4·r ms, so checking several guesses costs little more than producing one token. That alone is 1.79–2.89× on top, and the output is exactly what plain greedy decoding would have written.

Where ours still wins. Reading the prompt: 562 tokens per second against TensorFold's 103, about 5.5× faster, because our prompt path batches 1,024 tokens through a proper matrix kernel. And ours is a server: streaming, sampling, eight users at once, an OpenAI-compatible endpoint. TensorFold's Kimi-K3 path is a test runner that takes files of token ids and prints greedy tokens.

Bottom line. On identical hardware and identical weights, Ash's engine decodes 2.4–3.8× faster than ours, for reasons we can measure and explain, and two of them transfer to our build.

69.7 tok/s
best drafted single stream (Python prompt), TensorFold, reproduced today
24.1–25.2 vs 18.4
plain greedy, one stream, same prompts: TensorFold vs our vLLM pod, 1.31–1.37×
74.4 vs 42.2
eight streams at once, same prompts, 1.76× (our own-prompt figure is 46.5)
103 vs 562
prompt reading, tok/s: TensorFold vs ours. Ours ~5.5× faster; 570.6 remeasured today
1 pod, 1 checkpoint
same Station, RTX PRO 6000 and eight Sparks; nvidia/Kimi-K3-NVFP4 b2428a0b, unmodified
PASS, every run
TensorFold's drafted output = plain greedy, each of 8 lanes = its solo decode, on all runs we saw

Scoreboard

TensorFold (Ash Hart)Our vLLM podSource
One stream, plain greedy24.1–25.2 tok/s on the four prompts; 1000 / 24.6 ≈ 40.6 ms a token18.4 tok/s; 54.4–54.6 ms a token today, 54.0–54.3 published[R] / [M], [O]
One stream, drafted44.4–69.7 tok/s (2.44–5.56 tokens a round)no drafting[R] / —
Eight streams74.4 tok/s together, 9.30 a lane, 107.6 ms a pass42.2 tok/s on Ash's eight prompts; 46.5 on our own benchmark prompts[R] / [M], [O]
Prompt reading103 tok/s (6,144 tokens in 59.9 s)562 tok/s (3,364 tokens, first token in 5.99 s); 570.6 today[A] / [O], [M]
Cost of one more stream(107.6 − 40.6) / 7 ≈ 9.6 ms a pass; a draft row of the same stream ~4.4 ms (Ash's model)(172.1 − 54.3) / 7 = 16.8 ms on our prompts; (189.3 − 54.4) / 7 ≈ 19.3 ms on Ash's[D], [A] / [D]
Exactnessdrafted = plain greedy and each lane = its solo decode, asserted every run; token ids bit-identical across three separate bootsper-layer parity with stock vLLM; worker kernel is not deterministic, so two greedy runs on one boot diverge after 3–29 tokens[R], [A] / [O], [M]
What it istest runner: files of token ids in, greedy tokens out; no HTTP, no streaming, no samplingOpenAI-compatible server: streaming, sampling, 8 concurrent sequences[A] / [O]
Context limitprompts up to 8,192 tokens (6,144 tested)4,096[A] / [O]

Source labels. [R] reproduced by us today, running Ash's own runner on the pod. [A] Ash's handover or logs, not reproduced. [O] our pod's published receipts from October 30–31. [M] matched run today: our vLLM pod booted fresh and fed Ash's exact prompt token ids. [D] arithmetic on the numbers above, shown in the text. Single-stream figures on both engines time decode only; prompt reading is excluded on both sides.

Same hardware, same weights

Everything in this post ran on one pod. The Station is a DGX Station GB300: a Blackwell Ultra GPU with HBM, a Grace CPU with about 494 GiB of LPDDR joined to the GPU by NVLink-C2C, and an RTX PRO 6000 Blackwell (Max-Q) in the same chassis. Eight DGX Sparks (GB10, about 117 GiB usable each) hang off two MikroTik CRS804 switches over RoCE; the Station's mlx5_0 port serves Sparks 1–4 and mlx5_1 serves Sparks 5–8. The checkpoint is nvidia/Kimi-K3-NVFP4 at revision b2428a0b, read-only and unmodified: 93 layers, 896 routed experts per MoE layer, 16 of them picked per token, 82,432 routed experts of 18,579,480 bytes each. Both engines run attention, routing and the final add on the Station GPU, and both send expert work to the Sparks and the RTX. The difference is entirely in how the experts are placed and computed, and in what the program is.

Same hardware for both engines: one DGX Station GB300 with an RTX PRO 6000, eight DGX Sparks, two switches, one checkpoint A block diagram. On the left a DGX Station GB300 holding a Blackwell Ultra GPU with HBM, a Grace CPU with about 494 GiB of LPDDR joined by NVLink-C2C, and an RTX PRO 6000 Blackwell card. Two fabric ports, mlx5_0 serving Sparks 1 to 4 and mlx5_1 serving Sparks 5 to 8, lead to two MikroTik CRS804 switches joined by an inter-switch link. Eight DGX Sparks with GB10 chips and about 117 GiB usable each hang off the switches. The checkpoint is nvidia Kimi-K3-NVFP4 revision b2428a0b, 1,499 GiB, unmodified, 93 layers, 896 routed experts per MoE layer, 16 picked per token. Same hardware for both engines: one Station, one RTX PRO 6000, eight Sparks nvidia/Kimi-K3-NVFP4 b2428a0b, 1,499 GiB, unmodified · 93 layers · 896 routed experts per MoE layer, 16 per token DGX Station GB300 Blackwell Ultra GPU HBM · fastest tier · ~3 µs to read one expert both engines: attention, router, join run here NVLink-C2C (~345 GB/s) Grace CPU · ~494 GiB LPDDR ~48 µs to read one expert over C2C expert memory for both engines, read in place RTX PRO 6000 Blackwell (Max-Q) a ninth expert worker, inside the Station ours: 56 experts a layer · TensorFold: 58 mlx5_0 mlx5_1 RoCE v2 fabric, two ports CRS804 · switch B Sparks 1–4 · Station CRS804 · switch A Sparks 5–8 inter-switch link Sparks 5–8: +1 hop to Sparks 1–4 to Sparks 5–8 DGX Spark 1 · GB10 · ~117 GiB usable DGX Spark 2 · GB10 · ~117 GiB usable DGX Spark 3 · GB10 · ~117 GiB usable DGX Spark 4 · GB10 · ~117 GiB usable DGX Spark 5 · GB10 · ~117 GiB usable DGX Spark 6 · GB10 · ~117 GiB usable DGX Spark 7 · GB10 · ~117 GiB usable DGX Spark 8 · GB10 · ~117 GiB usable ours 62 whole each · TensorFold 1/8 of 511 each Same pod, same checkpoint, same RoCE fabric over two MikroTik CRS804 switches. Only the program running on it changes.
Diagram 1. The pod, same for both engines: one Station (GB300 HBM, Grace, RTX PRO 6000) and eight Sparks over two switches. Only the program changes.

Reason 1 — slice every expert across all eight Sparks

This is the biggest structural idea, and it is simple once you see it. In our pod each Spark owns whole experts: 62 of each layer's 896, so 496 per layer across the eight. When a token's router picks its 16 experts, those picks land on the Sparks unevenly. One Spark might hold three of them, another none. A Spark computing k whole experts takes 73 + 77·k µs, measured on the real bank with a graphed worker, and the 77 µs is just reading one 18.6 MB expert at the GB10's roughly 248 GB/s. The layer cannot finish until the busiest Spark replies. So every layer, 92 times a token, our pod waits for whichever Spark drew the short straw.

TensorFold does not give any Spark a whole expert. Each Spark holds one-eighth of the intermediate columns of all 511 Spark-tier experts. Whatever the router picks, every Spark has exactly one-eighth of the work and all eight finish together; each returns a partial sum and the Station adds the eight partial sums in a fixed order. The arithmetic: 18,579,480 B / 8 = 2.32 MB per expert per Spark, and 2.32 MB at 246 GB/s (the rate Ash reports the Sparks sustaining at five rows) is about 9.4 µs. Nine Spark-tier experts in a layer come to 9 × 2.32 MB ≈ 21 MB, about 85 µs of reading, on every Spark alike. The whole-expert way, the same nine experts could put 3 × 77 µs plus the fixed cost on one unlucky Spark while two others sit idle. The slices live only in GPU memory; the Station sends them at load, about 109 GB per Spark, and the workers keep no weights on disk.

Whole experts: a layer waits for the unluckiest Spark. Slices: all eight Sparks finish together Two panels on the same microsecond scale. Left, our pod: eight Spark columns for an illustrative layer whose nine Spark-tier experts land 3, 2, 1, 1, 1, 1, 0, 0. Each Spark's time is 73 plus 77 times k microseconds from the measured model, so the busiest Spark takes 304 microseconds and a dashed line marks that the layer waits for it. Right, TensorFold: eight equal columns, each reading one eighth of all nine experts, 9 times 2.32 megabytes, about 21 megabytes, about 85 microseconds at 246 gigabytes per second, labelled as arithmetic from measured bandwidth. Whole experts: wait for the unluckiest Spark. Slices: all eight finish together. One MoE layer's Spark-tier work, 9 experts (illustration). Left: our pod. Right: TensorFold. Same µs scale on both panels. Our vLLM pod — whole experts illustration: 9 experts land as 3, 2, 1, 1, 1, 1, 0, 0 0 100 200 300 µs 304 µs S1 k=3 227 µs S2 k=2 150 µs S3 k=1 150 µs S4 k=1 150 µs S5 k=1 150 µs S6 k=1 no call S7 k=0 no call S8 k=0 the layer waits for the slowest: 73 + 77·3 = 304 µs Spark time = 73 + 77·k µs for k whole experts (measured, graphed worker) ~77 µs per extra expert, measured (the 18.6 MB read alone: ~75 µs at ~248 GB/s) TensorFold — slices every Spark holds 1/8 of each Spark-tier expert: equal work each layer 0 100 200 300 µs ~85 S1 1/8 of 9 ~85 S2 1/8 of 9 ~85 S3 1/8 of 9 ~85 S4 1/8 of 9 ~85 S5 1/8 of 9 ~85 S6 1/8 of 9 ~85 S7 1/8 of 9 ~85 S8 1/8 of 9 all eight finish together: ~85 µs (read time only) arithmetic from measured bandwidth 18,579,480 B per expert ÷ 8 Sparks = 2.32 MB per expert per Spark 2.32 MB ÷ 246 GB/s ≈ 9.4 µs per expert slice 9 experts × 2.32 MB ≈ 21 MB → 9 × 9.4 ≈ 85 µs, on every Spark alike versus ~77 µs per whole expert, 3 of them on one Spark in the left panel fixed hop cost not shown here Each Spark returns a partial sum; the Station adds the eight in a fixed order. Slices are sent at load (~109 GB per Spark); workers keep no weights on disk. Which experts a token picks is luck from a Spark's point of view, so the left-hand spread changes every layer; the right-hand columns never do.
Diagram 2. Whole experts versus slices on the Spark tier, same µs scale. The left-hand spread (3, 2, 1, 1, 1, 1, 0, 0) is an illustration of one layer; the 73 + 77·k µs per Spark is measured. The right-hand ~85 µs is arithmetic from measured bandwidth and leaves out the fixed per-hop cost.

Reason 2 — put the hottest experts in the fastest memory

Not every expert is used equally. Our placement, plan-4tier-v1, ignores that: it is uniform and random, so a heavily used expert is as likely to sit in Grace memory as in HBM. On the Station, reading one expert costs about 3 µs from HBM and about 48 µs from Grace over NVLink-C2C at roughly 345 GB/s, a 48 / 3 = 16-fold difference the uniform plan never exploits.

TensorFold's placement file, place_cal2.bin, ranks each layer's 896 experts by how often drafted decoding of eight calibration prompts picked them. The hottest 104 go to GB300 HBM, the next 58 to the RTX PRO 6000, the next 511 to the Sparks (sliced, as above), and the coldest 223 to Grace. 104 + 58 + 511 + 223 = 896. Grace still holds a quarter of the experts, but they are the ones a token rarely asks for, and the ranking comes from calibration text, so it is only as good as the match between that text and yours.

Our pod spreads one layer's 896 experts uniformly over the tiers; TensorFold ranks them hottest to coldest, HBM first and Grace last Two horizontal stacked bars of 896 units each. Ours: Station 344 experts in HBM and Grace mixed, RTX 56, Sparks 496 as 8 times 62 whole experts; annotated uniform, not frequency ranked. TensorFold: HBM 104 hottest, RTX 58, Sparks 511 sliced one eighth per Spark, Grace 223 coldest; annotated ranked hot to cold by how often decode picked each expert. A legend gives read costs: HBM about 3 microseconds per expert, Grace about 48, Sparks about 77 per whole expert or about 9.4 per one-eighth slice. Ours spreads experts uniformly; TensorFold ranks them hot → cold The 896 routed experts of one MoE layer, by memory tier · Spark tier: whole experts (ours) vs 1/8 slices on every Spark (TensorFold) Our vLLM pod · plan-4tier-v1 · uniform / random placement Station 344 HBM + Grace, mixed; 402 GiB of them sit in Grace RTX 56 Sparks 8 × 62 = 496 whole experts: a Spark computes every expert it holds, alone uniform, not frequency ranked: a hot expert is as likely to sit in Grace or on a Spark as in HBM TensorFold · place_cal2.bin · ranked by how often drafted decode of 8 calibration prompts picked each expert HBM 104 hottest RTX 58 Sparks 511, sliced every Spark holds 1/8 of each of these; equal work every layer Grace 223 coldest hottest coldest ranked hot → cold: the most-picked experts sit in the fastest memory, the least-picked in the slowest HBM: ~3 µs to read one expert Grace: ~48 µs to read one expert over NVLink-C2C (~345 GB/s) Sparks: ~77 µs per whole expert (ours) · ~9.4 µs per 1/8 slice (TensorFold, arithmetic) RTX PRO 6000: 56 (ours) / 58 (TensorFold) experts a layer Counts per layer from each engine's placement: 344 + 56 + 496 = 896 (ours); 104 + 58 + 511 + 223 = 896 (TensorFold). Read costs are per-expert measurements from our pod (HBM, Grace, whole expert on a Spark); the 1/8-slice cost is arithmetic.
Diagram 3. Where the 896 experts of one layer live. Ours: Station 344 (HBM and Grace mixed), RTX 56, Sparks 8 × 62 = 496, uniform. TensorFold: HBM 104, RTX 58, Sparks 511 sliced, Grace 223, ranked hot to cold.

Reason 3 — one native program

TensorFold is Zig and CUDA. One 308 MB runner binary, built for sm_103a (the GB300), sm_120 (the RTX PRO 6000) and sm_121 (the GB10), runs on the Station and on all nine workers: the same file everywhere. Its transport is Ash's MCDMA fabric library: a UDP rendezvous on the fabric interfaces, then RDMA. There is no Python anywhere on the per-token path.

Our pod is stock vLLM 0.31.0 plus an external Python hook (k3pod.py, no fork) plus a C proxy thread that does the network send and collect. We spent the night of October 30 taking Python out of that path piece by piece: the graph-resident expert step that Javier suggested took a token from 79.9 ms to 63.7, and CUDA-graphing every worker's expert call took it to 54.0. Per layer at batch 1 we are at 0.59 ms, of which 0.305 ms is proxy send to last worker reply and 0.154–0.171 ms is the Station's own attention, router and shared experts. That is a respectable place for a hook on a general-purpose server to be. It is still a hook on a general-purpose server.

The combined effect of reasons 1–3, with no drafting on either side, is plain greedy decoding at about 40.6 ms per token on TensorFold (1000 / 24.6) against 54.0 ms on ours: 1.33×. On the matched prompts the range is 24.1–25.2 tok/s against 18.4, or 1.31–1.37×. We did not measure how the 13.4 ms (54.0 − 40.6) splits among slicing, placement and the native path, so we will not pretend to.

Reason 4 — draft, then check several tokens in one pass (DSpark)

This is the biggest multiplier, and it is the part worth understanding even if you never touch a Spark. A big model writes one token per pass, and a pass is mostly spent reading weights, not doing arithmetic. So checking a second token in the same pass is nearly free if you already know what to check. Speculative decoding supplies the guesses. A small drafter, here RadixArk's Kimi-K3-DSpark at 4.5 GB, proposes up to seven tokens ahead. A confidence head decides how many of them, r, are worth checking. Kimi-K3 then runs one pass over all r rows at once and compares its own choice at each position with the guess. It keeps the guesses up to the first disagreement, adds the token it would have written there itself, and the next round starts from the result. Because the check is greedy, the output is token for token what plain greedy decoding would have produced: faster, not different. TensorFold asserts this on every run (K3_CHECK=1 reruns plain greedy and compares), and every run we saw printed PASS.

Why it pays so well here: Ash's cost model, measured on this pod, is that a pass of r rows costs about 38 + 4.4·r ms. The 38 is the pass; each extra row adds only 4.4 ms, because draft rows of one stream mostly touch experts the pass is reading anyway. So checking three to six guesses costs little more than checking one, and every guess that lands is a token for nearly nothing. Measured today on the Python prompt the drafter was right often: 5.86 drafts checked a round, 5.56 tokens kept a round, 69.7 tok/s. On the chat prompt it was harder to predict: 2.95 checked, 2.44 kept, 44.4 tok/s. Plain greedy on the same engine runs 24.1–25.2 tok/s on these prompts, so drafting alone is 1.79–2.89× depending on how predictable the text is.

A drafting round on TensorFold: the drafter guesses up to 7 tokens, Kimi-K3 checks them all in one pass, and the agreed prefix plus one token of its own is kept A left-to-right flow of four boxes joined by arrows, with a return arrow for the next round. One: the DSpark drafter, 4.5 gigabytes, proposes up to 7 next tokens. Two: a confidence head picks r, how many to check. Three: Kimi-K3 checks the r rows in one pass costing about 38 plus 4.4 times r milliseconds in Ash's model. Four: the tokens up to the first disagreement are kept plus one token of Kimi-K3's own. Below, a schematic strip of seven guess slots: three agree, the fourth differs, the fifth was checked and dropped, the sixth and seventh were not checked, then one own token, four tokens kept. A measured panel: Python with tests, 5.86 drafts checked a round, 5.56 tokens kept a round, 69.7 tokens per second; chat answer continued, 2.95 checked, 2.44 kept, 44.4 tokens per second; plain greedy on the same engine 24.1 to 25.2. A drafting round: guess up to 7 tokens, check them in one pass, keep the agreed prefix RadixArk's Kimi-K3-DSpark drafter (4.5 GB) in front of Kimi-K3 (1,499 GiB) · the output is exactly plain greedy 1 · Drafter guesses DSpark proposes up to 7 next tokens; 4.5 GB, fast 2 · Confidence head picks r, how many of the guesses are worth checking 3 · Kimi-K3 checks r rows ONE pass over all r guesses ≈ 38 + 4.4·r ms (Ash's model) 4 · Keep the agreed prefix tokens up to the first disagreement, +1 of its own next round: the drafter starts again from the kept tokens Schematic round: r = 5 checked, 3 agree, so 3 + 1 = 4 tokens come out of one pass guess 1 agrees guess 2 agrees guess 3 agrees guess 4 differs guess 5 dropped guess 6 not checked guess 7 not checked Kimi-K3's own +1 token kept this round: guesses 1–3 and the token Kimi-K3 wrote where guess 4 was wrong. Greedy check: the result equals plain greedy, token for token. Measured today on the pod (reproduced run; Ash's own 1 November runs matched) Python with tests: 5.86 drafts checked a round, 5.56 tokens kept a round → 69.7 tok/s chat answer continued: 2.95 drafts checked a round, 2.44 tokens kept a round → 44.4 tok/s plain greedy on the same engine, one token a pass: 24.1–25.2 tok/s on the four prompts Greedy verification: every drafted run's tokens equal plain greedy's, asserted on each run (K3_CHECK=1) — PASS on all four prompts today.
Diagram 4. One drafting round, with a schematic example and the two measured extremes: 5.56 tokens a round on the Python prompt, 2.44 on the chat prompt.
Same engine, same four prompts: drafting lifts TensorFold from 24.1 to 25.2 tokens per second plain greedy to 44.4 to 69.7 drafted; our vLLM pod decodes the same prompts at 18.4 Grouped bars for four prompts. Plain greedy versus drafted tokens per second: photosynthesis continuation 25.20 versus 50.99 at 2.79 tokens a round; chat answer continued 24.81 versus 44.40 at 2.44; word problem in thinking mode 24.26 versus 46.41 at 2.96; Python with tests 24.09 versus 69.70 at 5.56. A dashed reference line at 18.4 tokens per second marks our vLLM pod decoding the same prompts one stream at a time. All TensorFold bars reproduced today. Same engine, same prompts: drafting lifts 24.1–25.2 tok/s to 44.4–69.7 TensorFold, reproduced today on the pod (200–204 tokens a prompt, greedy) · dashed line: our vLLM pod decoding the same prompts 0 20 40 60 80 tok/s 25.20 50.99 photosynthesis continuation (31 tok) 2.79 tok/round · ×2.02 24.81 44.40 chat answer continued (46 tok) 2.44 tok/round · ×1.79 24.26 46.41 word problem thinking (88 tok) 2.96 tok/round · ×1.91 24.09 69.70 Python with tests (52 tok) 5.56 tok/round · ×2.89 our vLLM pod one stream, same prompts: 18.4 (matched) plain greedy, one row a pass drafted (DSpark drafter) × = drafted ÷ plain from the bars shown (arithmetic); across the four prompts 1.79–2.89×. Tokens a round = tokens kept per drafting round. Ash's own 1 November drafted runs on the same prompts: 52.8 / 52.5 / 52.6, 44.6 / 44.3 / 44.0, 46.5 / 45.9 / 46.5 and 69.8 / 69.6 / 68.8 tok/s.
Diagram 5. Plain greedy against drafted on the four prompts, TensorFold, reproduced today, with our vLLM pod's matched single-stream rate (18.4 tok/s) as the reference line.

Why more rows are cheap on TensorFold

The same chart explains both drafting and concurrency. On our pod, adding a concurrent stream costs (172.1 − 54.3) / 7 = 16.8 ms per pass on our own benchmark prompts, and (189.3 − 54.4) / 7 ≈ 19.3 ms on Ash's eight prompts today. On TensorFold, adding a lane costs (107.6 − 40.6) / 7 ≈ 9.6 ms, about half, and a draft row of the same stream costs about 4.4 ms in Ash's model, because its experts overlap with the rows already in the pass. A new stream brings its own 16 experts a layer; a draft row mostly reuses them. On our pod every row's experts also have to clear the busiest Spark, which is where the whole-expert layout bites again.

One more concurrent stream costs about 17 to 19 milliseconds a pass on our pod, about 10 on TensorFold, and a draft row of the same stream about 4 A line chart of milliseconds per decode pass against rows in the pass, 1 to 8. Our vLLM pod, measured on our own prompts: 54.3 at 1, 74.3 at 2, 113.1 at 4, 172.1 at 8; a hollow marker at 8 rows shows 189.3 on Ash's prompts today. TensorFold, measured: 40.6 at 1 row and 107.6 at 8 lanes. A dashed line shows Ash's cost model for draft rows, 38 plus 4.4 times r milliseconds. A side panel gives the slopes: ours 16.8 and about 19.3 milliseconds per added stream, TensorFold about 9.6 per lane, and 4.4 per draft row. One more stream costs ~17–19 ms on ours, ~10 ms on TensorFold, and a draft row ~4 ms Milliseconds per decode pass against rows in the pass · points are measured · the dashed line is Ash's cost model 0 50 100 150 200 ms per pass 1 2 3 4 5 6 7 8 rows in one pass: concurrent streams, or draft rows of one stream 54.3 74.3 113.1 172.1 (our prompts) [O] 189.3 (Ash's prompts, today) [M] 40.6 = 1000 / 24.6, plain greedy, one row 107.6 (8 lanes) [R] dashed: 38 + 4.4·r ms, Ash's cost model for draft rows of one stream cost of one more row ● ours, own prompts [O] (172.1 − 54.3) / 7 = 16.8 ms ◇ ours, Ash's prompts [M] (189.3 − 54.4 today) / 7 ≈ 19.3 ms ● TensorFold lanes [R] (107.6 − 40.6) / 7 ≈ 9.6 ms - - draft rows, Ash's model [A] 4.4 ms per row (38 + 4.4·r) Why the gap: draft rows of one stream reuse experts the pass is reading anyway; a new stream brings its own 16 experts a layer, and on our pod a layer waits for the busiest Spark. Ours: published 1 / 2 / 4 / 8-stream steps on our own prompts, plus today's 8-stream pass on Ash's prompts (8 / 42.26 tok/s ≈ 189.3 ms a pass). TensorFold: plain greedy one row a pass (1000 / 24.6 ≈ 40.6 ms) and 8 lanes (107.6 ms a pass), both reproduced today. Dashed: Ash's default K3_COST.
Diagram 6. Milliseconds per pass against rows in the pass. Our 1 / 2 / 4 / 8-stream points are published measurements on our own prompts; the hollow marker is today's 8-stream pass on Ash's prompts, drawn just left of 8 so it does not hide the 172.1 point; its 19.3 ms arithmetic starts from today's 54.4 ms single stream. TensorFold's 1-row and 8-lane points are reproduced measurements; the dashed line is Ash's cost model, not a measurement.

Where our build is still ahead

Prompt reading. Ours reads a 3,364-token prompt at 562 tok/s, first token in 5.99 s (remeasured today: 570.6 tok/s, 5.90 s). TensorFold reads prompts at 103 tok/s: 6,144 tokens took 59.9 s. The reason is the mirror image of reason 4. Our prompt path sends each worker 1,024-token chunks and runs them as one batched GEMM, so each expert is read from memory once per chunk and amortised over every prompt token that needs it. TensorFold's Kimi-K3 path has no prompt kernel yet; prompts go through the decode kernels 16 rows a pass, so experts are re-read every 16 tokens. That makes ours about 5.5× faster at the prompt (562 / 103). Ash's own next step is a real prompt path, which he estimates at about 1,000 tok/s; that is his estimate, not a measurement.

It is a server. Our pod is an OpenAI-compatible endpoint with streaming, sampling and eight concurrent sequences at up to 4,096 tokens of context. TensorFold's Kimi-K3 path is a test runner: prompt files of token ids in, greedy token ids out, no HTTP, no streaming, no sampling. It handles prompts up to 8,192 tokens (6,144 tested), and a run takes the whole pod for 10–15 minutes including a 6.5-minute warm load (about 25 minutes cold). Neither of those is a criticism; it is what a research runner is for. But if you want to talk to Kimi-K3 on this pod today, ours is the one that answers.

Speed without correctness is a random number generator, so here is what each engine can show, side by side, with the limits stated.

EvidenceTensorFoldOur vLLM pod
Self-consistencyDrafted output equals plain greedy and each of eight lanes equals its solo decode, asserted on every run; PASS on every run we saw today. Token ids were bit-identical across three separate runs on separate boots (Ash's two November 1 logs and our reproduction) on all four prompts.The Spark and RTX worker expert kernel is not deterministic (0.8–2% run to run on a Spark, a heavier tail on the RTX). Two greedy runs of the same prompt on the same boot diverged after 19, 12, 29 and 3 tokens today (photosynthesis, chat, math, code). Two boots give logprobs a median 0.12–0.14 apart.
Against a referenceFour layers' logits match a float64 reference at cosine 0.999964. A row's bits depend only on its own inputs (batch-invariant). Moving experts between tiers changes the order partial sums add, so plain greedy can differ between placements.Per-layer parity with stock vLLM on the same inputs, layers 1–11 at cosine ≥ 0.9998 mean. All 92 MoE layers against an fp32 reference built from the checkpoint: 87 of 92 at cosine ≥ 0.99.
Whole-model quality gateNone.None.

The two engines against each other. On the same prompts, ours and TensorFold produce identical tokens for the first 12–23 tokens on three prompts and then diverge; on the code prompt the very first token differs (a # heading versus "Here's"), a near-tie. Both outputs are coherent and on topic: both engines open the chat answer with "# Photosynthesis: How Plants Turn Sunlight into Food". Divergence between the engines is not evidence that either is wrong. Ours is not even consistent with itself from run to run, so it cannot be the yardstick; TensorFold, on the evidence above, is reproducible. What neither has is a teacher-forced comparison against a stock copy of the full model, because no single machine here can run one. That gate is still open for both.

What we checked ourselves

We did not take the handover's numbers on trust. Between 09:46 and 10:03 CST today we ran Ash's own k3_run.sh drafted and then lanes on the pod, as his account, with his placement and prompt files. Model load took 368.1 s. The runs produced the same rounds and tokens as Ash's logs and PASS on all four prompts.

Prompt (tokens)Drafted tok/s, today [R]Ash's three November 1 runs [A]Tokens a roundDrafts checked a roundPlain greedy tok/s, today [R]
photosynthesis continuation (31)50.9952.8 / 52.5 / 52.62.793.3625.20
chat answer continued (46)44.4044.6 / 44.3 / 44.02.442.9524.81
word problem, thinking (88)46.4146.5 / 45.9 / 46.52.963.4824.26
Python with tests (52)69.7069.8 / 69.6 / 68.85.565.8624.09
8 lanes, plain greedy, 128 tokens each74.36 together (9.30 a lane, 107.6 ms a pass)74.79——PASS: each lane = its solo decode

Then the other direction. From 10:06 to 10:34 CST we booted our vLLM pod fresh in its exact parked configuration (vLLM 0.31.0 with the k3pod hook, --max-num-seqs 8, all nine workers graphed), warmed it, and fed Ash's exact prompt token-id files through /v1/completions: greedy, streamed, 200 tokens, two repetitions each, decode rate taken from first token to last so prompt reading is excluded on both sides. Steady state was 18.33–18.38 tok/s (54.4–54.6 ms a token; the published 18.4–18.5 stands). The first request after warmup ran slower, 15.41 tok/s, and is reported but not used. Eight of Ash's lane prompts at once, 128 tokens each, two repetitions: 42.31 and 42.21 tok/s over the decode window, so 42.2 against TensorFold's 74.36, or 1.76×. Prompt reading remeasured at 32 / 284 / 1,124 / 3,364 tokens: 45.8 / 260.8 / 418.2 / 570.6 tok/s, consistent with the published 562.

What this means for our build

Two of Ash's ideas transfer to the vLLM pod without changing what it is. Slicing experts across the Sparks replaces "wait for the busiest Spark" with "all eight finish together", and the Spark tier is where our per-token time goes. Frequency-ranked placement puts the experts a token actually asks for in HBM instead of Grace, at a 3 µs versus 48 µs difference per read. Both are placement and worker changes, not engine changes. Drafting is the third thing, and the largest, but it is a different kind of work: a drafter, a verification step and a server that still streams and samples correctly around it. We are not going to promise numbers for any of these before we have measured them. We will say that today's comparison turned "our Sparks set the floor" from a suspicion into a mechanism with a fix attached, and that is worth more than the 13.4 ms.

Credits

Ash Hart built TensorFold and did every TensorFold measurement in this post: github.com/ashhart/TensorFold (main is public, Apache-2.0) and x.com/ashxhart; the transport is his MCDMA fabric library, github.com/ashhart/MCDMA. The Kimi-K3 work is on his lead/k3-cuda branch, which is not public yet; the source and the runner are on the pod under his account. The handover, the placement, the cost model and the drafting results are his; our part was to rerun them and to run our own pod on his prompts.

RadixArk for the Kimi-K3-DSpark drafter. NVIDIA for the NVFP4 Kimi-K3 checkpoint. Moonshot AI for Kimi-K3. Javier (@javierbits) for the graph-resident expert step in our build. The vLLM project for the engine our pod is built on. Our side of the story is in Kimi K3 NVFP4 on Station, and 8 Sparks, the four-tier page and the Station + Sparks fabric how-to.

Provenance

All numbers come from one fact sheet assembled on November 1, 2026 from commands run that day and named receipt files. [R] TensorFold reproduced by Milo, 09:46–10:03 CST, running Ash's k3_run.sh drafted and lanes under his account on the pod: four drafted prompts (200–204 tokens each), their plain-greedy checks, and the 8-lane run. [A] Ash's handover (HANDOVER-kimi-k3-pod.md, November 1) and his logs: the placement counts, the cost model 38 + 4.4·r ms, the 103 tok/s prompt figure, the float64 parity figure and the ~1,000 tok/s prompt-path estimate; not reproduced. [O] our pod's published receipts of October 30–31 (the earlier post, STATUS.md, PARKED-2026-10-31.md). [M] our vLLM pod rebooted fresh 10:06–10:34 CST in the parked configuration and fed Ash's exact prompt token ids, two repetitions each; the 8-stream and prefill remeasurements the same way. [D] arithmetic shown in the text. Modelled numbers (Ash's 38 + 4.4·r line, the ~85 µs slice read, the ~1,000 tok/s estimate) are labelled as such wherever they appear and are never called measurements. The hardware is described in the earlier posts; nothing about it changed between the two engines' runs.