GB300 GLM-5.3-Flash Round 3: I Was Benchmarking the Wrong Thing, Then Seven Axes Closed
enable_thinking variable, so the "thinking off" my probes sent was ignored and every C1 and acceptance row in round 2 was max-effort reasoning with the thinking text counted as output. Fixed the ruler first, re-measured, then ran eight one-axis windows on top. One moved the needle (a newer nightly, +2.8%). One was a wash worth knowing (block size). Five closed with receipts. Recipe: J-M-Recipes PR #25.
The ruler was wrong
Round 2 sent chat_template_kwargs: {"enable_thinking": false} on every benchmark request and I reported the result as "thinking default." It was neither. GLM-5.3's Jinja template has no such variable; the generation prompt always emits <|assistant|><think>. The only control the template honours is reasoning_effort ∈ {low, high}, and anything else — including absent — renders <|system|>Reasoning Effort: Max. So the model reasoned at full effort, the reasoning parser was told not to expect a think block, the thinking landed in content, and the token counter counted it. 252 tok/s was the speed of the model thinking out loud at length, not the speed of it answering.
The tell was sitting in my own receipt: think-default 256.5 vs think-OFF 256.7, reasoning_tokens=None on both. A flag that does nothing produces identical numbers. I read that as "thinking is cheap here" instead of "the flag is dead."
chat_template_kwargs: {"reasoning_effort": "low"}, temperature 0, same prompts. Both instruments (c1_methods.py, accept_probe.py) now take EFFORT and the recipe requires the effort be reported with every C1 number. This is upstream-tracked as sglang#39227, which lands a proper split in the 09-16 nightly.Re-measured on the same 09-11 image, same drafter, same knobs (window W0):
| prompt class | round 2 published (max-effort, thinking counted) | W0, effort=max(reproduces round 2) | W0, effort=low, answer-only |
|---|---|---|---|
| history essay 512 (recipe method) | 257.7 | 197.1 | |
| code 400 | — | 282.0 | |
| prose 300 | — | 147.4 | |
| shell-ops 200 | — | 160.7 | |
| acceptance, 7-class mean | 0.40 | 0.41 | |
| acceptance, prose | 0.29 | 0.17 | |
| acceptance, code | 0.41 | 0.48 |
Two things fall out. The class spread widens once thinking is out of the way: code gets faster (282 vs 253), prose gets slower (147 vs 175), because max-effort thinking is uniformly prose-ish and was averaging every class toward the middle. And the mean acceptance barely moves (0.39 → 0.41) while the per-class numbers move a lot — so "acceptance is 0.29 on prose" in round 2 was really "acceptance on thinking text is 0.29." The 0.54–0.67 the vLLM port reports is therefore confounded three ways at once (effort, drafter revision, prompt mix) and I stop citing it as a gap to close until someone measures both stacks with the same ruler.
Eight windows, one axis each
With the ruler fixed, the campaign was: fresh container per window, one flag or artifact changed, block-7 control re-run inside every session so boot-to-boot drift is measured not assumed, greedy-equivalence on 20 prompts plus teacher-forced per-token |Δlogp| against the control for every axis that is supposed to be lossless, KL against the FP8 oracle for every axis that is not. First hit of every batch shape discarded, three reps, spread reported; any C≥8 spread over 10% is a leaked autotune pass and the row is void and re-run.
| window | axis | C1 recipe, low | gates | verdict |
|---|---|---|---|---|
| W0 | ruler: reasoning_effort=low | 197.1 | — | calibration; correction above |
| W1 | drafter incoai/GLM-5.3-Flash-DFlash2 @ bf582e4e (Aug 31) vs 7d74cdd8 | 197.5 · accept 0.39 | — | no gain; kept 7d74cdd8 |
| W2 | nightly 20260915-8874c51a (carries #37818 DFlash mamba-checkpoint fix) | 202.7 (+2.8%) | tools 10/10 ×2 · Hermes agent 10/10 | new daily image |
| W3 | SGLANG_OPT_FUSED_KDA_VERIFY=1 (post-#39219) | 200.5 | greedy 1/20 vs W2 (W2-vs-W2 20/20) | closed: not lossless |
| W6c | DFlash2 block 5 / 6 / 7, hook-free | 212.3 / 207.0 / 202.6 | all 20/20 greedy | block 7 kept (see below) |
| W5 | --enable-linear-replayssm-spec — drop the 9.57 GB intermediate SSM buffer | — | — | rejected at boot |
| W4 | --dsa-prefill/decode-backend trtllm (cookbook GB300 pairing for fp8 MLA KV) | 202.1 | KV pool lines byte-identical to control · KL 0.1360 (= baseline) | no-op on glm5_next |
| W7 | native MTP: NextN layers.45, EAGLE steps 5 / topk 1 / draft 6, no adaptive, no drafter | 186.4 (−7%) · code −20% | accept 3.16 / 0.43 natural · 20/20 · p99 0.0 · tools 10/10 | DFlash2 stays |
| W8 | --moe-runner-backend flashinfer_trtllm | 201.6 · C8 756 (+3%, inside spread) | KL identical to 4 digits | flat; closed |
Control row for the second session (W4/W5/W7/W8, all booted against it within 90 minutes): C1 201.0, prose 150.5, code 287.0, accept 3.48 / 0.41, greedy 20/20, teacher-forced self-score repeat-identical on 2,857 tokens, clean C8 732 agg / 91.5 per user at 6% spread. The block-7 control reproduced W2 within 0.1% on every C1 class across two sessions two hours apart. Boot-to-boot noise on this image, for C1, is not the problem. C8 is — see method.
Block size is a trade, not a win
Round 2 found block 7 beat block 8. The natural question was whether 6 or 5 beats 7. Hook-free, same image, three boots back-to-back with a block-7 control last:
| block | recipe method | prose 300 | code 400 | shell 200 | accept len / rate |
|---|---|---|---|---|---|
| 7 (control) | 202.6 | 151.3 | 289.0 | 162.4 | 3.46 / 0.41 |
| 6 | 207.0 (+2.2%) | 158.0 (+4.4%) | 286.7 (−0.8%) | 165.8 (+2.1%) | 3.26 / 0.45 |
| 5 | 212.3 (+4.8%) | 156.7 (+3.6%) | 271.1 (−6.2%) | 162.9 (+0.3%) | 3.03 / 0.51 |
Shorter blocks commit fewer tokens per verify but accept a higher fraction of what they draft. Net effect is within ±5% everywhere and the sign flips by class: prose likes short blocks, code likes long. There is no block that wins everywhere, and the biggest single move in the table is block 5 losing 6% on code. Block 7 stays; it is the code-best setting and the recipe's agent workload is code-heavy. If you serve mostly prose, block 5 is a free 4–5%. Block 5 also posted the only clean C8 of that session (776 agg) — but its two siblings leaked a warm pass and are void, so I have no concurrency comparison to offer from it, and I will not manufacture one from a single row.
Five axes closed, and why that is the useful part
Three of the five were code-read from SGLang main in the planning doc and looked ready. Two of those three did not survive the boot log.
- Fused KDA chain-verify (W3) engaged — the boot log said so — and sped nothing up (200.5 vs 202.7). It also changed greedy output on 19 of 20 prompts, diverging as early as character 4. The PR describes ~1 ulp reduction-order differences; this is not that. Closed, with the receipt (
greedy-w2-vs-w3.txt). Not filed upstream yet. - ReplaySSM spec-verify (W5) was the memory axis I wanted most: it replaces the per-draft-token fp32 SSM snapshots (9.57 GB at 48 slots, the thing capping DFlash2 concurrency) with a raw-input ring. It rejected at pool allocation:
ValueError: --enable-linear-replayssm-spec with DSPARK/DFLASH requires a KDA (kimi_linear) model; got a non-KDA model.The guard keys on Kimi's config shape. The KDA kernels themselves accept GLM-5.3 — W3 proved that — so this is a guard being stricter than the code path it protects. That is a legitimate upstream ask; I am not making it in this post. - TRT-LLM DSA backends (W4), the cookbook's GB300 pairing for real fp8 MLA KV, produced KV-pool lines byte-identical to the control: fp8 indexer 3.81 GB, bf16 K 5.50, bf16 V 5.50, 577,024 tokens. DSA is DeepSeek sparse attention; GLM-5.3 does not have one; the flags parse and do nothing. "FP8 KV is half true" in the recipe's known-limits stays exactly as written.
- flashinfer_trtllm MoE (W8): flat C1, C8 +3% inside an 8% spread, KL vs the FP8 oracle 0.1360 — the same four digits as W4 and the 09-11 baseline. Either it did not engage or it is the same kernel by another name. One attempt was the budget; one attempt was taken.
- Drafter Aug 31 revision (W1): three "Checkpoint update" commits with no changelog. Acceptance 0.39 vs 0.41. Kept the August 27 release.
None of this is wasted. Each closure is a flag someone else does not have to try on this model, and each carries the receipt that would let them argue with me.
Native MTP works now — and still loses
Round 2 said native MTP "crashes on first request / misaccepts" citing #36829. On 8874c51a it boots, loads the NextN layer from layers.45 under NVFP4, answers, calls tools 10/10, and matches the control's greedy output 20/20 with teacher-forced p99 |Δlogp| of exactly 0.0. Natural acceptance length is 3.16 at rate 0.43 — not the ~1.0 that issue reports, so whatever that bug is, it is not live on this path. That is the first natural (not SGLANG_SIMULATE_ACC_LEN) MTP acceptance number I have seen published for this quant.
It still loses to DFlash2 on every axis that matters here. C1 recipe 186.4 vs 201.0 (−7%), code 228.5 vs 287.0 (−20%), C8 712 vs 732. And the memory story is backwards from what I expected: the NextN layer costs 15.38 GB versus the DFlash2 drafter's 2.68, so the KV pool shrank 4%. The cookbook's 5.98 acceptance on FP8 is on a different quant with a different draft depth; 3.16 on NVFP4 with depth 6 is what this box does. Recipe known-limits updated to say so.
Method, and what I would do differently
- Fix the ruler before touching the engine. Every window after W0 is interpretable only because W0 came first. If I had run W1–W8 against the round-2 ruler, every acceptance delta would have been noise on top of thinking text.
- The first C8 pass after warm still leaks. Four of six C8 measurements this campaign had one rep at ~135 tok/s against two at ~700 — a 110%+ spread from a single autotune-shaped rep sneaking through the warm pass. The runner now detects spread >10% and rewarms-and-reruns automatically; all four recovered to ≤8%. Anyone quoting a C8 from this stack without a spread is quoting a coin flip.
- Same-session control every time. Cheap (one extra boot) and it turned "W2 is 2 hours old, is it still the reference?" into a measured 0.1%.
- Code reads find flags, not preconditions. W4 and W5 both passed a careful read of the argument parser and the hook that consumes them. Neither survived the pool builder. Boot log or it did not happen.
- Automate the whole window. Each of the last four ran detached on the box from a single script with stop-and-keep containers and nothing restored on exit. The two bugs I hit (a doubled
/v1in a URL, a container name not exported) each cost twenty minutes and were fixed once. Nothing was rerun by hand.
Everything in this post is in results/2026-09-15-effort-drafter-nightly: 61 rows in throughput.csv with spreads and source files, per-window boot excerpts, greedy comparisons, teacher-forced JSON, KL reports, and the verbatim W5 rejection. The recipe's headline metric is now 202.7 with a second row at 265.3 labelled for what it is.
Credits
ebfio for the vLLM DFlash2 overlay and the acceptance numbers that started the question; incoai for the drafter; NVIDIA for the first-party NVFP4; SGLang contributors on #39227 (b8zhong — the effort-template rule that made me look), #39219 (mmangkad), #37818, #36821, and the GLM-5.3-Flash cookbook. Community numbers cited above are labelled with their stack and whether acceptance was natural or simulated.