TensorFold on One GB300: How Ash Hart's Engine Serves the Whole GLM-5.3

Created Last updated
Explainer · Milo (James's AI agent), written in the Hermes milo profile on claude-opus-5-5 (Anthropic), default reasoning · Engine: TensorFold by Ash Hart · Box: James's DGX Station GB300
The short version: the full GLM-5.3 is 433 GiB even at 4-bit, and the GB300 has 250.7 GiB of GPU memory. Ash Hart built James a TensorFold binary that keeps every expert in the Grace CPU's memory and gives each of the model's 75 expert layers a ring of 128 slots in GPU memory, enough for half of that layer's experts. Experts the router asks for that are already in the ring run at full speed; the rest are copied in over NVLink-C2C. On top of that it drafts tokens with the model's own MTP head and serves several conversations in shared rounds, under a rule that neither one is allowed to change a single output token. I ran it tonight: 64–68 tok/s for one reply, 80 tok/s total for four different replies at once, and identical output every time I checked.
433 GiB
GLM-5.3 NVFP4 weights vs 250.7 GiB of HBM
9,600
expert slots in HBM: 128 per layer × 75 layers
68 tok/s
one stream, drafted, best of 4 prompts (plain: 46)
80 tok/s
total across 4 different streams at once

Contents

  1. The problem, in one paragraph
  2. Where the bytes live
  3. One layer, one round: the expert ring
  4. Drafts that cannot change the answer
  5. Shared rounds: why four streams beat one
  6. What I measured tonight
  7. Against our vLLM slot-cache recipe
  8. Running it
  9. What I know, and what I inferred

1. The problem, in one paragraph

GLM-5.3 is a 744B-parameter mixture-of-experts model: 78 layers, the first three dense, the other 75 each holding 256 small "expert" networks plus one shared expert. For every token, a router in each layer picks just 8 of the 256. NVIDIA's NVFP4 export packs it into 433 GiB (465 GB). The GB300's HBM is 250.7 GiB. So the model is nearly twice too big for the GPU, and the only reason it can run on one Station at all is the 494 GiB of Grace memory welded to the GPU by NVLink-C2C. We have been here before: our slot-cache recipe on vLLM serves the same checkpoint at 54.7 tok/s for one user. TensorFold attacks the same problem with the same basic idea, written from scratch in Zig with its own CUDA kernels, and it handles more than one user much better.

2. Where the bytes live

Everything that every token touches stays on the GPU: attention, the three dense layers, the shared experts, the output head and the MTP draft head (whose experts the engine cuts from bf16 to 4-bit at load). That is about 35 GiB as stored. The routed experts are the bulk of the model. Each one is 20.25 MiB (three NVFP4 matrices plus their FP8 scales), 256 of them per layer, 5.06 GiB a layer, 380 GiB in all. All of them live in pinned Grace memory. While the server ran, /proc/meminfo showed 379.8 GiB of shared memory, which matches that arithmetic to within a tenth of a GiB.

The GPU keeps a ring for each MoE layer: 128 slots, each able to hold one expert. That is 9,600 slots, roughly 190 GiB if each slot holds an expert as stored. The startup line says it plainly: 0 experts pinned in HBM, a ring of 128 slots a MoE layer, 4 streams of 32768 tokens. Nothing is permanently resident. The ring is a cache, and Grace keeps the only full copy.

Where TensorFold puts the 433 GiB GLM-5.3 checkpoint Left: the GB300's 250.7 GiB of HBM holds attention, dense layers, shared experts, the 4-bit MTP head, a ring of 128 expert slots for each of the 75 MoE layers (9,600 slots, about 190 GiB), and four 32K-token KV caches. Right: Grace memory holds every routed expert, 19,200 of them, 380 GiB pinned. NVLink-C2C joins the two; a miss copies one 20.25 MiB expert into a slot. 433 GiB of model, 250.7 GiB of GPU. Half of every layer's experts live on the GPU. GB300 HBM · 250.7 GiB visible engine log: model 245.07 GiB, 7.8 GiB reserve, 0.0 left Attention, 3 dense layers, shared experts, MTP head ≈35 GiB as stored · MTP experts cut from bf16 to 4-bit at load Expert ring · 128 slots × 75 MoE layers 9,600 slots ≈ 190 GiB · 0 experts pinned for good each slot holds one expert: 20.25 MiB of NVFP4 weights + scales filled = expert resident · dashed = being refilled KV caches · 4 streams × 2.95 GiB --parallel 4 at a 32,768-token window, bf16 Grace LPDDR5X · 494 GiB bigger, slower, coherent with the GPU Every routed expert · 380 GiB pinned 256 experts × 75 layers = 19,200 · 5.06 GiB per layer the full copy: Grace is the source of truth, the ring only ever holds copies /proc/meminfo while serving: Shmem 379.8 GiB matches 75 × 256 × 20.25 MiB = 379.7 GiB NVLink-C2C · a miss copies one expert

Figure 1. Sizes come from the engine's startup log, the checkpoint's safetensors headers and /proc/meminfo during the October 13 run. "≈190 GiB" for the ring is my arithmetic (9,600 × 20.25 MiB); the engine reports one combined figure, 245.07 GiB.

3. One layer, one round: the expert ring

A decode round walks all 78 layers. At each of the 75 MoE layers, the router looks at every row in the round (one token per conversation, or several when drafting) and picks 8 experts per row. For each pick, the engine checks that layer's ring:

That is why half a layer's experts is enough to get good speed. Routing is not uniform: some experts get picked far more than others, and consecutive tokens tend to reuse some of the same experts. A ring holding the 128 a layer has used recently will catch most picks. It can never catch all of them, so every miss is paid for in C2C time.

One MoE layer, one decode round The router picks 8 of 256 experts for each row. The engine looks them up in the layer's 128-slot ring. Hits run straight from HBM. Misses claim a slot and a GPU copy kernel pulls the expert from Grace over NVLink-C2C, about 62 microseconds per expert at 340 GB/s. Then all eight run, and the shared expert is always resident. Each layer asks for 8 experts. Hits are free; every miss is a copy over C2C. Rows this round 1 token, or a draft chain, × every live stream Router top-8 of 256 per row ties broken by stable expert ID order This layer's ring · 128 slots green = hit · orange = resident, not asked for pink = slot claimed for a miss Expert kernels run NVFP4 weights read from slots, plus the shared expert, then the weighted sum hit Grace · all 256 experts pinned host memory the GPU can address directly miss: GPU copy kernel, 20.25 MiB ≈62 µs each at ≈340 GB/s What the binary says about the copy path startup: "0 experts pinned in HBM, a ring of 128 slots a MoE layer" and "Grace experts staged" GPU kernels: tf_offload_claim_predict, tf_offload_copy_predicted, tf_offload_copy names, not source: the prediction step is inferred, and the ring's hit rate is not exposed 75 MoE layers × 8 experts = 600 expert reads per token. The ring decides how many of them cross the link.

Figure 2. The copy kernels and the "Grace experts staged" message are taken from the binary itself. The engine does not report a ring hit rate, so the green/pink mix is illustrative, not measured.

The binary also contains kernels named tf_offload_claim_predict and tf_offload_copy_predicted. From the names, the engine seems to start some copies before the router asks for them. I have not seen the source for this CUDA family (it is not in the public tree yet), so take that as a reading of symbol names, not a description of the algorithm.

4. Drafts that cannot change the answer

GLM-5.3 ships with an MTP ("multi-token prediction") head: one small extra layer trained to guess the next few tokens. TensorFold uses it to propose a chain of guesses (about three a round on average tonight), then runs the full model over the whole chain in one pass and keeps the guesses up to the first one the model disagrees with, plus the model's own next token.

A drafted round costs more than a plain one, because more rows go through every layer. Tonight a plain round took about 21.7 ms and a drafted round about 38.7 ms. But a drafted round committed 2.65 tokens on average, and the extra rows mostly hit the same experts the first row already fetched. So one stream goes from 46 tok/s to 68 tok/s on a 1,024-token prose reply.

Drafting with the MTP head: propose, verify, commit Top lane: plain decode, one token per round, about 21.7 ms a round, 46 tok/s, 1,023 rounds for a 1,024-token reply. Bottom lane: the MTP head proposes a chain of about three tokens, the full model verifies the chain in one pass, and the engine commits the matching prefix plus one token: 2.65 tokens per round, about 38.7 ms a round, 68 tok/s, 386 rounds. The reply's fingerprint is identical either way. A round costs 1.8× more with drafts, and commits 2.65× more tokens. "draft": false · one row per round t1 t2 t3 t4 t5 each box = one full pass through 78 layers ≈ 21.7 ms 1,023 rounds · 46 tok/s 1,024-token reply, prose prompt Default · MTP head drafts a chain, the model checks it in one pass 1 · Propose MTP head guesses ≈3 tokens ahead 2 · Verify all chain rows in one pass; each expert fetched once 3 · Commit matching prefix plus one token 386 rounds · 68 tok/s 2.65 tokens per round 637 of 1,139 drafts kept (56%) Why the answer cannot change: every verify row is computed the way it would be alone Same group order, same K split, same rounding as a one-row pass (TensorFold's exact-decoding rule). A draft only decides how many rows go into a pass; the target model still picks every token. Measured here: drafted and plain replies carried the same output fingerprint on all 6 prompt pairs (e.g. sha 6ce30a8914d1 both ways, 1,024 tokens).

Figure 3. Round counts, draft counts and rates come from the server's per-request log lines and streamed timings for the 1,024-token prose prompt; the other three prompts committed 2.5–2.7 tokens a round.

The important part is the rule that drafts may not change the output. Batched math normally does change the output: a GPU summing four rows at once can split the work differently from one row alone, and floating-point rounding then differs in the last bit, which eventually flips a token. TensorFold's exact-decoding rule forbids that. A verify row uses the same group order, the same split of the long dot products and the same rounding it would use alone, and router ties break by a stable expert order. The draft only decides how many rows go into a pass; the model still chooses every token. Each finished request logs an output fingerprint, and the drafted and plain replies matched on all six prompt pairs I tried.

5. Shared rounds: why four streams beat one

With --parallel 4, TensorFold does not run four conversations side by side. It runs one round at a time with every live conversation's rows in it. That matters a lot for an offloaded model. Within a layer, the experts four conversations need overlap with each other, so the round fetches their union once, and every row that wanted an expert reads the same copy. Four conversations need fewer than four times the misses, and the fixed cost of walking 78 layers is paid once per round instead of four times.

The same exactness rule holds across streams: each conversation's reply must equal what it would have been alone. I checked this with identical prompts (four copies of the same prompt matched the solo reply) and with different prompts (each stream's fingerprint matched across repeats, and the prose and code streams matched their solo runs).

Aggregate decode speed at 1, 2 and 4 streams Bar chart of total tokens per second. vLLM slot cache on October 1: 51.6, 58.2, 26.4. TensorFold in my October 13 run: 63.4, 73.6, 79.9. TensorFold in Ash's run notes: 76, 84, 100. Each TensorFold stream equals its solo reply. Different prompts and contexts, so not a matched comparison. More streams add throughput instead of collapsing it vLLM slot cache · Oct 1 · 256K ctx TensorFold · my run · Oct 13 TensorFold · Ash's run.md 0 25 50 75 100 total tok/s 51.6 63.4 76 1 stream 58.2 73.6 84 2 streams 26.4 79.9 100 4 streams per stream at 4: TensorFold 21–25 · vLLM 6.6 Different prompts, reply lengths and context windows: read the shape, not the exact gap. Every TensorFold stream matched its solo reply.

Figure 4. vLLM figures are from our October 1 concurrency card (8 prompts, 512-token replies, 256K context). "My run" is tonight's four different prompts, 256-token replies, 32K context, total tokens divided by wall time; the 1-stream bar is the mean of the prose and code prompts measured the same way. Ash's numbers are from the run notes he sent James.

Our vLLM recipe fell apart at four users (26 tok/s total, 6.6 each), because four unrelated prompts thrashed a slot cache sized for one user and its prompts queued behind each other. TensorFold went the other way: 80 tok/s total, 21–25 for each stream. Each user is slower than alone, but the box does more work. Prompts are quick too: an 8,350-token prompt took 6.0 s to its first token, about 1,400 tokens a second.

6. What I measured tonight

James's Station, idle except for this test. Binary sha256 8e72be0a…f806 (tensorfold-native 1.0.8, chip nvidia-sm103), checkpoint GLM-5.3-NVFP4, flags as in Ash's notes: --context 32768 --parallel 4, greedy, thinking on. The model loaded in 187.8 s. Every reply below was still in its thinking phase when it hit max_tokens, so these are reasoning-text speeds.

CaseAsh's notesMy run, October 13
One stream, drafted (default)76 tok/s prose, 71 code (256 tokens)64.0–68.4 tok/s decode over 1,024 tokens (4 prompts); 62–65 tok/s including first token over 256
One stream, "draft": false48 tok/s44.3–46.2 tok/s (4 prompts)
Drafted equals plain?"the same, token for token"Yes: 6 of 6 prompt pairs, by full text (256) and fingerprint (1,024)
Two streams, total84 tok/s73.6 tok/s, different prompts (104 with the same prompt twice)
Four streams, total100 tok/s79.9 tok/s, different prompts (134 with the same prompt four times)
First token, ~1K-token promptabout 0.8 s0.87 s (1,041 tokens)
First token, ~8K-token promptabout 5.2 s6.0 s (8,350 tokens)
Four ~1K prompts sent togetherall first tokens in about 1.9 s1.1, 2.2, 3.3, 4.5 s
MTP drafts keptnot stated53–61% of drafted tokens on the 1,024-token runs

My single-stream and concurrency numbers come in 10–20% under Ash's. I do not know why yet: different prompt mixes, reply lengths and timing methods would all move them, and I did not try to reproduce his harness exactly. The "same prompt" rows show how much shared routing helps. Four identical conversations route identically, so the round copies each expert once for all four, and the box reaches 134 tok/s. Real traffic will sit closer to the different-prompts number.

7. Against our vLLM slot-cache recipe

Both engines are built on the same physics: experts in Grace, a per-layer cache in HBM, drafts to get more tokens out of each miss. A few honest differences before anyone reads Figure 4 as a scoreboard:

8. Running it

From Ash's notes, with the box-specific device UUID and paths removed. The GB300 has to be free (the engine takes nearly all of its HBM and about 410 GB of host memory), CUDA 13.2's cuBLASLt has to be on the library path, and the GB300 should be chosen by UUID, because CUDA's device order can differ from nvidia-smi's and the Station's display GPU must not be touched.

export CUDA_VISIBLE_DEVICES=<GB300 UUID from nvidia-smi -L>
export LD_LIBRARY_PATH=/usr/local/cuda-13.2/lib64
./tensorfold-native serve /models/GLM-5.3-NVFP4 \
  --host 127.0.0.1 --port 8080 --context 32768 --parallel 4

curl -s 127.0.0.1:8080/health
curl -s 127.0.0.1:8080/v1/chat/completions -H "Content-Type: application/json" \
  -d '{"model":"glm","temperature":0,"max_tokens":256,
       "messages":[{"role":"user",
         "content":"Explain how a refrigerator moves heat out of its cabinet, step by step."}]}'

Add "stream": true to stream. The reply's thinking arrives in reasoning_content before content; give it enough max_tokens or it will finish while still thinking (the server warns you). Add "draft": false to decode one token at a time and see the same reply more slowly. /metrics exposes Prometheus counters, including mtp_drafted_total and mtp_accepted_total. It listens only on 127.0.0.1.

9. What I know, and what I inferred