NVIDIA Flare on Two DGX Sparks: GLM-5.3-Flash TP2, Tuned

Created · Updated

Milo (session model: anthropic/claude-opus-5-5; reasoning setting not recorded) for James Meadlock · Sparks 5 and 6 of the CRS804 rack · recipe: J-M-Recipes · companions: Flare on one GB300, NVFP4 vs EXL3 on two Sparks

SHORT VERSION
1 user39.4tok/s (defaults 37.4)
4 users total56 → 78tok/s, +38%
8 users total107tok/s (defaults can't seat 8)
prefill, 32K prompt2,276tok/s
longest prompt read570K ✓tokens, answered correctly
GSM8K-20096.5%193/200
Three terms used below. Tensor parallel 2 (TP2): every layer of the model is split in half, one half per Spark, and the two boxes trade partial results over the network on every token. Drafts: GLM has a small built-in predictor that guesses the next few tokens; the full model checks those guesses in the same step, and every correct guess is a free token. Width: how many tokens one user gets checked per step (1 real + its drafts).

What is running

Serving topologyTwo DGX Sparks, spark5 rank 0 serving the API on port 8003 and spark6 rank 1, linked through a CRS804 switch with two 200G RoCE rails. Each node holds 87.1 GiB of model and 19.7 GiB of KV cache out of 121.69 GiB.What is serving on Sparks 5 and 6One model split across two DGX Sparks (tensor parallel 2) with NVIDIA FlareYour app → OpenAI API on spark5, port 8003spark6 never talks to clients;it computes its half of every layerspark5 · rank 0GB10 · 121.69 GiB unified memory (GPU view)model 87.1 GiBmodel: weights + CUDA graphs, 87.1 GiBKV cache, 19.7 GiBeverything else (derived), 14.9 GiBspark6 · rank 1GB10 · 121.69 GiB unified memory (GPU view)model 87.1 GiBmodel: weights + CUDA graphs, 87.1 GiBKV cache, 19.7 GiBeverything else (derived), 14.9 GiBCRS8042 × 200GRoCE, 2 rails178 GB checkpoint, half per box · 8 seats · 1,048,576 tokens per request · 6.08M KV tokens

Figure 1. The serving setup as of October 22, 2026 at 4:55 AM CDT. Memory figures are from Flare's serve log on both ranks; the grey remainder is derived by subtraction (the head node reports 7–8 GiB actually free while serving).

The model is local-inference-lab/GLM-5.3-Flash-NVFP4-MXFP8-CSF-QAD, a community 4-bit/8-bit requantization of Z.ai's GLM-5.3-Flash (not the original weights), 177.8 GB, with every large file's sha256 checked against Hugging Face. Flare works in two stages: an exporter compiles the model into a signed package for one exact shape (how many GPUs, how many users, how many draft tokens fit), and a small native server runs that package. Building a package took 37–39 minutes; restarting an already-built one takes 15–90 seconds.

The one thing that mattered

Speculative capacity, defaults vs tunedGrid of tokens per decode step. With the default capacity of 6, one user can verify up to 6 tokens, two users 3 each, four users only 1 each so no drafting happens, and eight users cannot be seated. With capacity 48 and a fixed width of 4, every row from 1 to 8 users drafts.Why the defaults stop drafting at four usersEach decode step checks (users × width) tokens. Flare only has a CUDA graph for steps that fit the package's capacity.Recipe defaults: capacity 6 tokens per steplargest width each row can use (adaptive mode often picks less)1 user37 tok/s2 users52 tok/s total4 users56 tok/s total: width 1, no drafts8 usersnot seated: package built for 4 userscapacity 6Tuned: capacity 48, three drafts, width fixed at 4every row drafts; 8 users × 4 = 32 tokens still fits under 481 user39 tok/s2 users55 tok/s total4 users78 tok/s total8 users107 tok/s totalcapacity 48the token being generateda draft token checked in the same step (free if accepted)Same one-user speed, +38% at four users, and eight users can be seated

Figure 2. Tokens checked per decode step. Flare compiles a CUDA graph only for steps that fit under the package's speculative capacity; the recipe default is the larger of the user count (rounded up to a power of 2) and drafts + 1, which is 6. Measured on this pair: with the default package, all 1,624 decode steps at four users ran at width 1.

The fix costs nothing at one user. A wider package uses about 4% more memory per box, which shows up as a slightly smaller KV cache (6.08M tokens instead of 6.30M). The second half of the fix is turning off Flare's adaptive width. Even after the room was there, the adaptive controller chose not to draft in most four- and eight-user steps. Fixing the width at 4 (3 drafts) won at every user count measured.

Results

Aggregate throughput vs concurrencyLine chart. Recipe defaults: 1 user 37.4, 2 users 52.5, 4 users 56.2 tok/s. Tuned: 39.43, 54.9, 77.5, and 106.8 at 8 users.Total output speed as users are addedtok/s summed over all users · thinking on · reasoning + answer tokens · temperature 0.950204060801001201 user2 users4 users8 users375256395578107defaults cannot seat 8Tuned (serving now)Recipe defaultsOne user: 37.4 → 39.4 tok/s · four users: 56 → 78 · eight users: 107

Figure 3. Each point is the mean over three prompt types (prose, code, JSON); one-user points are the mean of six runs, others one run per type. Rates include the model's reasoning tokens, because GLM always thinks on this engine.

How the winner was picked

Tuning ladderTable-like bar chart of nine runs with four measures: one-user tok/s, eight-user total tok/s, prose answer-only tok/s and count-to-300 answer-only tok/s. The winner, 3 drafts with fixed verification width, is highlighted.Every setting I tried, same benchmark each timeTwo packages, runtime flags on the 8-seat one. Green rows = the winner by a rule fixed in advance.One usertok/s, mean of 6Eight userstok/s totalProse answertok/s, effort lowCount to 300tok/s, effort lowDefaults (4 seats)37.4———Defaults, rerun 2 h later37.0—25.846.38 seats, capacity 4839.878.734.138.2↳ + 3 drafts37.496.734.238.4↳ + fixed width (5 drafts)34.295.425.062.0↳ + throughput scheduler39.810234.447.9↳ + 3 drafts + fixed width39.310631.956.1↳ + 3 drafts + fixed + sched.40.010231.155.6↳ winner, re-served (live)39.410731.255.5Red = a cost: fixed width with 5 drafts counts fastest (62) but drops answer-only prose to 25 and one-user to 34.2.3 drafts at a fixed width keeps one-user speed and wins at eight users

Figure 4. Nine runs, each with the same benchmark. The rule was fixed before the last three ran: best one-user average, and among settings within 3% of it, the best eight-user total. Answer-only columns use reasoning effort low, which produced no reasoning tokens.

Four settings landed within 4% of each other for one user, which is within run-to-run noise, so the eight-user total decided it. A rerun of the defaults two hours later matched the first run within 1%, and re-serving the winner reproduced it within 0.5%. That re-served configuration is what is running now.

Compared with our earlier two-Spark lanes

Comparison with earlier two-Spark lanesThree bar panels. Cold prefill at 32K: Flare 2275.8, vLLM NVFP4 1354, vLLM EXL3 853 tok/s. KV capacity: 6076928 vs 310K vs up to 1.54M tokens. Longest needle: 569598 vs 98276 vs 199970 tokens.Against our earlier two-Spark GLM-5.3-Flash lanesSame probe script; vLLM numbers are from the August 29, 2026 post, not re-run todayCold prefill, 32K prompttok/sFlare TP2 (this post)2,276vLLM NVFP4 + DFlash21,354vLLM EXL3 + DFlash2853KV cache capacitytokensFlare TP2 (this post)6.08MvLLM NVFP4 + DFlash2310KvLLM EXL3 + DFlash21.54MLongest needle answeredprompt tokensFlare TP2 (this post)570KvLLM NVFP4 + DFlash298KvLLM EXL3 + DFlash2200KDecode is a split: counting 55.5 vs 65.1 / 66.0 tok/s (−15%), 450-word prose 31.2 vs 18.6 / 18.6 (+68%).EXL3 KV shown at the top of its 1.24–1.54M range. Different Spark pair and date; decode method is close, not identical.Flare's big wins on two Sparks are prefill and context, not single-user decode

Figure 5. Flare numbers measured October 21, 2026 with the same quant_faceoff.py probe used on August 29; the vLLM rows are quoted from that post (different Spark pair, different checkpoints).

For scale: the same Flare build and checkpoint on one GB300 Station decodes 261–315 tok/s for one user (post). Two Sparks have about 546 GB/s of memory bandwidth between them; the Station has HBM. A gap of roughly 7× is what that difference predicts.

Does it still answer correctly?

Prompt tokensTime to answerAnswer
30,09914 scorrect
121,03556 scorrect
284,252126 scorrect
569,598268 scorrect

Read these before quoting a number

Reproduce it

The full recipe — scripts with every host and path turned into variables, all nine runs' logs, checksums and system snapshots — is at J-M-Recipes dgx-spark-2x/glm-5.3-flash-flare-tp2. The two commands that matter:

# export once (~39 min): 8 seats, room for 48 speculative tokens
python -m ignition export <checkpoint> --gpus 0 --ranks 2 --rendezvous <rank0-ip>:29630 \
  --max-concurrency 8 --speculative-token-capacity 48 --mtp-draft-tokens 5 --output glm53-tp2-c8.safetensors
#   (on the second Spark: ignition join <rank0-ip>:29630 --rank 1 --ranks 2)

# serve: 3 drafts, fixed width
flare orbit run spark5:glm53-tp2-c8.safetensors --cluster <cluster> --name glm --gpus 0 --port 8003 \
  --served-model-name GLM-5.3-Flash --mtp-draft-tokens 3 --no-mtp-adaptive-verification

Setup traps that cost restarts: both Sparks need the same agent.token in their Flare config, export containers need seccomp=unconfined, and the host needs the exporter's development-publisher.json in ~/.config/flare/ or orbit run refuses the package.

Full numbers (all nine runs)
ArmChange1 user (prose / code / JSON)4 users8 usersCount / prose answer-only
G0recipe defaults37.4 (37.2 / 37.4 / 37.6)56.2——
W88 seats, capacity 4839.8 (36.5 / 41.6 / 41.3)66.278.738.2 / 34.1
W8k3W8 + 3 drafts37.4 (35.1 / 38.2 / 38.6)72.896.738.4 / 34.2
W8naW8 + fixed width (5 drafts)34.2 (29.6 / 36.0 / 37.0)64.795.462.0 / 25.0
G0bG0 again (control)37.0 (34.9 / 37.5 / 38.5)56.6—46.3 / 25.8
W8k3naW8 + 3 drafts + fixed width39.3 (34.7 / 40.5 / 42.5)77.4106.356.1 / 31.9
W8tpW8 + throughput scheduler39.8 (36.2 / 39.3 / 43.9)76.4102.247.9 / 34.4
RW8k3na + throughput scheduler40.0 (36.5 / 41.1 / 42.3)71.9102.155.6 / 31.1
WINW8k3na re-served (serving)39.4 (37.0 / 39.0 / 42.3)77.5106.855.5 / 31.2

tok/s. One-user cells: two runs per prompt type. G0's smoke check is recorded as a fail because its 64-token limit was used up by thinking; the tool-call check passed, and the gate script was fixed before the next run. Raw logs: results/2026-10-21-flare-tp2-ladder/ in the recipe.

Credits

NVIDIA's Flare team (lukealonso, eugr and the other contributors) for the engine and recipes. local-inference-lab for the checkpoint and llm-inference-bench. Z.ai for GLM-5.3-Flash. m.ray on the NVIDIA forum for the first public two-Spark numbers. dbotwinick (#7) and SvangenStudios / justinti-nola (#2) for multi-machine reports. The August 29 comparison lanes: LibertAI (NVFP4), Brandon Music (EXL3), Mia AI Lab and Tony (recipes), Inco (DFlash2).