Real, on-hardware performance of the
qwen38-*recipe family on a single DGX Spark (dgx1, GB10 sm_121, 128 GB unified memory), measured 2026-08-22. Update this table as you re-benchmark newer images / configs. Each row is a like-for-like measurement unless noted.Benchmark:
tool-eval-bench bench --perf-only --pp 2048 --tg 128 @d0(free-gen, single stream, concurrency 1) unless noted. SGLang rows used a direct SSE stream timer (tool-eval-bench's bench/run cannot parse SGLang's SSE response shape — see "Measurement caveats").Clock: all rows since the dgx1 thermal shutdown are measured under the qwen38
before_serveclock lock (nvidia-smi -lgc 0,2000— 2000 MHz cap vs 3003 MHz max). The earlier vLLM rows (22.8) were at full boost; the lock costs ~5% (22.8 → 21.7).
| Config | Image | decode | prefill | Notes |
|---|---|---|---|---|
| unsloth NVFP4 + MTP (k=5) | vllm-gb10 v0.27.1-gb10.2 |
21.7 | 2,177 | WINNER (21.7 @ lock; 22.8 @ boost) |
| unsloth NVFP4 + DSpark (k=7) | vllm-gb10 v0.27.1-gb10.2 |
22.8 | 2,563 | ties decode, better prefill; needs patched draft |
| RedHatAI NVFP4 + MTP (k=5) | vllm-gb10 v0.27.1-gb10.2 |
19.2 | 2,153 | slower than unsloth NVFP4 |
| Int4 AutoRound + MTP (k=4) | vllm-gb10 v0.27.1-gb10.2 |
19.6 | 921 | mixed-4bit, slower prefill |
| FP8 + DSpark (k=7) | vllm-gb10 v0.27.1-gb10.2 |
17.7 | 1,133 | uses patched draft |
| FP8 + MTP (k=5) | vllm-gb10 v0.27.1-gb10.2 |
14.0 | 701 | surprisingly slow |
| BF16 + MTP (k=5) | vllm-gb10 v0.27.1-gb10.2 |
10.1 | 1,194 | quality baseline |
| SGLang RadixArk NVFP4 + DSpark | lmsysorg/sglang:qwen38-27b |
9.30 | — | stock image (pre-DSpark... pre-DFlash2 tag) |
| SGLang RadixArk NVFP4 + DFlash2 | ...-dflash2-minoverlay |
9.22 | — | DFlash2 fork (c14312a66) |
run --short)| Config | score | median turn |
|---|---|---|
| unsloth NVFP4 + MTP | 100/100 (28/28) | 4.7 s |
| unsloth NVFP4 + DSpark | 100/100 (30/30) | 5.2 s |
qwen38-27b-nvfp4-unsloth — vLLM+MTP on the unsloth NVFP4 checkpoint. Best
decode (21.7 tok/s under the lock), 100/100 agentic quality, and the MTP head is
in-checkpoint (no extra draft to download). This is what pi is pointed at via
~/.pi/agent/models.json → provider dgx1 → model qwen3.8-27b.
ghcr.io/timothystewart6/vllm-gb10:v0.27.1-gb10.2 (latest immutable,
vLLM 0.27.1, torch 2.13+cu130, FlashInfer from source). Full qwen3_5 + MTP
support. Used over eugr images (unpinned nightly) per the repo rule.ndec.py
cached-code-context artifact, not per-request generation throughput.unsloth NVFP4 beat RedHatAI NVFP4 (21.7 vs 19.2 decode). Both are
compressed-tensors NVFP4. RadixArk (modelopt) NVFP4 was only needed for
SGLang's quantized-head path (see below).num_speculative_tokens: 5 for the MTP recipes (helge/forum peak; the
single MTP layer cycles), 4 for Int4 (SlopOps). DSpark uses block 7 = 8 draft
tokens (requires max-num-batched-tokens ≥ 16384 to avoid the
max_num_scheduled_tokens warning).content +
reasoning_content deltas, divide time by token count). tool-eval-bench
bench/run cannot parse SGLang's SSE stream (llama-benchy "no usable
throughput metrics"; run times out per-turn). vLLM rows used the standard
bench --perf-only.bench/ndec.py (2-call net-decode A/B on
a large cached code prefix) — measures marginal edit cost, not per-request
generation. Not comparable to the table above.thinking/reasoning_content is counted as token generation; the winner's
21.7 tok/s is measured with thinking on (default) counting both content
and reasoning deltas.| Recipe | Target | spec-decode | Status |
|---|---|---|---|
qwen38-27b-nvfp4-unsloth |
unsloth NVFP4 | MTP (k=5) | winner — serving on dgx1 |
qwen38-27b-nvfp4 |
RedHatAI NVFP4 | MTP (k=5) | measured |
qwen38-27b-nvfp4-unsloth-dspark |
unsloth NVFP4 | DSpark (k=7) | experimental |
qwen38-27b-fp8-dspark |
FP8 | DSpark (k=7) | experimental |
qwen38-27b-fp8 |
FP8 | MTP (k=5) | measured |
qwen38-27b-int4 |
Pilcothink Int4 | MTP (k=4) | measured |
qwen38-27b |
Qwen BF16 | MTP (k=5) | measured baseline |
qwen38-27b-nvfp4-sglang-dspark |
RadixArk NVFP4 | SGLang DSpark | experimental (slow) |
qwen38-27b-nvfp4-sglang-dflash2 |
RadixArk NVFP4 | SGLang DFlash2 | experimental (slow) |
The vLLM method:"dspark" paths (qwen38-27b-fp8-dspark,
qwen38-27b-nvfp4-unsloth-dspark) load a dedicated draft model that must live
at a local dir with its config.json arch patched:
Qwen3.8-27B-DSpark declares architectures: ["DSparkDraftModel"],
which vLLM maps to the DeepSeek-V4 DSpark code path (hc_mult attribute
error). It must be re-labelled Qwen3DSparkModel + draft_vocab_size set to
route to qwen3_dspark./home/kilork/dspark-draft, bind-mounted
into the container at /dspark-draft via the recipe's [[extra_mounts]].lmsysorg/sglang:qwen38-27b (cookbook) predates DFlash2 (upstream merged
c14312a66 on 2026-08-19). DFlash2 is overlaid by hand:
lmsysorg/sglang:qwen38-27b-dflash2-minoverlay (5 sha256-verified modules from
the MiaAI repo, built on dgx1). That minoverlay already bakes in the
quantized-head selector fix, so it serves the modelopt NVFP4 target. SGLang only
accelerates modelopt NVFP4 — unsloth/RedHatAI (compressed-tensors) fall
back to a slow unquantized path.
qwen38 single-node cluster (dgx1) — the before_serve
clock lock is applied automatically.tool-eval-bench bench --base-url http://dgx1.lan:30000/v1 --model qwen3.8-27b --perf-only --pp 2048 --tg 128 --concurrency 1.Measurement caveats).