Skip to content

CUDA: add Q2_0 support - #25707

Merged
ggerganov merged 1 commit into
ggml-org:masterfrom
PrismML-Eng:pr/q2_0-cuda
Jul 30, 2026
Merged

CUDA: add Q2_0 support#25707
ggerganov merged 1 commit into
ggml-org:masterfrom
PrismML-Eng:pr/q2_0-cuda

Conversation

@khosravipasha

Copy link
Copy Markdown
Contributor

Overview

Follow up PR after the Q2_0 CPU (#24448) and Metal (#25419) backends. This PR adds the CUDA backend for Q2_0.

Supported Models

Supported Models: Ternary-Bonsai-{27B, 8B, 4B, 1.7B}.

Note: In our huggingface repos ggufs ending with _Q2_0_g64.gguf are packed for this PR; Q2_0.gguf ones are the older group-128 packing from our fork. After all backends merge we will rename and replace with the official Q2_0 (group size 64).

hf download prism-ml/Ternary-Bonsai-8B-gguf   Ternary-Bonsai-8B-Q2_0_g64.gguf   --local-dir models
hf download prism-ml/Ternary-Bonsai-27B-gguf  Ternary-Bonsai-27B-Q2_g64.gguf    --local-dir models

Correctness: F16 vs Q2_0 logits KL

To test for correctness we compare the logits from Q2_0 vs F16. Since the Ternary-Bonsai models are natively ternary and it packs perfectly into 2-bits (Q2_0), if we compre logits from Q2_0 vs F16 we should get a KL close to 0.

./build/bin/llama-perplexity -m models/Ternary-Bonsai-<size>-F16.gguf \
  -f datasets/wikitext-2-raw/wiki.test.raw -c 512 --chunks 20 -ngl 99 --save-all-logits f16.bin

./build/bin/llama-perplexity -m models/Ternary-Bonsai-<size>-Q2_0_g64.gguf \
  -f datasets/wikitext-2-raw/wiki.test.raw -c 512 --chunks 20 -ngl 99 \
  --kl-divergence --kl-divergence-base f16.bin
size Mean KLD Same top-1 PPL(Q2_0)/PPL(F16)
1.7B 0.000445 98.69 % 1.0017
4B 0.000462 98.35 % 1.0026
8B 0.000446 98.53 % 1.0001
27B 0.000620 98.77 % 1.0004

Speeds: llama-bench CUDA (NVIDIA L40S)

./build/bin/llama-bench -m models/Ternary-Bonsai-<size>-Q2_0_g64.gguf -ngl 99 -p 512 -n 128 -r 5
| model            |       size |     params | backend | ngl |  test |               t/s |
| ---------------- | ---------: | ---------: | ------- | --: | ----: | ----------------: |
| qwen3 1.7B Q2_0  | 461.79 MiB |     1.72 B | CUDA    |  99 | pp512 | 30390.71 ± 7897.9 |
| qwen3 1.7B Q2_0  | 461.79 MiB |     1.72 B | CUDA    |  99 | tg128 |   530.76 ±   9.90 |
| qwen3 4B Q2_0    |   1.05 GiB |     4.02 B | CUDA    |  99 | pp512 | 16355.03 ± 3005.3 |
| qwen3 4B Q2_0    |   1.05 GiB |     4.02 B | CUDA    |  99 | tg128 |   322.21 ±   1.79 |
| qwen3 8B Q2_0    |   2.15 GiB |     8.19 B | CUDA    |  99 | pp512 | 10490.07 ± 1138.2 |
| qwen3 8B Q2_0    |   2.15 GiB |     8.19 B | CUDA    |  99 | tg128 |   239.52 ±   0.76 |
| qwen35 27B Q2_0  |   7.05 GiB |    26.90 B | CUDA    |  99 | pp512 |  2782.77 ± 214.07 |
| qwen35 27B Q2_0  |   7.05 GiB |    26.90 B | CUDA    |  99 | tg128 |    72.03 ±   0.19 |
Q2_0 g64 vs g128; speed/size tradeoff (for the curious)

The g128 is built from our fork, but wanted to compare speeds if anyone is curious.
Prompt processing is similar as expected; token generation is slightly slower for the 8B as expected from the larger size for g64 vs g128. For 27B token generation looks very similar.

model block pp512 (t/s) tg128 (t/s) size
8B g64 10490 239.5 2.15 GiB
8B g128 11008 252.9 2.03 GiB
27B g64 2783 72.0 7.05 GiB
27B g128 2855 71.1 6.66 GiB

Requirements

  • I have read and agree with the contributing guidelines: Yes

  • AI usage disclosure: Initial kernels were for group size 128, for the migration to group size 64 and rebasing to master used AI to make the changes (some cuda files were moved and refactored). Mostly followed either Q4_0 or Q1_0.
    Manually reviewed all the changes and ran both speed and KL tests for correctness with a fresh build. The Q2_0 group 128 has been used by many people before and they behaved well, the main extra tests here were done to make sure newly converted g64 ggufs match the PR.

@khosravipasha
khosravipasha requested review from a team and ggerganov as code owners July 15, 2026 09:17
@github-actions github-actions Bot added testing Everything test related ggml changes relating to the ggml tensor library for machine learning CUDA Related to the CUDA backend labels Jul 15, 2026
@cfournel

Copy link
Copy Markdown

@khosravipasha need help on this ?

@khosravipasha

Copy link
Copy Markdown
Contributor Author

@cfournel Thanks, this is mostly good for initial PR I think. Mostly needs a review.
Can always optimize it more in future PRs. For first round tried to make it simple and minimal.

@mata-p

mata-p commented Jul 22, 2026

Copy link
Copy Markdown

@khosravipasha
I built this before and it works great for me.
But seems this no longer cleany applies onto master, needs a rebase.

@khosravipasha

Copy link
Copy Markdown
Contributor Author

@mata-p thanks for the heads up and testing.
Just rebased, for me applied ok for the rebase, do you remember what issue you had with applying it to master?

@mata-p

mata-p commented Jul 23, 2026

Copy link
Copy Markdown

Thanks!
Sorry for the confusion. I didn't realize my local master had some changes from other tests which seeminlgy weren't in their intended branch... So all my fault.

@am17an am17an 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.

Looks ok to me from static analysis.

@Green-Sky

Green-Sky commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

This pr works great with sd.cpp and their ternary flux.2 klein 4b quant.

bonsai-q2_0_mod

https://huggingface.co/Green-Sky/bonsai-image-ternary-4B-GGUF/blob/main/bonsai_image_4b-mod_q8_0-q2_0.gguf

edit: @khosravipasha did you know that unlike the binary, the ternary version preserves the editing capabilities for the model?

@khosravipasha

Copy link
Copy Markdown
Contributor Author

@Green-Sky cute cat :)
nice great to know, we have not tried image editing much yet.


#define DECL_MMQ_CASE(type) \
template void mul_mat_q_case<type>(ggml_backend_cuda_context & ctx, const mmq_args & args, cudaStream_t stream) \

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.

I think Q2_0 needs a declaration here as well, like Q1_0 got in #25778

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

added it I believe, and rebased with new master

@khosravipasha

Copy link
Copy Markdown
Contributor Author

@am17an
rebased and added some missing declerations based on @dfriehs's suggestion, there was some refactoring too but small things moved around. Reran our speed/KL tests and looks good still.

@am17an am17an added the merge ready A maintainer can use this label to indicate that they consider the changes final and ready to merge. label Jul 30, 2026
@ggerganov
ggerganov merged commit 9b2a088 into ggml-org:master Jul 30, 2026
26 of 32 checks passed
@MaxKruse

MaxKruse commented Jul 30, 2026

Copy link
Copy Markdown

is the provided dpsark drafter in the repo supposed to work in this PR?

hf download prism-ml/Ternary-Bonsai-27B-gguf  Ternary-Bonsai-27B-Q2_g64.gguf --local-dir ./bonsai-27b
hf download prism-ml/Ternary-Bonsai-27B-gguf Ternary-Bonsai-27B-dspark-Q4_1.gguf --local-dir ./bonsai-27b

and the following router-mode ini config (abbreviated)

[Ternary Bonsai 27B]
model = C:\Users\maxkr\LLMs\bonsai-27b\Ternary-Bonsai-27B-Q2_g64.gguf
ctx-size = 4096
spec-type = draft-dspark
md = C:\Users\maxkr\LLMs\bonsai-27b\Ternary-Bonsai-27B-dspark-Q4_1.gguf
spec-draft-n-max = 4
Load logs
0.08.361.700 I srv          load: spawning server instance with name=Ternary Bonsai 27B on port 49990
0.08.361.752 I srv          load: spawning server instance with args:
0.08.361.753 I srv          load:   C:\tools\llamacpp\llama-server.exe
0.08.361.753 I srv          load:   --host
0.08.361.753 I srv          load:   127.0.0.1
0.08.361.754 I srv          load:   --min-p
0.08.361.754 I srv          load:   0.0
0.08.361.755 I srv          load:   --port
0.08.361.755 I srv          load:   49990
0.08.361.755 I srv          load:   --presence-penalty
0.08.361.755 I srv          load:   0.0
0.08.361.756 I srv          load:   --reasoning-budget
0.08.361.756 I srv          load:   16384
0.08.361.756 I srv          load:   --reasoning-budget-message
0.08.361.757 I srv          load:   ...\n Let's stop here. We considered enough. Let's answer directly.
0.08.361.757 I srv          load:   --reasoning-preserve
0.08.361.757 I srv          load:   --repeat-penalty
0.08.361.758 I srv          load:   1.0
0.08.361.758 I srv          load:   --model-draft
0.08.361.759 I srv          load:   C:\Users\maxkr\LLMs\bonsai-27b\Ternary-Bonsai-27B-dspark-Q4_1.gguf
0.08.361.759 I srv          load:   --spec-draft-n-max
0.08.361.759 I srv          load:   4
0.08.361.760 I srv          load:   --spec-ngram-mod-n-match
0.08.361.760 I srv          load:   24
0.08.361.760 I srv          load:   --spec-ngram-mod-n-max
0.08.361.760 I srv          load:   64
0.08.361.761 I srv          load:   --spec-ngram-mod-n-min
0.08.361.761 I srv          load:   48
0.08.361.762 I srv          load:   --spec-type
0.08.361.762 I srv          load:   draft-dspark
0.08.361.762 I srv          load:   --temperature
0.08.361.762 I srv          load:   0.7
0.08.361.763 I srv          load:   --top-k
0.08.361.763 I srv          load:   20
0.08.361.763 I srv          load:   --top-p
0.08.361.764 I srv          load:   0.95
0.08.361.764 I srv          load:   --alias
0.08.361.764 I srv          load:   Ternary Bonsai 27B
0.08.361.765 I srv          load:   --batch-size
0.08.361.765 I srv          load:   512
0.08.361.765 I srv          load:   --ctx-size
0.08.361.765 I srv          load:   4096
0.08.361.766 I srv          load:   --cache-ram
0.08.361.766 I srv          load:   8192
0.08.361.766 I srv          load:   --fit
0.08.361.767 I srv          load:   on
0.08.361.767 I srv          load:   --fit-target
0.08.361.767 I srv          load:   1536
0.08.361.767 I srv          load:   --load-mode
0.08.361.768 I srv          load:   none
0.08.361.768 I srv          load:   --model
0.08.361.768 I srv          load:   C:\Users\maxkr\LLMs\bonsai-27b\Ternary-Bonsai-27B-Q2_g64.gguf
0.08.361.769 I srv          load:   --parallel
0.08.361.769 I srv          load:   1
0.08.361.770 I srv          load:   --reasoning
0.08.361.770 I srv          load:   on
0.08.361.771 I srv          load:   --threads
0.08.361.771 I srv          load:   12
0.08.361.771 I srv          load:   --ubatch-size
0.08.361.772 I srv          load:   512
[49990] 0.00.017.747 I cmn  common_param: common_params_print_info: verbosity = 3 (adjust with the `-lv N` CLI arg)
[49990] 0.00.078.513 W srv  llama_server: -----------------
[49990] 0.00.078.519 W srv  llama_server: CORS is set to allow all origins ('*') and no API key is set
[49990] 0.00.078.519 W srv  llama_server: this can be a security risk (cross-origin attacks)
[49990] 0.00.078.520 W srv  llama_server: more info: https://github.com/ggml-org/llama.cpp/pull/25655
[49990] 0.00.078.521 W srv  llama_server: -----------------
[49990] cmd_child_to_router:state:{"state":"loading","payload":{"stages":["text_model","spec_model"],"current":"text_model","value":0.0}}
[49990] 0.00.093.514 I srv    load_model: loading model 'C:\Users\maxkr\LLMs\bonsai-27b\Ternary-Bonsai-27B-Q2_g64.gguf'
[49990] 0.00.093.776 E gguf_init_from_reader: tensor 'dspark.fc.weight' has offset 337718592, expected 357584192
[49990] 0.00.093.779 E gguf_init_from_reader: failed to read tensor data
[49990] 0.00.093.861 E llama_model_load: error loading model: llama_model_loader: failed to load model from C:\Users\maxkr\LLMs\bonsai-27b\Ternary-Bonsai-27B-dspark-Q4_1.gguf
[49990] 0.00.093.869 E llama_model_load_from_file_impl: failed to load model
[49990] 0.00.093.905 W srv    load_model: [spec] failed to measure draft model memory: failed to load model
[49990] cmd_child_to_router:state:{"state":"loading","payload":{"stages":["text_model","spec_model"],"current":"text_model","value":0.0}}
[49990] cmd_child_to_router:state:{"state":"loading","payload":{"stages":["text_model","spec_model"],"current":"text_model","value":0.13665305078029633}}
[49990] cmd_child_to_router:state:{"state":"loading","payload":{"stages":["text_model","spec_model"],"current":"text_model","value":0.3052625358104706}}
[49990] cmd_child_to_router:state:{"state":"loading","payload":{"stages":["text_model","spec_model"],"current":"text_model","value":0.4795544445514679}}
[49990] cmd_child_to_router:state:{"state":"loading","payload":{"stages":["text_model","spec_model"],"current":"text_model","value":0.6559694409370422}}
[49990] cmd_child_to_router:state:{"state":"loading","payload":{"stages":["text_model","spec_model"],"current":"text_model","value":0.8281069397926331}}
[49990] cmd_child_to_router:state:{"state":"loading","payload":{"stages":["text_model","spec_model"],"current":"text_model","value":1.0}}
[49990] cmd_child_to_router:state:{"state":"loading","payload":{"stages":["text_model","spec_model"],"current":"spec_model","value":0.0}}
[49990] 0.02.373.294 I common_speculative_init_result: loading draft model 'C:\Users\maxkr\LLMs\bonsai-27b\Ternary-Bonsai-27B-dspark-Q4_1.gguf'
[49990] 0.02.373.539 E gguf_init_from_reader: tensor 'dspark.fc.weight' has offset 337718592, expected 357584192
[49990] 0.02.373.545 E gguf_init_from_reader: failed to read tensor data
[49990] 0.02.373.590 E llama_model_load: error loading model: llama_model_loader: failed to load model from C:\Users\maxkr\LLMs\bonsai-27b\Ternary-Bonsai-27B-dspark-Q4_1.gguf
[49990] 0.02.373.595 E llama_model_load_from_file_impl: failed to load model
[49990] 0.02.373.596 E common_speculative_init_result: failed to load draft model, 'C:\Users\maxkr\LLMs\bonsai-27b\Ternary-Bonsai-27B-dspark-Q4_1.gguf'
[49990] 0.02.373.602 E srv    load_model: failed to load draft model, 'C:\Users\maxkr\LLMs\bonsai-27b\Ternary-Bonsai-27B-dspark-Q4_1.gguf'
[49990] 0.02.373.612 I srv    operator(): operator(): cleaning up before exit...
[49990] 0.02.374.553 E srv  llama_server: exiting due to model loading error
0.10.944.316 I srv    operator(): instance name=Ternary Bonsai 27B exited with status 1

@khosravipasha

@khosravipasha

Copy link
Copy Markdown
Contributor Author

@MaxKruse dspark drafter still experimental so will need our fork in near future.

We will be doing a major refactor our of our fork after this PR is merged so will see how best to support that in mainline, for now need to wait until llama.cpp's dspark version gets merged and stable, our implemention is very experimental and not ready for being upstreamed here.

epoyraz added a commit to epoyraz/Bonsai-demo that referenced this pull request Jul 31, 2026
The Q2_0 CUDA merge covers normal decoding only. The paired
*dspark-Q4_1*.gguf drafter does not load on mainline llama.cpp, which
rejects it with "tensor 'dspark.fc.weight' has offset ..., expected ...";
a user hit exactly this on ggml-org/llama.cpp#25707 while testing the
merged CUDA backend.

Per the upstream reply on that thread, the dspark implementation is
experimental, not ready to be upstreamed, and waiting on llama.cpp's own
dspark support to merge and stabilize. Without this the preceding
"no fork needed" line reads as if the fork is now optional everywhere.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
kashif pushed a commit to kashif/llama.cpp that referenced this pull request Aug 2, 2026
huaxel pushed a commit to huaxel/CachyLLama that referenced this pull request Aug 2, 2026
smalinin pushed a commit to smalinin/llama.cpp that referenced this pull request Aug 4, 2026
khosravipasha added a commit to PrismML-Eng/Bonsai-demo that referenced this pull request Aug 5, 2026
* docs: mark Q2_0 CUDA backend as merged in mainline llama.cpp

ggml-org/llama.cpp#25707 ("CUDA: add Q2_0 support") was merged upstream on
2026-07-30 (9b2a088), so the ternary migration table no longer needs the
"in review" row.

Also refreshes the surrounding prose that enumerated which backends run
Q2_0 on mainline, which the merge (and the earlier Vulkan merge, #25430)
had left stale: CPU, Metal, Vulkan, and CUDA now all run the group-64
files on stock llama.cpp. x86 AVX-512-VNNI remains the only outstanding
item, and the demo still ships the fork binaries for the group-128
*-Q2_0.gguf files until the format migration completes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs: note that the dspark drafter is still fork-only

The Q2_0 CUDA merge covers normal decoding only. The paired
*dspark-Q4_1*.gguf drafter does not load on mainline llama.cpp, which
rejects it with "tensor 'dspark.fc.weight' has offset ..., expected ...";
a user hit exactly this on ggml-org/llama.cpp#25707 while testing the
merged CUDA backend.

Per the upstream reply on that thread, the dspark implementation is
experimental, not ready to be upstreamed, and waiting on llama.cpp's own
dspark support to merge and stabilize. Without this the preceding
"no fork needed" line reads as if the fork is now optional everywhere.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs: tighten ternary upstream status; clarify PQ2_0 is reserved/experimental

* docs: correct dspark upstream status — mainline has DSpark (#25173), our drafter fork-only due to packing (#26337)

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Pasha Khosravi <khosravipasha@gmail.com>
satindergrewal pushed a commit to satindergrewal/llama.cpp that referenced this pull request Aug 11, 2026
satindergrewal pushed a commit to satindergrewal/llama.cpp that referenced this pull request Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CUDA Related to the CUDA backend ggml changes relating to the ggml tensor library for machine learning merge ready A maintainer can use this label to indicate that they consider the changes final and ready to merge. testing Everything test related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants