# MTP draft heads for Qwen3.8-Flash-Next An MTP head guesses the next few tokens; the main model verifies them in one pass. Verification is exact, so the output is unchanged - only the speed. Worth about **1.3x to 1.7x** at low concurrency. ## Which file **Use `mtp-Qwen3.8-Flash-Next-shared-Q8_0.gguf` (2.60 GB).** Fastest of the set. | file | size | | |---|---|---| | `shared-Q8_0` | 2.60 GB | **recommended** | | `shared-Q4_K_M` | 1.78 GB | smaller, ~2 points less acceptance | | `shared-BF16` | 4.87 GB | bigger *and* slower than Q8_0 | | `Q8_0` / `Q4_K_M` / `BF16` | 3.85 / 2.60 / 7.24 GB | self-contained variants | `shared-` heads borrow the token embedding and output projection from the model you are already running, saving about 1.3 GB. They draft identically to the self-contained files, which carry their own copies and are only needed on builds without borrowing support. BF16 is bigger and slower: a draft step is dominated by the output projection, which is cheaper to execute at 8 bits. ## Requirements **A stock `ggml-org/llama.cpp` build cannot use these.** Mainline has no MTP graph for the `qwen4exp` architecture, no cross-model tensor borrowing, and no `--spec-type draft-mtp` option, so passing a head to it does nothing. Pick one of the three below. ### Option 1: prebuilt binaries (easiest) From [unslothai/llama.cpp releases](https://github.com/unslothai/llama.cpp/releases), tag `b10715-mix-86bd2d3` or newer. Assets are `app----.tar.gz` (`.zip` on Windows) for CPU, CUDA 12/13, ROCm and Vulkan. ### Option 2: build the upstream pull request [ggml-org/llama.cpp#28243](https://github.com/ggml-org/llama.cpp/pull/28243) is the MTP support going to mainline. Building it: ```bash apt-get update apt-get install pciutils build-essential cmake curl libcurl4-openssl-dev -y git clone --branch qwen4exp/mtp https://github.com/danielhanchen/llama.cpp cmake llama.cpp -B llama.cpp/build \ -DBUILD_SHARED_LIBS=OFF -DGGML_CUDA=ON cmake --build llama.cpp/build --config Release -j --clean-first \ --target llama-cli llama-mtmd-cli llama-server llama-gguf-split cp llama.cpp/build/bin/llama-* llama.cpp ``` Omit `-DGGML_CUDA=ON` for a CPU build. To track the pull request rather than the branch, which keeps working if the branch is renamed or deleted, replace the clone with: ```bash git clone https://github.com/ggml-org/llama.cpp git -C llama.cpp fetch origin refs/pull/28243/head git -C llama.cpp checkout FETCH_HEAD ``` Then fetch a head: ```bash pip install -U "huggingface_hub[cli]" hf download unsloth/Qwen3.8-Flash-Next-GGUF \ --local-dir unsloth/Qwen3.8-Flash-Next-GGUF \ --include "*mtp-Qwen3.8-Flash-Next-shared-Q8_0.gguf*" ``` It lands in an `MTP/` subfolder, so the path to pass is `unsloth/Qwen3.8-Flash-Next-GGUF/MTP/mtp-Qwen3.8-Flash-Next-shared-Q8_0.gguf`: ```bash llama.cpp/llama-server \ -hf unsloth/Qwen3.8-Flash-Next-GGUF:UD-Q4_K_XL \ -md unsloth/Qwen3.8-Flash-Next-GGUF/MTP/mtp-Qwen3.8-Flash-Next-shared-Q8_0.gguf \ --spec-type draft-mtp --spec-draft-n-max 2 ``` ### Option 3: build the unsloth fork [unslothai/llama.cpp#144](https://github.com/unslothai/llama.cpp/pull/144): ```bash git clone https://github.com/unslothai/llama.cpp && cd llama.cpp git fetch origin pull/144/head:mtp && git checkout mtp cmake -B build -DCMAKE_BUILD_TYPE=Release -DGGML_CUDA=ON # omit -DGGML_CUDA for CPU cmake --build build -j ``` ## Usage ```bash llama-cli \ -m Qwen3.8-Flash-Next-UD-Q4_K_XL-00001-of-00004.gguf \ -md mtp-Qwen3.8-Flash-Next-shared-Q8_0.gguf \ --spec-type draft-mtp --spec-draft-n-max 2 -ngl 999 ``` Same flags for `llama-server`. `--spec-draft-n-max 2` is a good default; higher drafts more but each guess is accepted less often. **Always pass `-md` explicitly.** The heads live in an `MTP/` subfolder, which sidecar auto-discovery does not search, so `--spec-type draft-mtp` on its own finds nothing and you get the main model's speed with no error saying why. **A `shared-` head logs one error line at startup and then works.** The automatic memory fit sizes the draft by loading it on its own, before the main model exists, so there is nothing for it to borrow from and the measurement fails: ``` E llama_model_load: error loading model: borrow_shared_tensor: this model is a draft head ... W operator(): failed to measure the memory of the extra model, fitting without it ``` Speculation still runs. The only real consequence is that the fit does not count the draft's memory, so on a card with little headroom it may choose a context size that does not fit. Pass `-c` and `-ngl` yourself, or use a self-contained head, if that matters to you. Self-contained heads measure cleanly and produce no such line. To confirm it is running, look for this in the log. If it never appears, speculation is off and you are probably on a build without MTP support: ``` draft acceptance = 0.66139 (325 accepted / 491 generated), mean len = 2.76 ``` ## Measured Single stream, greedy, one B200, `shared-Q8_0`: | main model | off | on | | |---|---|---|---| | `UD-Q4_K_XL` | 83.2 tok/s | **138.8 tok/s** | **1.67x** | | `UD-IQ1_S` | 90.1 tok/s | **120.9 tok/s** | **1.34x** | Acceptance: `shared-BF16` 66.5%, `shared-Q8_0` 66.1%, `shared-Q4_K_M` 64.4%. **These are greedy numbers.** Higher temperature makes the target less predictable, so fewer guesses are accepted and the speedup shrinks. A speculative decoding figure means nothing without the sampler it was measured with. ## When not to use it **Skip MTP for concurrent serving.** A win at concurrency 1, but measured as a net loss (~0.81x to 0.87x) at concurrency 8: a busy model has little idle capacity for a draft to exploit. These heads are for Qwen3.8-Flash-Next only; a mismatched pairing is rejected with an error rather than producing bad output. Loading a head on its own, without a main model, is also rejected.