Skip to content

spec: enable backend sampling for both dflash & dspark - #26958

Merged
ggerganov merged 3 commits into
ggml-org:masterfrom
ruixiang63:dflash_gpu_sampling
Aug 13, 2026
Merged

spec: enable backend sampling for both dflash & dspark#26958
ggerganov merged 3 commits into
ggml-org:masterfrom
ruixiang63:dflash_gpu_sampling

Conversation

@ruixiang63

Copy link
Copy Markdown
Member

Overview

Similar to #24655, this PR adds backend sampling support for DFlash. After #25532 got merged, we can enable multi-token sampling for DFlash/DSpark now.

This PR provides a simpler, more robust implementation to replace #26842.

Additional information

Requirements

@ruixiang63
ruixiang63 requested a review from a team as a code owner August 12, 2026 11:27
@ruixiang63
ruixiang63 requested review from gaugarg-nv and ggerganov and removed request for a team August 12, 2026 11:27
@pich

pich commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

I ran #26958 against the specialized #26842 implementation on my Glimmer setup.

Same box, same config:

RTX PRO 4000 Blackwell SFF 24 GB / 70 W
Debian 13.6, kernel 6.12.38
driver 610.57.04, CUDA 12.9
Muse Glimmer 30B Q5_K_M, 256K ctx, Q8 KV, DFlash F16 KV, n_max=15, mmproj

10 runs, 1024 tokens each:

#26842: 80.05 tok/s avg
#26958: 80.69 tok/s avg

Pretty much identical performance, #26958 was actually a tiny bit faster here.

Acceptance was identical too: 38.67%, average accepted length 6.77. CPU usage was basically unchanged at around one core.

The one difference I noticed is memory. #26842 loaded at 22,852 MiB, while #26958 used 23,052 MiB, so roughly 200 MiB extra.

I reran the mixed workload as well and got 37.51 tok/s with 14.41% acceptance, very close to my previous numbers. So the big difference between coding and mixed workloads still seems to be mostly about how predictable the output is.

Also tested with the KV actually filled to 262,116 tokens. No OOM, peak was 23,120 / 24,467 MiB.

I like the direction of #26958 more. We get basically the same speed without needing the DFlash-specific argmax path, and the same mechanism can work for DFlash and DSpark.

Maybe one thing worth looking at is that extra ~200 MiB. With greedy top_k(1) I wonder if we could keep only the sampled IDs and avoid some of the full logits/probs/candidate buffers.

I wrote up the original Glimmer/DFlash experiments here if any of the numbers are useful for comparison:
https://piszczek.pl/blog/dflash-changes-what-tokens-per-second-means

@ruixiang63

Copy link
Copy Markdown
Member Author

I ran #26958 against the specialized #26842 implementation on my Glimmer setup.

Same box, same config:

RTX PRO 4000 Blackwell SFF 24 GB / 70 W Debian 13.6, kernel 6.12.38 driver 610.57.04, CUDA 12.9 Muse Glimmer 30B Q5_K_M, 256K ctx, Q8 KV, DFlash F16 KV, n_max=15, mmproj

10 runs, 1024 tokens each:

#26842: 80.05 tok/s avg #26958: 80.69 tok/s avg

Pretty much identical performance, #26958 was actually a tiny bit faster here.

Acceptance was identical too: 38.67%, average accepted length 6.77. CPU usage was basically unchanged at around one core.

The one difference I noticed is memory. #26842 loaded at 22,852 MiB, while #26958 used 23,052 MiB, so roughly 200 MiB extra.

I reran the mixed workload as well and got 37.51 tok/s with 14.41% acceptance, very close to my previous numbers. So the big difference between coding and mixed workloads still seems to be mostly about how predictable the output is.

Also tested with the KV actually filled to 262,116 tokens. No OOM, peak was 23,120 / 24,467 MiB.

I like the direction of #26958 more. We get basically the same speed without needing the DFlash-specific argmax path, and the same mechanism can work for DFlash and DSpark.

Maybe one thing worth looking at is that extra ~200 MiB. With greedy top_k(1) I wonder if we could keep only the sampled IDs and avoid some of the full logits/probs/candidate buffers.

I wrote up the original Glimmer/DFlash experiments here if any of the numbers are useful for comparison: https://piszczek.pl/blog/dflash-changes-what-tokens-per-second-means

Thanks for testing and sharing the results. Very useful! @pich

@am17an

am17an commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

This is still not going to work with -sm tensor right?

@pcuenca

pcuenca commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

cc @ngxson @CISC

Comment thread common/speculative.cpp
Comment thread common/speculative.cpp

@pcuenca pcuenca left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works according to my tests and @pich's 🚀

@ggerganov
ggerganov merged commit 0d0bfcd into ggml-org:master Aug 13, 2026
24 of 27 checks passed
crusaderky pushed a commit to crusaderky/llama.cpp that referenced this pull request Aug 13, 2026
* dflash: enable backend sampling for both dflash & dspark

* enable p_min > 0 in backend sampling and add guard

* cont : add TODO

---------

Co-authored-by: Georgi Gerganov <ggerganov@gmail.com>
@ruixiang63

Copy link
Copy Markdown
Member Author

This is still not going to work with -sm tensor right?

I think -sm tensor falls back to CPU sampling when the draft model itself is tensor-split, GPU sampling still kicks in when the draft runs on a single GPU.

@ruixiang63
ruixiang63 deleted the dflash_gpu_sampling branch August 13, 2026 14:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants