Files
serverAppConfigs/serverAppConfigs-docs/LLM/qwen3.8-spec-matrix.md
T
Frank 5829a0bfe8 docs: rename docs/ -> serverAppConfigs-docs/ per system-wide PROJECTNAME-docs convention
All in-repo path references (AGENTS.md, README, old scripts, report artifacts,
test code paths) rewritten in the same commit; focused-test map extended for
.gitignore, old/scripts/*, dankclaw/comfyui/*, and report/* per this repo's
own convention ('add one before committing'). Obsidian wikilinks are
title-based and unaffected.
2026-08-28 02:20:06 -07:00

7.5 KiB

SUPERSEDED 2026-08-17. The Frank redo retired this topology. See frank-redo-2026-08-17.md and deployment.md for the active configuration.

Qwen3.8-27B Speculative-Decoding Matrix

This page records the speculative-decoding benchmark history and the production tuning for the Qwen3.8-27B service (frank-default-large), now a pipeline-parallel head with a Dankserver worker.

Production state (2026-08-17: Q6_K, q8_0/f16 KV, split 0.68)

Current production, verified against the live process and journal (2026-08-17, load 02:06 + smoke tests). Topology detail: Pipeline Parallelism/Qwen3.8 PP Deployment.

  • Devices: RPC0 (Dankserver RTX 3090) + ROCm0 (Strix iGPU). The head passes no --device flag. Default selection puts RPC first and picks ROCm for the iGPU (HIP registers before Vulkan). The Vulkan0 side stays an option via HEAD_BIN=.../build-vulkan-rpc.
  • Model: Qwen3.8-27B-Uncensored-Q6_K.gguf (JonathanColetti, 22.43 GB, imatrix, 65 blocks, 866 tensors, NO bundled MTP head). The repo's standalone draft sidecar Qwen3.8-27B-Uncensored-draft-Q8_0.gguf (3.16 GB) is loaded via --spec-draft-model; it runs on ROCm0 only (1718 MiB, zero worker impact).
  • Spec: MTP (standalone draft) --spec-draft-n-max 4 + the --spec-default ngram-mod fallback (48/64/24). Measured acceptance 0.83 then 0.75, mean len 4.0-4.3, per-position ~0.86/0.71/0.71/0.71 — stronger than the old bundled head (0.739/0.554/0.427/0.340).
  • KV: K q8_0 + V f16 (mismatched). 3136 B/token per FA layer = 784 MiB at 262144 (K 1088 + V 2048). RPC0 8624 MiB (11 FA), ROCm0 3920 MiB (5 FA); total 12544 = 16 x 784. Flash Attention stays enabled with the mismatch (resolve_fused_ops: Flash Attention enabled) via the GGML_CUDA_FA_ALL_QUANTS rebuild of both binaries at 9b05354ec — see the PP doc.
  • Split: --tensor-split 0.68,0.32 = 45 of 65 blocks (0-44) on the 3090, 69% of the layer weight. The maximum: 46 blocks -> ~0.3 GiB margin; 48 blocks (f >= 0.7197) adds FA block 47 -> KV 9408 -> OOM by ~0.85 GiB. Boundary math: the PP doc.
  • Context: pool 262144, 1 slot, n_ctx_slot = 262144. The pi models.json declares 262144 (contextWindow = maxTokens, house convention).
  • Worker steady state: 23854/24576 MiB used on the 3090, ~273 MiB free (tight but stable; all allocations happen at load).
  • Batch 8192 / ubatch 256, --direct-io, --fit off, --gpu-layers all.

How MTP works on Qwen3.8 (bundled-head era)

The unsloth UD-* GGUFs keep the MTP (Multi-Token Prediction) head inside the model file. llama.cpp b10411+ auto-detects it and creates the draft context from the target model. Do not pass --spec-draft-model for those files. This is superseded: the production Qwen3.8-27B-Uncensored-Q6_K.gguf has NO bundled head, and the serve script passes the standalone draft sidecar with --spec-draft-model (see Production state above).

The stock llama.cpp build must be b10411 or newer. The 2026-08-14 rebuild on Dankclaw is b10433 (9b05354ec) with GGML_HIP + gfx1151 + Vulkan.

History (superseded configurations, 2026-08-14 to 08-16)

All superseded configs used Qwen3.8-27B-UD-Q8_K_XL.gguf (29.29 GiB, bundled MTP) before the Q6_K swap. The ngram-mod preset survives as the fallback layer under MTP. The Vulkan0 verdict from this era applies to the standalone configuration only; the PP head runs ROCm0.

  • 2026-08-14: bundled MTP — draft-mtp, n_max 5, parallel 2, ctx 524288 (2 x 262144). ~13.3 t/s single, ~8 t/s per slot under 2 concurrent streams. No premature stop.
  • 2026-08-15: DSpark sidecar — magnitudedev/Qwen3.8-27B-DSpark-GGUF (Q8_0, 1.36B, block_size 7), n_max 7, PARALLEL=1. Two concurrent 16k prose prompts crashed the ROCm backend with ROCm error: an illegal memory access in common_speculative_impl_draft_dflash::draft (status 6/ABRT, systemd restart, ~503 during reload). DSpark reuses the DFlash block-diffusion machinery, which shares one draft context across parallel slots; concurrent block drafting faults in HIP stream sync. This is the same multi-slot bug class that retired draft-dflash (premature EOS). The crash shows on 16k concurrent streams, not on a 67k pair — concurrency is the trigger, not size. Fix: PARALLEL=1 (requests queue safely). Retired the same day.
  • 2026-08-15 to 08-16: --spec-default (ngram-mod), Vulkan0 — n_match 24 / n_min 48 / n_max 64, draftless, PARALLEL=2 (2 x 32k parallel stream test passes). Chosen for parallel safety after DSpark; kept as the fallback under MTP in the current config.

Benchmark Method

The matrix ran 54 configurations: 3 spec types (draft-mtp,ngram-mod, draft-mtp, ngram-mod) x --spec-draft-n-max 2..10 x --spec-draft-p-min {off, 0.8}. Each config loaded llama-server, then ran 3 generations. The tool is scripts/spec-matrix-bench.sh; analysis is scripts/analyze-spec-matrix.py.

Matrix Caveat: Benchmark Prompt

The matrix used a repetitive 1100-token prompt (scripts/bench_live.sh style). MTP drafts that repeated sentence almost perfectly, which inflated the numbers for long drafts. The matrix reported draft-mtp n_max=10 at ~30 t/s. A realistic story prompt gave very different results:

n_max median t/s (realistic)
4 12.5
5 13.3 (peak)
6 10.6
7 10.1
10 8.6
none (baseline) 7.2

Long drafts collapse to ~18% acceptance on real writing. n_max=5 balances acceptance against draft cost.

Premature Stop Investigation

Two failure patterns appeared during early production tests:

  1. Speed collapse. ubatch >= 512 reduced MTP acceptance to ~25% and generation to ~9 t/s. ubatch 128 with batch >= 2048 triggered a premature instant-EOS under 2 concurrent slots. ubatch 256 avoids both: ~30 t/s single, concurrent-safe.
  2. Apparent premature-EOS. The repetitive benchmark prompt with temp 1.0 sometimes stops after 1-2 tokens because the model legitimately emits an EOS on a wall of repeated text. This is sampling variance, not a corruption bug. Realistic prompts never stop early.

High-Context Backend Comparison (2026-08-15)

Tool: scripts/bench-high-ctx.sh <device> <ctx>. Both runs at ctx 262144 with a ~200k-token realistic-prose prompt, --spec-default, batch 8192 / ubatch 256, single slot, on scratch port 7892.

Device Prompt tokens Prefill (t/s) Generation (t/s) Wall (single req)
ROCm0 202822 119.1 3.83 1770 s
Vulkan0 200235 106.3 5.68 1929 s

Verdict: at very high context, Vulkan0 decodes 48% faster (5.68 vs 3.83 t/s); ROCm0 prefills 12% faster. Generation is the interactive cost (users stream tokens). This verdict applies to the standalone configuration; the PP head runs ROCm0 (HIP registers before Vulkan). Both tg_3s values were rock-steady across the run (3.83 and 5.67 t/s). High-context decode collapses on both backends versus ~25 t/s at 16k ctx; that is the long-context attention penalty, not a spec artifact.

Production Configuration

The live tunables live in scripts/frank-default-large.env (head) and scripts/frank-pp-worker.env (worker) in this repo. The Production state section at the top of this page records the current values: Q6_K model + standalone MTP draft, split 0.68/0.32, 262144 ctx, PARALLEL=1, q8_0/f16 KV.

See Also