DGX Spark · documentation

Measurements

docs/measurements.md Last pushed 3 September 2026

All figures on one DGX Spark (GB10, 128 GB), the v0.1.0 image, the shipped config.yml, model resident and warm. Client-side numbers are what bench/bench.py prints: time to first token from request send, and decode rate = completion tokens ÷ (last chunk − first chunk), so they exclude prefill and include everything between you and the server. "Server" is TabbyAPI's own completion_tokens_per_sec. The full runs are in bench/results.md.

Short prompt, streamed

Three runs per cell, max_tokens: 800, medians.

Story, temperature 1.0 Story, temperature 0 Code, temperature 0 Code, temperature 1.0
prompt tokens 18 18 38 38
time to first token 0.60 s 0.60 s 1.1 s 1.1 s
decode, client 30.2 tok/s 31.8 tok/s 57.3 tok/s 53.3 tok/s
decode, server 29.7 31.2 53.9 50.3
draft acceptance 58–71 % 72–73 % 90 % 81–89 %
chunks per 794 tokens ~158 ~156 ~49 ~49

Story: "Write a short story about a lighthouse keeper (about 500 words)." Code: "Write a Python module implementing a thread-safe LRU cache class with get, put, capacity eviction, and a small pytest test file. Code only, no explanation."

The same story prompt sent through Open WebUI (server on the LAN, browser on a laptop) came out at 0.63–0.64 s to first token and 30.4–31.4 tok/s client-side against 29.9–30.8 server — the network adds nothing measurable.

What the numbers mean

  • The decode rate is set by the text, not the temperature. The MTP draft proposes ~6 tokens per step; the main model verifies them in one pass that costs about 2.6× a single-token pass on this weight-bandwidth-bound GPU. Code accepts ~90 % of proposals and runs at 53–57 tok/s; a short story accepts 58–73 % and runs at 30–32. Temperature 0 buys ~5 % on prose. Without the draft model everything runs at ~29 tok/s.
  • Chunk count is the draft step count. ~49 chunks for 794 code tokens means ~16 tokens accepted per step on average; ~158 chunks for the story means ~5. Streaming looks "bursty" on code and smooth on prose for that reason.
  • Time to first token on a short prompt is ~0.6 s, of which prefill of 18 tokens is ~0.15 s; the rest is the first draft step and the response plumbing. The code prompt's 1.1 s includes a longer first step.

Prefill

Prompt Prefill Rate Peak memory
8,800 tokens ~13 s 580–730 tok/s
100,000 tokens 125 s ~800 tok/s
300,000 tokens 374 s ~800 tok/s 106 GB used / 15 GB available
600,000 tokens (1M config) 938 s ~640 tok/s 113 GB used / 8 GB available

Prefill rate is flat to 300k thanks to sparse attention; the 1M configuration is slower because there is little memory left for activation buffers. Needle-in-a-haystack retrieval was correct at every one of those lengths.

Decode at long context

At the 384k configuration, with an 8.8k-token prompt already in context, decode on structured output measured 44–53 tok/s across the settings sweep — the same band as the short-prompt code figure. Decode does not fall off with context length on this pack because the sparse cache read stays small.

Memory

Used Available
idle, model + draft + caches resident 106 GB 15 GB
during a 300k-token prefill 106 GB 15 GB
1M-token configuration, idle 108 GB 12 GB

Cache pool sizes measured with the engine's own accounting: main 0.84 GiB at 131k, 1.68 GiB at 256k, 2.52 GiB at 384k; draft 0.4 / 0.75 / 1.1 GiB.

Things that do not move the needle

Tried and measured within ±2 tok/s of the shipped configuration: a stricter or looser draft confidence threshold, FP8 or 8-bit cache modes (ignored for the sparse layers anyway), dynamic draft length, prefill graph capture, chunk_size 4096. The GPU is reading weights the whole time; the levers that matter are how many tokens each weight read produces (the draft) and how many requests share it (max_batch_size).

Reproducing

./run.sh bench                                                  # the story prompt, temperatures 1.0 and 0
./run.sh bench --temps 0 --prompt "Write a Python module ..."   # any prompt you like

Run it on an idle server: with max_batch_size: 1 another request in flight shows up as queueing time in your time-to-first-token.

Every number here was measured. Open an issue if one looks wrong.

All documentation