Repository navigation
qwen4exp MTP: reduced draft vocabulary (d2t + K-row head) - #56
Merged
bong-water-water-bong merged 1 commit intoOct 1, 2026
Merged
Conversation
A draft head GGUF may now carry blk.<L>.nextn.shared_head_head.weight with K rows plus an
I64 d2t [K] (draft row -> token id). The draft pass runs the K-row head and scatters its
logits into a full-vocabulary row that is -inf elsewhere, the same set_rows scatter DFlash
uses. The target still verifies every drafted token. Our code is in
src/models/qwen4exp-draft-vocab.{h,cpp}; qwen4exp.cpp keeps two hooks.
Qwen3.8-Flash-Next UD-Q4_K_XL on Strix Halo (Vulkan, greedy, draft length 3, rounds 2-3 of
3): the 65,536-id English + code list from dime-online/qwen3.8-Flash-DGX-UltraFast turns the
borrowed 644 MiB Q8_0 output matrix into a 170 MiB head. Code 47.8 -> 52.1 tok/s, prose
38.6 -> 42.2, 768-token code 45.8 -> 48.3; step 76 -> 70 ms; acceptance 186/207 both. A
French prompt is slower (46.4 -> 39.6): its tokens are outside the list. Text in rounds 2-3
is the same as with the full head; neither is token-identical to decoding without a draft
(near-tie flips from batched verification, present with the stock head too).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bong-water-water-bong
left a comment
Author
There was a problem hiding this comment.
Review (PR-Agent duty). The approach is good: the K-row head plus a scatter to a full row of -inf reuses DFlash's set_rows path. All the code is in our headed files (qwen4exp-draft-vocab.{h,cpp}), with two hooks in qwen4exp.cpp. And the target still verifies every token.
One fix before merge:
- Validate the d2t ids.
qwen4exp_draft_vocab_rowschecks d2t's type, rank and length, but not its values. A d2t id that's negative or >= n_vocab makesggml_set_rowswrite outside the full-vocabulary row, and duplicate ids silently drop a row. d2t is tiny (K int64s), so check it once after tensor data is loaded (0 <= id < n_vocab, and no duplicates) and fail the load with a clear message. A malformed or hand-made draft GGUF shouldn't be able to corrupt memory.
Notes:
- Non-English. French drops 46.4 -> 39.6 tok/s (-15%) because its tokens fall outside the 65,536-id list. Since the reduced head only exists when the draft GGUF carries it, the default is unaffected. Please keep it that way:
1bit serveshouldn't auto-pick a reduced-head draft, and the docs that mention it should state the non-English cost next to the speedup. - Provenance of the id list. The 65,536-id list comes from
dime-online/qwen3.8-Flash-DGX-UltraFast. Before we publish a reduced-head GGUF (HF org) or commit the list, check that repo's license, and credit it in the model card and NOTICE (per-model license checks, as with every HF upload). - Measurements. "Rounds 2-3 of 3" means the warm rounds. Fine, but say so in the docs table too.
Mergeable once the d2t value check is in.
bong-water-water-bong
merged commit Oct 1, 2026
fd16437
into
1bit/vulkan-upstream
9 of 11 checks passed
Author
|
Post-merge: the d2t value validation from the review is still missing (see engine ggml-org#269). Needed before the engine pins this. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Lets a Qwen3.8-Flash-Next (
qwen4exp) MTP draft head draft from part of the vocabulary.blk.<L>.nextn.shared_head_head.weightwith K rows and an I64d2t[K] (draft row -> token id). Withoutd2tnothing changes.ggml_fill+ggml_set_rowspatterndflash.cppuses.src/models/qwen4exp-draft-vocab.{h,cpp}(Apache notice);qwen4exp.cppgets two hooks.Measured
Strix Halo, Vulkan, Qwen3.8-Flash-Next UD-Q4_K_XL, Unsloth's shared Q8_0 MTP head, greedy, draft length 3,
-lm mmap -lzm on, coder-llm stopped, box.lock held. Median of rounds 2-3 of 3.The id list is English + code (dime-online/qwen3.8-Flash-DGX-UltraFast, Apache-2.0), so French drafts get rejected more often. Without a draft: 23.7 tok/s.
Output
Greedy text compared with decoding without a draft:
Unsloth's shared Q4_K_M head was also tried: no faster (46.7 code) and more flips, so not used.
Tests
🤖 Generated with Claude Code