test(frank-medium): A/B DFlash2 vs MTP on FP8 — DFlash2 wins on decode

DFlash2 (100.0 t/s decode, 287.1 t/s burst-4) beats MTP self-speculation
(85.9 t/s, 179.7 t/s) despite a lower accept length (4.24 vs 5.66): the
small separate drafter steps faster than the target's own MTP layer applied
autoregressively. DFlash2 is the deployed shape; both swap-script paths now
default to dflash/7. Docs updated.
This commit is contained in:
Frank committed 2026-09-20 22:20:53 -07:00
1 parent 1ae73db084
commit f998a2d9f2
6 files changed
+111 -44

No files matched your search

@@ -54,7 +54,7 @@ From `dankserver/frank-medium/quadlet/frank-medium.container`:
| Setting | Value |
|---|---|
| Model | `orcarouter/Qwen3.8-27B-Uncensored-FP8` (FP8 E4M3 block-wise 128×128, 30.9 GB / 7 shards) |
| Speculative | `--speculative-config {"method":"mtp","num_speculative_tokens":8}` (the model's own single-layer MTP head; mean acceptance 5.66/8, 66.2% draft acceptance) |
| Speculative | `--speculative-config {"method":"dflash","model":"/models/Qwen3.8-27B-DFlash2-FP8","num_speculative_tokens":7}` (the z-lab DFlash2 external drafter; mean acceptance 4.24/7. A/B tested against MTP self-speculation — DFlash2 wins on decode, see below) |
| Parallelism | `--tensor-parallel-size 2` (`ROCR_VISIBLE_DEVICES=0,1`) |
| Context | `--max-model-len 262144` |
| Concurrency | `--max-num-seqs 8`, chunk 8192 |
@@ -67,17 +67,43 @@ From `dankserver/frank-medium/quadlet/frank-medium.container`:
| Chat template | `qwen-fixed-v22.3.jinja` (patch dir bind-mounted, `MM_MERGE_ENGINE=AXROW_SEPARATE` for Gemma4 vision) |
**The KV pool is auto-sized, not pinned.** Production ships no `KV_MEM` pin
(`KV_MEM=0`): the pool comes up at whatever VRAM the weights leave. The FP8
checkpoint is bigger than the MXFP4 one (30.9 vs 19.4 GiB of weights), so it
leaves less for the pool: the 2026-09-20 FP8 start auto-sized to **461,540
tokens = 1.76× the full window** (`GPU KV cache size: 461,540 tokens`,
`Maximum concurrency for 262,144 tokens per request: 1.76x`). The MXFP4
rollback target auto-sized to 943,581 tokens (3.59×) on the same cards.
Per-token cost in steady state is 19,673 B — fp8 KV across the model's 16
full-attention layers plus the MTP drafter's. After the first healthy start,
(`KV_MEM=0`): the pool comes up at whatever VRAM the weights + drafter leave.
The FP8 checkpoint is bigger than the MXFP4 one (30.9 vs 19.4 GiB of
weights), and the DFlash2 drafter is a separate 2.1 GiB model, so the pool
is the smallest of the three shapes: the 2026-09-20 DFlash2 start auto-sized
to **384,238 tokens = 1.47× the full window** (`GPU KV cache size: 384,238
tokens`, `Maximum concurrency for 262,144 tokens per request: 1.47x`). The
MTP shape (no external drafter) auto-sized to 461,540 (1.76×); the MXFP4
rollback target to 943,581 (3.59×). After the first healthy start,
recalibrate with `calibrate-kv.sh` to use the extra VRAM on the 34.2 GiB
cards.
## DFlash2 vs MTP — A/B on the FP8 checkpoint (2026-09-20)
The FP8 checkpoint carries its own single-layer MTP head, so it can self-
speculate (`SPEC_METHOD=mtp`) or use the z-lab DFlash2 external drafter
(`SPEC_METHOD=dflash`, a separate quantization-agnostic 2.1 GiB
`DFlash2DraftModel`). Both were deployed and measured on the same FP8 target
(`scripts/bench-conversation.py`, 10 real prompts, `max_tokens=512`, greedy,
salted, `--burst 4`):
| Arm | Mean accept | Decode (geomean) | TTFT (mean) | Prefill (geomean) | Burst-4 aggregate | KV pool |
|---|---|---|---|---|---|---|
| **DFlash2** (depth 7) | 4.24/7 | **100.0 t/s** | 0.19 s | 923.0 t/s | **287.1 t/s** | 384,238 (1.47×) |
| MTP self-spec (depth 8) | 5.66/8 | 85.9 t/s | 0.06 s | 1096.9 t/s | 179.7 t/s | 461,540 (1.76×) |
**DFlash2 wins on decode (+16 %) and burst-4 (+60 %); MTP wins on TTFT and
prefill.** The counterintuitive part: DFlash2 accepts *fewer* tokens per step
(4.24 vs 5.66) yet decodes *faster*. Accept length is a proxy, not the metric
— throughput is `(accept + 1) / (draft time + verify time)`. The DFlash2
drafter is a small separate model that steps fast; the MTP head is the
target's own layer applied autoregressively 8×, so its draft is slower. The
faster step more than makes up the lower accept length. FP8 + DFlash2 (100.0
t/s) also beats the original MXFP4 + DFlash2 (92.4 t/s) — the higher-precision
FP8 target verifies more of the drafter's proposals. The drafter costs 2.1 GiB
of VRAM (the KV pool is 1.47× vs MTP's 1.76×), an acceptable trade for the
decode win. **DFlash2 is the deployed shape.**
## Prefix caching — measured granularity (2026-09-20)
`--enable-prefix-caching` is on, and radiance says so at boot: *"Prefix caching