Standalone multi-token-prediction draft heads for empero-ai/Qwen3.8-9B-Distill, converted from the official BF16 safetensors with llama.cpp's own convert_hf_to_gguf.py.
The upstream model carries an MTP head in its weights (mtp_num_hidden_layers: 1 in config.json), but the official GGUF repo publishes main-model quants only. With no head file there is nothing to pass to --model-draft, so the model runs unassisted and gives up the speculative-decoding path it was built with. These files restore it.
Files
File
Size
Precision
Notes
mtp-Qwen3.8-9B-Distill-head-BF16.gguf
4.56 GB
BF16
Canonical, unquantized. Requantize from this.
mtp-Qwen3.8-9B-Distill-head-Q8_0.gguf
2.43 GB
Q8_0
Benchmarked below. Recommended default.
Both carry 18 tensors — the blk.32.nextn.* MTP block plus token_embd, output and output_norm — for 2.28B parameters, architecture qwen35.
Why Q8_0 is the recommended default here. Draft heads are usually published unquantized, on the reasoning that a few hundred MB of head is negligible beside a multi-GB target and quantizing risks acceptance loss. That reasoning does not transfer to this model. Of the head's 2.28B parameters, ~2.03B is the vocab embedding and output matrices (248,320 x 4,096, twice) — not the MTP block itself. At BF16 the head is 4.56 GB against a 5.38 GB Q4_K_M target, so it very nearly doubles resident size. Q8_0 halves that while quantizing exactly the tensors that tolerate it best, and measured 81.4% acceptance — squarely in the band a same-architecture third-party head reaches. BF16 is here for anyone who wants to verify that trade or requantize differently.
Decode and acceptance over 20 runs x 300 tokens at sampled settings (temp 0.6 / top-p 0.95 / top-k 20) — not greedy, which is variance-dominated at small N. Prefill from server-side prompt_ms, 3 samples per size.
Metric
Value
Decode
73.97 tok/s median · 65.56 mean · sigma 10.57
Acceptance
81.4% (4,236 accepted of 5,203 draft tokens)
Accepted per draft
2.43 (at --spec-draft-n-max 3)
Prefill
1,914 @ 2K · 1,957 @ 5K · 2,020 @ 12K tok/s
Peak VRAM
14.76 GiB (target + head, context <= 12K)
For scale, a same-architecture model with a well-regarded third-party MTP head — Ornith 1.5-9B Q4_K_M + Q8_0 head — measured 65.15 tok/s median and 84.7% acceptance on the same build, in the same session, under the same harness.
What the head is worth
Same model, same build, same session, with and without the head:
Decode (median)
sigma
Prefill @ 12K
Peak VRAM
unassisted
56.69 tok/s
0.05
2,366 tok/s
10.91 GiB
+ Q8_0 head
73.97 tok/s
10.57
2,020 tok/s
14.76 GiB
delta
+30.5%
—
-14.6%
+3.85 GiB
The head costs prefill to buy decode. +30.5% decode is the headline, but it also gives up 14.6% of prefill throughput and 3.85 GB of memory. That is clearly the right trade for chat and agent workloads, which are decode-bound. It is not automatically right for prefill-heavy workloads with short outputs — classification, routing, extraction — where the prefill loss can outweigh the decode gain. Measure your own workload shape before assuming the drafter is a win.
Decode with the head is bimodal, clustering near 74 and near 50 tok/s. Unassisted it is not (sigma 0.05), so the variance is acceptance-driven — the head is very good on some content and less good on other content — rather than thermal or scheduler noise. Prefer the median over the mean when comparing.
Tuning --spec-draft-n-max
Benchmarked at the conventional 3. On a different model on this hardware — one whose head reaches 99.5-100% acceptance — sweeping this flag was worth +16.3%, with a sharp throughput cliff once the verification batch (n_max + 1) exceeded 8. This head's acceptance is 81.4%, so it has less headroom, but the flag is worth sweeping rather than assuming. Do not tune it by stepping upward until throughput stops improving: on this backend that procedure can walk straight past a peak into a much worse setting.
--spec-type draft-mtp — the MTP-native speculative path, not classic n-gram drafting.
-ngld 99 — offload the draft head too; leaving it on CPU negates the win.
--reasoning off — routes thinking tokens back into content. Mandatory for structured-JSON workloads, which otherwise get an empty content and the text in reasoning_content.
--spec-draft-n-max 3 — matches the model's single MTP layer. Raising it past what the head was trained for does not help.
Check acceptance on your own workload from the server's Prometheus endpoint — llamacpp:spec_decode_num_accepted_tokens_total over llamacpp:spec_decode_num_draft_tokens_total. Acceptance is workload-dependent; a number measured on synthetic filler is an upper bound, not a promise.
--mtp exports only the MTP tensors as a standalone draft GGUF. Its counterpart --no-mtp writes the target without them, so a repo can publish both halves cleanly. The same script also has --dspark for DSpark-style heads.
Converted at commit bb4caa754. Any recent llama.cpp with qwen35 MTP support will load these.
License
Apache 2.0, inherited from empero-ai/Qwen3.8-9B-Distill, itself a distillation of Qwen/Qwen3.5-9B. No weights were modified — this is a format conversion and quantization of tensors already present in the upstream release.
Not affiliated with empero-ai or Qwen. Thanks to both for publishing open weights, and to the llama.cpp maintainers for the MTP export path.