Can you move the speculative drafter onto another machine?
draftJul 19, 2026 · 2x DGX Spark (GB10, 121 GiB unified LPDDR each) TP2 over 200 Gb RoCE, plus an RTX 5090 box on 1 GbE
Measuring what a remote drafter would cost and free — the wire, the draft time, and the reason the experiment stalled.
GLM-5.2 runs across two DGX Sparks with a dspark drafter co-located on them. Moving that drafter to a separate GPU box would free the memory bandwidth it consumes, but every speculative cycle would then cross a wire. Holding model, drafter and workload fixed: how much does the drafter actually cost on the Spark, how much does the hop cost, and is the difference worth an engine port?
- →The payload is the surprise. A dspark drafter conditions on aux hidden states, not tokens — 5 layers x 6144 dims x 2 bytes = 60 KiB *per token*, ~180 KiB per speculative cycle. The intuition that 'it's just a few token ids, the hop is free' is wrong by three orders of magnitude, and it's what makes the link speed matter at all.
- →The regime flips, but not where you'd guess. Over 100 Mb AND 1 GbE the hop is bandwidth-bound - latency scales linearly with payload at a saturated wire (11.5 MB/s and 107.6 MB/s respectively). Only at 200 Gb RoCE does it go latency-bound, flat at ~0.2 ms regardless of size. So the crossover sits between 1 GbE and the fabric, not between 100 Mb and 1 GbE - and on any Ethernet you actually own, this is a bandwidth problem.
- →On any link worth having, the hop is not the obstacle. 0.198 ms over RoCE and 1.713 ms over 1 GbE are 0.2% and 1.6% of a 107.8 ms cycle. Only the 100 Mb path (16.02 ms, -12.9%) would sink the idea, and that turned out to be a marginal old cable plus a 10/100 router port - both since fixed. The interesting variable was never the wire.
- →Relocation looks worth building, on a measured number rather than an inferred one. t_draft = 15.88 ms directly (draft_fwd 9.91 + markov_sample 4.75 + ctxkv 1.23, via the fork's VLLM_DSPARK_PROFILE hooks) - 17.8% of the 89.34 ms K=2 cycle. draft_fwd lands within 23% of the 12.8 ms predicted by reading the speculator's 3.5 GiB/rank at GB10's ~273 GB/s, so the draft really is a weight read and moving it off-box frees that bandwidth. Net of the measured hop AND an estimated ~2.5 ms remote draft on the 5090, that projects 24.05 -> ~27.6 tok/s over 1 GbE (+15%).
- →The break-even is not the hop alone - it is hop + remote draft time vs the 15.88 ms freed. Over 1 GbE that budget is ~4.2 ms against 15.88, a comfortable 3.8x margin. Over the 100 Mb link the box originally had it was ~18.5 ms against 15.88 - a net loss. The margin is real but it is not unlimited, and it is why the link mattered at all.
- →UNFINISHED, and the blocker is capacity rather than physics. The premise all along was that the RTX 5090 box had spare room; it does not. Both GPUs are ~full with a long-running ASR + TTS + 3x vLLM stack, leaving ~3.6 GiB free against the drafter's 7.1 GiB, so the drafter could not even be loaded to time it. Measured read bandwidth there (1658 GB/s) instead puts t_draft_remote at ~6 ms single-GPU, revising the projected end-to-end gain to ~+10%. Worth recording that the estimate fell 45% -> 22% -> 15% -> 10% as each assumption was replaced by a measurement - the direction of that drift is more informative than any single value.
Results
| Run | Hop costms/cycle | Decodetok/s | Cycle timems |
|---|---|---|---|
| Co-located (K=2) | 0 | 24.05 | 89.34 |
| Remote · 200Gb RoCE* | 0.198 | 28.22 | 76.16 |
| Remote · 1 GbE* | 1.713 | 27.67 | 77.67 |
| Remote · 100Mb Ethernet* | 16.02 | 23.36 | 91.98 |
* indicates a derived or estimated value — see the write-up.
Background
We have two DGX Sparks running GLM-5.2 (753B MoE, TP2) with a dspark speculative drafter co-located on them, and a separate box with two idle RTX 5090s. The tempting move: relocate the drafter onto the 5090s. The Sparks are memory-bandwidth-starved; the 5090s have bandwidth to spare and 64 GB of GDDR7 doing nothing. Free the Sparks of draft work, maybe even run a bigger drafter that pushes acceptance up.
The catch is that a speculative cycle becomes a network round trip. So the question isn't "is the 5090 faster at drafting" — obviously it is — it's whether the wire is cheap enough that the answer matters.
Method
What has to cross the wire
This is where the intuition "it's only a few token ids, that's nothing" breaks
down. A dspark drafter isn't conditioned on tokens — it's conditioned on the
target model's aux hidden states, pulled from layers [8, 23, 39, 55, 70].
Per token, that's 5 × 6144 × 2 bytes (BF16). Multiply by the tokens accepted
per cycle and you get the real per-cycle payload. The return trip (K draft token
ids) genuinely is nothing.
The rungs
Same payload, same cycle, three link speeds spanning ~2000×:
| rung | link | measured hop (180 KiB) | regime |
|---|---|---|---|
| 100 Mb/s | Ethernet, GPU rig → router → Spark-1 | 16.02 ms | bandwidth-bound, 11.5 MB/s |
| 1 GbE | Ethernet via the gigabit router | 1.713 ms | bandwidth-bound, 107.6 MB/s |
| 200 Gb/s | ConnectX / RoCEv2, Spark↔Spark | 0.198 ms | latency-bound, flat |
All three measured with hopbench (stdlib TCP, TCP_NODELAY, real
request/response shape rather than bulk streaming — an iperf3 number would
answer the wrong question).
Findings
Bandwidth-bound or latency-bound?
If hop cost falls off a cliff between rungs, the transfer is bandwidth-bound and a faster link fixes it. If it's flat, it's latency-bound and no amount of bandwidth helps. The answer turned out to be both, depending where you stand: the two Ethernet rungs are cleanly bandwidth-bound (latency linear in payload, wire saturated), and only the RoCE fabric goes latency-bound. So the crossover sits between 1 GbE and the fabric — not, as I'd assumed, between 100 Mb and 1 GbE.
Why the answer probably isn't "just move it"
Two results already on this stack constrain what relocation could possibly buy:
-
The draft's scheduling was never the cost — but its bandwidth might be.
MTP-DRAFT-COST.md(session 12) records Approach A — wrapping the drafter in a FULL cudagraph — as built, confirmed engaged, and refuted: 18.5 → 18.5 tok/s. It's tempting to read that as "the draft is free, so relocating it frees nothing." That reading is wrong: the cudagraph removed host launch gaps, not the drafter's weight reads. On a machine whose binding constraint is memory bandwidth, those reads are the cost.Measured directly,
t_draft= 15.88 ms —draft_fwd9.91 +markov_sample4.75 +ctxkv1.23, from the fork's ownVLLM_DSPARK_PROFILEhooks. That's 17.8% of the 89.34 ms cycle at the K=2 optimum, anddraft_fwdalone lands within 23% of the 12.8 ms you'd predict from just reading the speculator's 3.5 GiB/rank at ~273 GB/s. The draft really is a weight read, and moving it off-box frees that bandwidth.Getting here required care. Inferring
t_draftfrom the spec-depth sweep's intercept gave 2.8 ± 9.3 ms — worthless, because run-to-run scatter exceeded the quantity (see the depth study). And the first direct profile readctxkvat 362 ms inside an 89 ms cycle, because the leadingcuda.synchronize()of each step was absorbing the previous step's verify forward. Draining the queue before the timer starts fixed it. -
The acceptance ceiling isn't a capacity problem. Four independently trained same-architecture drafts — including a from-scratch 4B — all converge at pos-0 ≈ 0.72–0.75. Sparkulator v4, which is exactly the "richer conditioning" idea, was built and failed GATE B. Confidence-gated dynamic K measured NULL. A bigger drafter on a bigger GPU doesn't obviously address an information bottleneck.
Which means the interesting version of this question isn't the synchronous one.
The verdict
Put the two measured numbers side by side and the question answers itself:
freed by relocating the drafter : 15.88 ms
cost of the hop : 0.198 ms (200 Gb RoCE)
1.713 ms (1 GbE)
16.02 ms (100 Mb Ethernet)
The break-even hop is 15.88 ms. Over gigabit the hop costs about a ninth of what it frees, projecting 24.05 → 28.59 tok/s (+19%); over the RoCE fabric, +21%. And 100 Mb Ethernet costs 16.02 ms — a dead heat with break-even, to within 1%. The single link this GPU box actually had was, by coincidence, precisely the point where the idea stops being worth doing.
Limitations
Two caveats worth keeping attached to those projections. They assume the whole
15.88 ms is genuinely freed and nothing else stalls — the aux hidden states are
already computed as part of the verify forward, so that part is safe, but no
remote proposer exists to serve, so this remains an extrapolation from two
measurements rather than an end-to-end result. And t_draft was measured under
profiling, whose extra syncs suppress any overlap that might exist in the
unprofiled engine, making 15.88 ms an upper bound on what is truly serialized.
Open questions
The remaining obstacle is engineering, not physics: dspark's proposer runs in-process in the Ray TP workers, and the only hidden-state export path writes safetensors to disk for offline distillation. A synchronous remote proposer is a real vLLM port. But it is now a port with a measured payoff attached to it.