Kept / trial active in llama-swap.yaml · aliases: qwen3.8-27b-awq-mtp
| Run | Score | tok/step | R5 | Log |
|---|---|---|---|---|
| No results-*.log found for this id/alias. | ||||
Trialled 2026-08-24. Same Qwen3.8-27B AWQ INT4 body as [[qwen38-awq-vllm-trial]]
plus a real multi-token-prediction draft head. Served as qwen38-mtp on :9165.
| | qwen38-awq | qwen38-mtp | |
|---|---|---|---|
| decode, 2000-tok generation | 28.2 tok/s | 41.7 tok/s | 1.48x (n=1, optimistic) |
| decode, 3-run mean @192k | — | 39.9 tok/s | spread 39.4-40.7 |
| decode, 45 battery tasks | — | 38.1 tok/s mean / 39.5 median | ~1.35x — USE THIS |
| prefill, ~30k prompt | 34.9 s | 36.4 s | 0.96x (4% slower) |
| size | ~16 GB | 19 GB | |
| cold load | ~3.7 min | ~4.7 min | |
The 33-38% prefill regression from [[qwen38-atomic-install]] did NOT reproduce.
That was the main reason to be wary of MTP on this family; here the prefill cost is
~4%, comfortably paid for by a 48% decode gain. Net win for interactive/agentic use,
where decode dominates.
config.json has no num_nextn_predict_layers / mtp_num_hidden_layers at all,
so metadata alone suggests "MTP" is just a repo-name claim. It is not: the weight
index carries 15 real mtp.* tensors (mtp.fc, a full single-layer block with
attention + MLP + both layernorms, mtp.norm, 2 pre-fc norms). Probe the WEIGHT
INDEX, not the config, to tell a real MTP build from a stub ([[agentworld-vision-stub]]
is the stub case). vLLM copes with the missing field:
getattr(config, "mtp_num_hidden_layers", 1) = 1, matching the single mtp.layers.0.
--speculative-config '{"method":"qwen3_5_mtp","num_speculative_tokens":1}'. qwen3_5_mtp is a REGISTERED method name in vllm/config/speculative.py; a wrong
string fails the load outright, so a successful load is itself evidence MTP wired up.
--mamba-cache-mode align IS LOAD-BEARING — Qwen3_5MTP.__init__ raises NotImplementedError on mamba_cache_mode == "all" (qwen3_5_mtp.py:218).
mtp.fc unquantized onpurpose. Mixed precision here is expected, not a packaging error.
--max-model-len 196608 — the SAME window as qwen38-awq. It was set to 131072first on the assumption that the BF16 draft head would starve the KV pool; that
assumption was WRONG and cost a context tier for nothing. **Verified 2026-08-24:
needle 3/3 at 182,830 real tokens** (depths 0.1/0.5/0.9, 313-358 s per depth —
cold prefills, not the ~1-2 s warm-cache false pass of
[[needle-warm-cache-invalidates-repeats]]), and the worker survived three
consecutive 183k prefills with no OOM. The head costs ~0.9 GB/card, not a tier.
The general lesson: "it loads" is not evidence a window WORKS
([[gemma4-ft-real-window-and-crashes]] loaded at 262k then OOMed under load), and
a filler prompt answering "OK" only proves ALLOCATION. A needle test proves the
context was processed AND retrievable, at the same memory pressure. Use it.
Full 13-suite battery, 1 rep, 212 min. Envelope CLEAN (0 control-token leaks,
0 tool-envelope problems, 0 stream-only leaks across 126 turns).
| suite | qwen38-mtp | field |
|---|---|---|
| coding R1-R4 | 21/23 | IDENTICAL to plain qwen38-awq |
| R5 long-horizon | 6/6, 0 looped, 0% redundant | perfect |
| KLayout unaided | 0/8 | normal ([[klayout-api-recall-vs-composition]]) |
| KLayout +ref | 8/8 | best measured on this harness |
| klvision | 5/6 | > bonsai 4/6 |
| closed loop | 0/4 | normal |
| closed loop +ref | 4/4 | vs qwen3vl-8b 1/4, bonsai 0/4 |
| office unaided | 9/9 | ONLY model to manage this |
| office +ref | 9/9 | ref neither helps nor hurts |
| real-case gate | PASS 7/7 | |
**The headline: 21/23 coding is EXACTLY plain qwen38-awq's score, at ~1.35x the
decode speed.** Speculative decoding is meant to be lossless (the main model
verifies every draft token); this is that claim measured, not assumed. R5 6/6 with
zero looping rules out the degradation-as-looping mode that would be the first
symptom.
Office 9/9 UNAIDED is the standout and it corrects a generalisation. Every other
model here needs the API reference for office (gemma4-ft 6/9, qwen3vl-8b 3/9,
bonsai 0/9), which had suggested the suite measures API RECALL rather than
capability. qwen38-mtp shows a model can simply HAVE the recall.
Cap-hits: 4 total (1 in R1 expr_eval, 3 in unaided KLayout). Per
[[qwen38-ridge-rejected]] the cap-hit COUNT is the leading indicator of a quality
regression, so this is the number to watch on a re-grill. expr_eval also fails on
plain qwen38-awq, so it is not MTP-specific. 5 abnormal finish_reason turns (4.0%).
n=1 — [[round5-is-a-sample-not-a-measurement]]. The perfect scores (8/8, 4/4,
9/9, 7/7) are the ones most likely to move on a repeat.
Three measurements of the same model, and the number SHRANK each time the sample
got more realistic:
41.7 tok/s one synthetic 2000-tok generation (n=1)
39.9 tok/s 3 runs of the same prompt at the 192k config
38.1 tok/s MEAN over 45 graded tasks in the full battery (median 39.5,
range 27.1-43.0)
38.1 is the honest figure — it spans short tasks where per-request overhead
weighs more and prefill-heavy ones where MTP gives nothing. A single long clean
generation is the best case, not the average.
Caveat on the RATIO: qwen38-awq's 28.2 tok/s is itself a single bench run, not a
battery mean, so ~1.35x compares an average against a point measurement. The
direction is solid; do not defend the second decimal.
I predicted ~30 min by calibrating on R4 (38.3s, 5 short tasks). Wrong by 7x. R4's
tasks are ~300 tokens; R1's are 7-10k, and KLayout+ref alone took 62 min because
its large API-reference prefills get NO MTP benefit (prefill measured 4% SLOWER).
Calibrate a battery estimate on a PREFILL-HEAVY suite, not the cheapest one —
MTP speeds decode only, so its advantage shrinks exactly where a battery spends
most of its time.
"qwen38-mtp":
# TRIAL 2026-08-24 — shawnw3i/Qwen3.8-27B-AWQ-MTP: the SAME Qwen3.8-27B AWQ
# INT4 as `qwen38-awq`, plus a REAL multi-token-prediction draft head. Probed
# before downloading: 15 `mtp.*` tensors (mtp.fc + a full single-layer block +
# norms), NOT a stub. 18.7 GB vs qwen38-awq's ~16 GB.
#
# THE CONFIG DOES NOT DECLARE MTP. config.json has no num_nextn_predict_layers
# / mtp_num_hidden_layers at all — exactly the undocumented-MTP case that cost
# time in [[qwen38-atomic-install]]. vLLM copes: qwen3_5_mtp.py reads
# `getattr(config, "mtp_num_hidden_layers", 1)`, and 1 matches the single
# mtp.layers.0 in the weights. Tensor prefix `mtp.` matches maybe_prefix too.
#
# --mamba-cache-mode=align IS LOAD-BEARING: Qwen3_5MTP raises
# NotImplementedError on mamba_cache_mode == "all" (qwen3_5_mtp.py:218).
#
# The draft head is BF16 while the body is INT4 — normal for MTP heads, and
# vLLM explicitly forces mtp.fc unquantized.
#
# WHY: MTP roughly DOUBLED decode on the Qwen3.8 GGUF
# ([[qwen38-uncensored-mtp-probe]]). Open question is whether that survives on
# vLLM AWQ, and whether it costs prefill — [[qwen38-atomic-install]] saw a
# 33-38% prefill REGRESSION from MTP on this family. Measure BOTH before
# preferring this over qwen38-awq.
#
# 196608 — the SAME window as qwen38-awq, VERIFIED not assumed. This was set to
# 131072 first, on the guess that the BF16 draft head would starve the KV pool.
# THE GUESS WAS WRONG. Measured 2026-08-24: it loads at 196608 and passes needle
# 3/3 at 182,830 REAL tokens (depths 0.1/0.5/0.9, 313-358 s each — cold prefills,
# NOT the ~1-2 s warm-cache false pass of [[needle-warm-cache-invalidates-repeats]]),
# and the worker survived three consecutive 183k prefills with no OOM. The draft
# head does cost ~0.9 GB/card, but it does not cost a context tier.
# NOT QUALITY-GRILLED — speed and long-context only, no coding/real-case scores.
aliases: [qwen3.8-27b-awq-mtp]
cmd: |
/usr/bin/env
HF_HOME=/home/giovanni/.cache/huggingface
CUDA_DEVICE_ORDER=PCI_BUS_ID
PATH=/home/giovanni/vllm-env/bin:/home/giovanni/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
${vllm_bin} serve /home/giovanni/models-nvme/qwen38-awq-mtp
--served-model-name qwen38-mtp
--tensor-parallel-size 2
--host 127.0.0.1 --port 9165
--max-model-len 196608
--max-num-seqs 8
--gpu-memory-utilization 0.97
--kv-cache-dtype fp8
--mamba-cache-mode align
--speculative-config '{"method":"qwen3_5_mtp","num_speculative_tokens":1}'
--override-generation-config '{"temperature":0.6}'
--enable-auto-tool-choice
--tool-call-parser qwen3_coder
--reasoning-parser qwen3
proxy: http://127.0.0.1:9165