Qwen 3.8 27B — Serving Configuration Report (Strix Halo, gfx1151)
Verdict. The fastest, most reliable serving configuration for Qwen 3.8 27B on this machine is:
llama.cpp
llama-server+Qwen3.8-27B-Q4_K_M.gguf+ native MTP speculative decoding,--spec-draft-n-max 4
Measured on the mandatory captured real-world replay (PLAN §2.2): 24.0 t/s mean / 20.1 t/s median generation (re-run: 24.67 t/s), accept_len 3.36, 12/12 turns, 0 errors, ~14 s cold start. No other engine or method comes close: the best vLLM cell is ~10× slower (2.4 t/s), and the best non-MTP llama.cpp method (DSpark) is ~2× slower (12.2 t/s).
This is a clear and reproducible winner, confirmed by running the winner cell twice
(L1.4 = 24.0 t/s, L1.4-rerun = 24.67 t/s) and by the fact that the next-best complete cell (L6.5,
--prio 3) is within noise of it (23.87 t/s) while every distinct configuration change is measurably
worse.
1. Exact winning configuration (reproducible)
# lab/serve_best.sh
/home/ericflo/Development/llama.cpp/build/bin/llama-server \
-m /home/ericflo/.lmstudio/models/lmstudio-community/Qwen3.8-27B-GGUF/Qwen3.8-27B-Q4_K_M.gguf \
--mmproj /home/ericflo/.lmstudio/models/lmstudio-community/Qwen3.8-27B-GGUF/mmproj-Qwen3.8-27B-BF16.gguf \
--alias qwen/qwen3.8-27b \
--host 127.0.0.1 --port 1234 \
--jinja -fa on -ngl 999 -np 1 -c 262144 \
--reasoning-preserve \
--chat-template-kwargs '{"preserve_thinking": true}' \
--spec-type draft-mtp --spec-draft-n-max 4 \
--verbose
| Setting | Value | Why |
|---|---|---|
| Engine | llama.cpp (local master build) | Fastest single-stream decode on gfx1151; ~10× faster than vLLM |
| Target | Qwen3.8-27B-Q4_K_M.gguf (lmstudio-community) |
Fastest quant rung that still serves well (Q4_K_M > Q5_K_M > Q8_0) |
| Speculative | MTP n=4 (--spec-type draft-mtp --spec-draft-n-max 4) |
Native head; knee of the n-curve; ~2.1× over no-spec |
| Thinking | ON (preserve_thinking, reasoning_effort xhigh) |
Forced by the frozen replay; drafter trained think-off but MTP is native |
| Context | -c 262144 |
Replay max prompt is 88,896 tok; headroom for agentic work |
| Flash attention | -fa on |
Essential — -fa off drops to 14.46 t/s (L6.1) |
| Parallel | -np 1 |
Single-stream scope (PLAN §2.1 excludes concurrency) |
| Precision ceiling | Q4_K_M | Q8-class is the user-set ceiling; Q4_K_M is fastest and highest quality at n=4 |
Replay benchmark (the ONLY authorized workload, PLAN §2.2):
./run_benchmark.sh local captures-desktop \
--chunks 3 --chunk 4 \
--conversation 199ae534158f31e2 \
--request-timeout 600
Capture: captures-desktop/capture-2026-08-15.jsonl, sha256 cba6a988487e73f4ccac374126e7473f0c4f3aafdae512d224d63f12baa43216
(derived per-request manifest sha 4d3900af5383df75b45c6beb8a31c51f71656c47c25d36f4eed66fe231ab0cda,
identical across every cell — the workload is frozen and consistent).
2. Captured-replay leaderboard (all 24 completed cells)
Source: lab/results/LEADERBOARD.md (auto-generated from per-cell JSON). Workload = the §2.2 replay,
12 turns, single conversation, thinking ON. Ranked by generation t/s (mean).
| rank | cell | target | spec | n | gen t/s (mean/med/p95) | TTFT med (s) | accept_len | notes |
|---|---|---|---|---|---|---|---|---|
| 1 | L1.4-rerun | Q4_K_M | draft-mtp | 4 | 24.67 / 21.67 / 40.86 | 25.3 | 3.44 | winner (re-run) |
| 2 | L1.4 | Q4_K_M | draft-mtp | 4 | 24.00 / 20.11 / 47.10 | 25.9 | 3.36 | winner (orig) |
| 3 | L6.5 | Q4_K_M | draft-mtp | 4 | 23.87 / 21.98 / 39.45 | 25.6 | 3.37 | --prio 3 (≈ default) |
| 4 | L1.3 | Q4_K_M | draft-mtp | 3 | 23.33 / 21.13 / 35.12 | 26.3 | 3.04 | n=3 |
| 5 | L6.6 | Q4_K_M | draft-mtp | 4 | 23.25 / 22.10 / 42.95 | 25.8 | 3.27 | -c 131072 |
| 6 | L6.3 | Q4_K_M | draft-mtp | 4 | 23.08 / 22.01 / 38.34 | 25.9 | 3.35 | -b 256 -ub 64 |
| 7 | L0 | Q4_K_M | draft-mtp | 2 | 22.72 / 21.30 / 33.23 | 26.7 | 2.52 | n=2 (pre-power-change) |
| 8 | L0-rerun | Q4_K_M | draft-mtp | 2 | 22.16 / 19.70 / 30.56 | 26.0 | 2.53 | n=2 (fair baseline) |
| 9 | L6.7 | Q4_K_M | draft-mtp | 4 | 22.13 / 23.09 / 37.18 | 25.9 | 3.26 | --ctx-checkpoints 8 |
| 10 | L1.5 | Q4_K_M | draft-mtp | 5 | 21.90 / 19.99 / 40.90 | 26.5 | 3.78 | n=5 (past knee) |
| 11 | L6.2 | Q4_K_M | draft-mtp | 4 | 21.02 / 19.28 / 43.12 | 20.7 | 3.25 | -ctk q8_0 -ctv q8_0 |
| 12 | L2.1 | Q5_K_M | draft-mtp | 4 | 20.92 / 18.59 / 30.83 | 25.4 | 3.29 | higher quant rung |
| 13 | L1.6 | Q4_K_M | draft-mtp | 6 | 20.60 / 17.30 / 36.25 | 26.8 | 3.95 | n=6 (past knee) |
| 14 | L1.1 | Q4_K_M | draft-mtp | 1 | 17.91 / 16.55 / 23.14 | 26.2 | 1.82 | n=1 |
| 15 | L2.2 | Q8_0 | draft-mtp | 4 | 17.86 / 18.21 / 28.62 | 26.3 | 3.38 | precision ceiling |
| 16 | L6.1 | Q4_K_M | draft-mtp | 4 | 14.46 / 9.44 / 35.73 | 22.1 | 3.54 | -fa off (bad) |
| 17 | L4.4 | Q4_K_M | draft-dspark | 5 | 12.23 / 10.74 / 17.35 | 36.6 | 2.29 | best DSpark (RadixArk 3.8) |
| 18 | L2.5 | Q4_K_M | none | — | 11.57 / 10.89 / 14.46 | 21.9 | — | no-spec reference |
| 19 | L5.1 | Q4_K_M | ngram-simple | 4 | 11.53 / 10.90 / 14.14 | 22.1 | — | ngram |
| 20 | L5.2 | Q4_K_M | ngram-simple | 8 | 11.50 / 10.88 / 14.43 | 22.1 | — | ngram |
| 21 | L4.3 | Q4_K_M | draft-dspark | 7 | 9.50 / 9.11 / 13.58 | 36.6 | 2.37 | DSpark (RadixArk 3.8) |
| 22 | L2.4 | Q8_0 | none | — | 7.51 / 7.19 / 8.84 | 23.2 | — | Q8 no-spec reference |
| 23 | L3.1 | Q4_K_M | draft-dflash | 4 | 6.88 / 6.22 / 9.53 | 24.4 | 1.01 | DFlash cross-gen = dead |
| 24 | L4.2 | Q4_K_M | draft-dspark | 10 | 4.55 / 3.31 / 11.16 | 37.8 | 1.44 | DSpark (satgeze 3.6) |
| 25 | L4.1 | Q4_K_M | draft-dspark | 15 | 4.32 / 3.13 / 11.38 | 39.5 | 1.50 | DSpark (satgeze 3.6) |
vLLM (excluded from ranking — incomplete lane):
| cell | target | spec | gen t/s (mean/peak) | turns | notes |
|---|---|---|---|---|---|
| V2.1 | qwen3.8-27b-fp8 | mtp n=4 | 2.40 / 5.70 | 9/12 | best vLLM; 3 idle-timeouts; ~10× slower than llama.cpp |
3. What the data says
3.1 MTP draft-depth knee (Q4_K_M) — the core finding
| n | gen t/s | accept_len | Δ vs n=4 |
|---|---|---|---|
| 1 | 17.91 | 1.82 | −25% |
| 2 | 22.16 | 2.53 | −8% |
| 3 | 23.33 | 3.04 | −3% |
| 4 | 24.00–24.67 | 3.36 | baseline |
| 5 | 21.90 | 3.78 | −9% |
| 6 | 20.60 | 3.95 | −14% |
Acceptance length keeps rising past n=4 (3.36 → 3.95) but throughput falls (24.0 → 20.6). This is the classic speculative knee: the draft cost and verification overhead of extra tokens outweighs the marginal extra acceptance. n=4 is the optimum.
3.2 Quantization ladder (MTP n=4)
| quant | gen t/s | no-spec t/s | MTP speedup |
|---|---|---|---|
| Q4_K_M | 24.00 | 11.57 | 2.07× |
| Q5_K_M | 20.92 | — | — |
| Q8_0 | 17.86 | 7.51 | 2.38× |
Lower precision = faster (bandwidth-bound), and MTP's relative speedup is actually larger on the heavier Q8_0 target (2.38× vs 2.07×) because the no-spec baseline is slower. Q4_K_M dominates on throughput and is the right default; Q8_0 is the precision-ceiling option if quality is prioritized over speed (user's Q8-class ceiling).
3.3 Speculative-method comparison (Q4_K_M)
| method | best gen t/s | best accept_len | verdict |
|---|---|---|---|
| MTP (native) | 24.00 (n=4) | 3.36 | WINNER |
| DSpark (RadixArk 3.8 head) | 12.23 (n=5) | 2.29 | ~2× slower |
| ngram-simple | 11.53 | — | ~2× slower |
| DFlash (z-lab 3.6 cross-gen) | 6.88 | 1.01 (0.12% per-tok) | dead — cross-gen feature mismatch |
| no-spec | 11.57 | — | reference |
MTP is unambiguous. It is the target's own next-token head (no cross-model feature transfer), so
it is the only method with high, stable acceptance. DSpark's Markov head survives cross-gen
(accept_len ~1.5–2.4) but is still ~2× slower than MTP. DFlash is a diffusion drafter whose fc
projection is trained on the Qwen3.6 target's hidden-state distribution — against the 3.8 target the
features are out-of-distribution → 0.12% per-token acceptance (L3.1), effectively zero. DFlash
needs a same-generation head (none exists for 3.8 yet).
3.4 Runtime-knob sweep on the winner (Q4_K_M, MTP n=4)
| knob | gen t/s | Δ vs default (24.0) |
|---|---|---|
default (-fa on, f16 KV, default batch, mmap, ctx 262144) |
24.00 | — |
--prio 3 (L6.5) |
23.87 | ≈ within noise |
-c 131072 (L6.6) |
23.25 | −3% |
-b 256 -ub 64 (L6.3) |
23.08 | −4% |
-ctk q8_0 -ctv q8_0 (L6.2) |
21.02 | −12% |
--ctx-checkpoints 8 (L6.7) |
22.13 | −8% |
-fa off (L6.1) |
14.46 | −40% — flash attention is essential |
The default configuration is already near-optimal. The single important flag is -fa on
(disabling it costs 40%). KV quantization, batch shape, priority, context length, and checkpoint
count all trend downward or are neutral — no runtime knob improves on the default.
4. Cross-engine due diligence
4.1 vLLM (kyuz0/vllm-therock-gfx1151, v0.27.1+strix, ROCm 7.14)
- V1.x (GGUF lane) — NOT EXECUTABLE. vLLM's GGUF load path OOM-killed the host (ExitCode=137, OOMKilled=true). The host has only 30 GiB system RAM; vLLM's GGUF loading (weights + page cache) peaks above that, while the identical 28.75 GB FP8 safetensors path streams shards to the GPU with ~6 GB host RAM. This permanently blocks the GGUF lane on this machine.
- V2.1 (FP8 + MTP n=4) — 2.4 t/s mean / 5.7 t/s peak, 9/12 turns. This is the best vLLM config. It is ~10× slower than llama.cpp. The gap is structural: vLLM's FP8 W8A8 decode on gfx1151 runs at ~2–5 t/s, and the fastest single vLLM turn (5.7 t/s) is still 2.7× slower than the slowest llama.cpp turn (15.6 t/s). No speculative method (MTP/DFlash/DSpark) can close a 4× decode gap.
- V2.2 (AWQ-INT4) — not executed. No AWQ-INT4 build/image for gfx1151 (kyuz0 image ships FP8+GGUF only).
- V3.x (DFlash) / V4.x (DSpark) — deprioritized (documented). The vLLM decode ceiling (~5.7 t/s) is already 4× below llama.cpp's minimum; speculative acceptance cannot lift it to 24 t/s. DFlash cross-gen is independently dead (L3.1). Recorded with evidence, not hidden.
4.2 SGLang (Phase 3)
- NOT EXECUTABLE. No public prebuilt SGLang image for gfx1151 exists
(
ghcr.io/JeremiahM37/strix-halo-sglangis not pullable). The only path is a local build from/home/ericflo/Development/strix-halo-sglang, deferred per the one-GPU-workload-at-a-time rule and because the SGLang README itself notes single-stream decode is weaker than llama.cpp on this stack. RadixArk's DSpark is verified on SGLang in principle, but even its best SGLang acceptance (3.4–4.6) cannot overcome a slower base decode lane. Recorded, not measured.
5. Reproducibility
The winner cell was run twice on the same frozen capture (identical manifest sha 4d3900af…):
| run | gen t/s mean | median | p95 | accept_len | turns | errors |
|---|---|---|---|---|---|---|
| L1.4 (orig) | 24.00 | 20.11 | 47.10 | 3.36 | 12/12 | 0 |
| L1.4-rerun | 24.67 | 21.67 | 40.86 | 3.44 | 12/12 | 0 |
Δ = +2.8% (run-to-run variance). The winner is stable and reproducible.
6. Recorded non-executable cells (full audit)
These PLAN cells have no replay result by necessity, each with a documented reason
(see results/<engine>/<cell>.json):
| cell | reason |
|---|---|
| L1.7, L3.7, L4.3-thinkoff, L4.7, V3.5, V4.4 | Think-off — the frozen replay's request bodies force enable_thinking:true, which overrides server-side kwargs (llama.cpp server-common.cpp:1279). Cannot run think-off on this capture. |
| L3.2–L3.6, L3.8, L3.9 | DFlash — abandoned after L3.1 (cross-gen 0.12% acceptance); same drafter/target, deeper N identically dead. |
| L2.3 | BF16 — dropped by user decision (Q8-class is the precision ceiling). |
| V1.1, V1.2, V1.4 | vLLM GGUF — host OOM on weight load (30 GiB RAM < GGUF load path). |
| V2.2 | vLLM AWQ-INT4 — no AWQ build/image for gfx1151. |
| V3.1, V3.5 | vLLM DFlash — cross-gen dead (L3.1) + lane 10× below llama.cpp. |
| V4.1, V4.2, V4.4 | vLLM DSpark — lane ceiling 4× below llama.cpp minimum; documented deprioritization. |
| S0, S1.1, S2.1, S3.1 | SGLang — no prebuilt image; local build deferred. |
7. Caveats & limitations
- Power mode. The system is in a reduced (stable) power mode since the 2026-08-15 crash (~11% slower than pre-crash). This affects all cells equally (relative ranking is unaffected), but absolute t/s numbers are ~11% below a full-power machine.
- TTFT dominates wall time. ~92% of replay wall time is TTFT/prefill (mean TTFT ~99–102 s, driven by long agentic prompts up to 88.9 k tok). The generation t/s (the decode metric, where the winner shines) is what this report ranks. If your workload is prefill-dominated, the relative advantage of speculative decoding shrinks (prefill is not spec-decoded).
- Single-stream scope. PLAN §2.1 excludes concurrency/soak. These numbers are for
-np 1single-conversation serving (the agentic-coding daily-driver use case), not for multi-user throughput. - Think-on only. All cells run thinking ON (forced by the replay). Think-off acceptance (often higher) is unmeasurable on this frozen capture.
- Drafters are third-party. The DSpark/DFlash heads are community models (RadixArk, satgeze, z-lab); MTP (the winner) is the target's own head and needs no external drafter.
8. Deliverables
| artifact | location |
|---|---|
| Daily-driver boot script | lab/serve_best.sh |
| Per-cell results (24 completed + 23 recorded) | lab/results/{llama,vllm,sglang}/*.json |
| Archived replay artifacts per cell | lab/results/llama/replay/<cell>/ |
| Leaderboard | lab/results/LEADERBOARD.md |
| This report | lab/results/REPORT.md |
| Bench harness | lab/bench/ (bench_client.py, leaderboard.py, suites.py) |
| Cell runner | lab/scripts/run_cell.sh, run_batch.sh, run_vllm_cell.sh |
Generated 2026-08-16. All measurements from the mandatory captured replay (PLAN §2.2), single stream, thinking ON, Q4_K_M→Q8_0 quantization ladder, on Strix Halo (Ryzen AI Max+ 395, Radeon 8060S, gfx1151, 96 GiB).