spec : add DFlash2 support (cherry-pick of ggml-org/llama.cpp#27816) - #261
Conversation
…gml-org#27342) (ggml-org#27816) * spec : add DFlash2 support (local convolution + candidate selector) (ggml-org#27342) * support DFlash2 * Add p_min in DFlash2 Assisted-by: Claude Opus 5 * Revert unnecessary changes Assisted-by: Claude Opus 5 * Revert draft sampling in rejection sampling Assisted-by: Claude Opus 5 * Refactor code structure Assisted-by: Claude Opus 5 * Delete embedding scaling Assisted-by: Claude Opus 5 * Gate output transforms on DFlash2 Assisted-by: Claude Opus 5 * Optimize Dflash 2 cost Assisted-by: Claude Opus 5 * Avoid using atoi Assisted-by: Claude Opus 5 * Modify comments Assisted-by: Claude Opus 5 * Move llama_model_dflash_selector_top_k to llama-ext.h Assisted-by: Claude Opus 5 * Formatting Assisted-by: Claude Opus 5 * Apply patch to fix the mrope bug Assisted-by: Claude Opus 5 * fix ci Assisted-by: Claude Opus 5 * Fix graph number calculation Assisted-by: Claude Opus 5 * rename hid and unary Assisted-by: Claude Opus 5 --------- Co-authored-by: Jian Chen <jianchen0311@gmail.com> Co-authored-by: Xuan-Son Nguyen <son@huggingface.co> * revert top-k.cu changes --------- Co-authored-by: Zihan Zhang <tiancaizhangdaxian@sjtu.edu.cn> Co-authored-by: Jian Chen <jianchen0311@gmail.com> (cherry picked from commit b10f9ca)
|
Benchmark on this branch, as promised in the description. NVIDIA L4 (sm_89, CUDA 12.8),
Per-sample outputs, summaries and scripts: https://huggingface.co/datasets/naklitechie/bonsai2-dflash2-bench |
There was a problem hiding this comment.
Checked this against upstream and ran it end to end on Metal and two RTX cards. Looks good to me.
Cherry-pick. All 15 files match b10f9ca hunk for hunk. The only differences are two dropped blank lines and one untouched LLM_KV_SHORTCONV_L_CACHE line in src/llama-arch.cpp that got reformatted. The DFlash2 logits go through build_lora_mm(output, ...), and the new get_rows calls only read the drafter's own selector tables, so the target's Hadamard transforms from #210 apply. #210 looks the transforms up by tensor pointer, so the tied output from #257 is covered too.
Tests. The new TOP_K cases pass 517/517 on Metal (M5 Pro), an RTX 4090 and an RTX 5090.
End to end. Ternary Bonsai 2 27B target, llama-server, one slot, greedy, --spec-draft-n-max 7, 256 tokens. Three DFlash2 drafters: yours (r3 Q4_K_M), ProCreations/Ternary-Bonsai-2-27B-DFlash2 (Q8_0) and incoai/Qwen3.8-27B-DFlash2-GGUF (Q8_0). Speedup over plain decode with thinking off, for code / prose / math prompts:
| device, target | plain tok/s | yours | ProCreations | incoai |
|---|---|---|---|---|
| RTX 4090, PQ2_0 | 87.2 | 1.70 / 1.16 / 2.40 | 1.75 / 1.27 / 2.37 | 1.70 / 1.00 / 2.30 |
| RTX 5090, PQ2_0 | 129.0 | 1.28 / 0.86 / 1.90 | 1.30 / 0.91 / 1.73 | 1.22 / 0.79 / 1.80 |
| RTX 4090, PTQ1_0 | 92.8 | 1.58 / 1.16 / 2.19 | ||
| RTX 5090, PTQ1_0 | 120.9 | 1.52 / 1.16 / 2.19 | ||
| M5 Pro, PQ2_0 | 27.8 | 1.03 / 0.52 / 1.19 | 1.07 / 0.58 / 1.17 | 1.03 / 0.45 / 1.10 |
| M5 Pro, PTQ1_0 | 26.5 | 0.48 / 0.27 / 0.53 |
Thinking on gives similar numbers on the 4090 and somewhat lower ones on the 5090. Thanks also for the L4 benchmark; the numbers above line up with it: roughly 2x on code and math, much less on prose.
Output. On Metal all 24 speculative runs match plain greedy decode byte for byte. On CUDA 28 of 48 match. Where a run differs, all three drafters diverge at the same character, which fits your explanation that the batched verify rounds differently from single-row decode rather than anything drafter-dependent.
Metal. It gives no speedup there, and PTQ1_0 gets slower. The cause is on our side: the Metal PTQ1_0 mat-vec has no fast path for 2 to 8 columns (17408 x 5120 takes 95 us at one column and 620 us at two, against 176 us for PQ2_0). We'll look at that separately; it isn't a reason to hold this PR.
Upstream follow-ups not included, which could be separate small cherry-picks: cc231cb (NVFP4 scales in DFlash attention), 662a0b0 (fused encoder KV injection), and fa67698 / b0dcb81 (speculation after image input).
|
Thanks @bri-prism for the careful review and the cross-device numbers. One more result that needs no code change here: stacking prompt lookup in front of DFlash2 with the existing priority list, NVIDIA L4,
On Metal I see the same thing you describe from the small-M side: an in-block copy draft (at most 8 rows) gave no gain in our MLX and WebGPU ports, so the long drafts are what pay, and those need a fast verify above 8 rows. Rows and summaries: https://huggingface.co/datasets/naklitechie/bonsai2-dflash2-bench (folder |
|
Windows build check (CI for this PR is stuck in the queue): merged onto
Smoke test: the MSVC-built One unrelated Windows issue came up that is pre-existing on Not covered: Ubuntu, and running DFlash2 end to end (no DFlash2 drafter here). Tested with Claude Code. |
Overview
Cherry-pick of upstream ggml-org#27816 (DFlash2: local convolution + candidate selector, merge commit b10f9ca) onto
prism. Authorship is kept; the only local edits are formatting insrc/llama-arch.cppto match this branch.With it,
--spec-type draft-dflashloads DFlash2 drafters (thez-lab/Qwen3.8-27B-DFlash2family) against Ternary Bonsai 2 27B. The borrowedtok_embd/outputHadamard handling from #210 covers the DFlash2 path; no extra change was needed.Additional information
Tested on NVIDIA L4 (sm_89, CUDA 12.8),
llama-server, Ternary-Bonsai-2-27B GGUF target + a DFlash2 Q4_K_M drafter, greedy, one slot:Target PQ2_0,
--spec-draft-n-max 7. Plain decode of the same target on the same GPU is about 30 tok/s, so code runs about 2x and prose about 1x. A larger run (HumanEval, MBPP, GSM8K, MT-Bench, thinking on/off, pass@1 plain vs speculative) is in progress; I will post it here.The drafter used is a re-fit of z-lab's drafter to the ternary target (naklitechie/Qwen3.8-27B-DFlash2-ternary-bonsai2). Greedy output with speculation is not byte-identical to plain decode: splits happen only where the plain run's top-2 logprobs are within 0.03 nats (batched verify vs single-row kernels).
Requirements