|
|
@@ -0,0 +1,124 @@
|
|
|
+# Qwen3.8-27B recipes — measured state
|
|
|
+
|
|
|
+> 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_serve` clock 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).
|
|
|
+
|
|
|
+## Engine / spec-decode matrix (decode tok/s, prefill tok/s)
|
|
|
+
|
|
|
+| 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) |
|
|
|
+
|
|
|
+### Agentic / edit-heavy quality (tool-eval-bench `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 |
|
|
|
+
|
|
|
+## Winner
|
|
|
+
|
|
|
+**`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`.
|
|
|
+
|
|
|
+## Key decisions & why
|
|
|
+
|
|
|
+- **Image:** `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.
|
|
|
+- **MTP over vLLM-DSpark for the winner:** both tie on decode (22.8), MTP is
|
|
|
+ simpler (no draft download/patch), and MTP has the slightly better agentic
|
|
|
+ turn-around (4.7s vs 5.2s). The vLLM DSpark recipes are kept as experimentals.
|
|
|
+- **SGLang (DSpark and DFlash2) is NOT faster here.** Despite the forum's
|
|
|
+ 52-87 tok/s claims, both measured ~9 tok/s for fresh prompts — 2.3× slower
|
|
|
+ than vLLM+MTP under the same clock lock. The forum numbers are an `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).
|
|
|
+
|
|
|
+## Measurement caveats
|
|
|
+
|
|
|
+- **SGLang rows** used a hand-rolled SSE stream timer (count `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`.
|
|
|
+- The forum's high SGLang numbers use `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.
|
|
|
+
|
|
|
+## Recipes in this family
|
|
|
+
|
|
|
+| 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) |
|
|
|
+
|
|
|
+### vLLM DSpark draft note
|
|
|
+
|
|
|
+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:
|
|
|
+
|
|
|
+- RadixArk `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`.
|
|
|
+- The patched copy lives on dgx1 at `/home/kilork/dspark-draft`, bind-mounted
|
|
|
+ into the container at `/dspark-draft` via the recipe's `[[extra_mounts]]`.
|
|
|
+- This is fragile; if you only run the winner, drop these two recipes.
|
|
|
+
|
|
|
+### SGLang image note
|
|
|
+
|
|
|
+`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.
|
|
|
+
|
|
|
+## Re-benchmarking
|
|
|
+
|
|
|
+1. Serve a recipe on the `qwen38` single-node cluster (dgx1) — the `before_serve`
|
|
|
+ clock lock is applied automatically.
|
|
|
+2. For vLLM: `tool-eval-bench bench --base-url http://dgx1.lan:30000/v1 --model qwen3.8-27b --perf-only --pp 2048 --tg 128 --concurrency 1`.
|
|
|
+3. For SGLang: use a direct SSE stream timer (see `Measurement caveats`).
|
|
|
+4. Record the new numbers in the table above; note the image tag + date.
|