vulkan: add TQ2_0 (ternary) support#25850
Conversation
…atmul via dequant_funcs First Vulkan ternary type in ggml. Correctness: OM-125m TQ2_0 vs F16 top-12 logprobs identical to 4 decimals fully offloaded (float dequant path, no Q8_K activation quant). Speed at 125m ~= F16 (overhead-bound at this scale); the bandwidth win targets larger BitNet SKUs. MMQ/int-dot path intentionally not wired yet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vulkan now implements TQ2_0 (dequant, mul_mat_vec, mul_mm, get_rows); backends without support skip via not-supported as usual. TQ1_0 stays disabled. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Hi @michaeltrabalka-tech, thanks for your contribution! Per our contribution guidelines, the automated PR checker found the following issue(s) that need your attention:
Please note that maintainers reserve the right to make final decisions on PRs. If you believe there is a mistake, please comment below. |
|
Thanks — two clarifications:
If this level of AI involvement falls outside what the project accepts, I understand and will defer to maintainer judgment — happy to rework, split, or close as preferred. |
Overview
Adds Vulkan backend support for
GGML_TYPE_TQ2_0(the 2.06 bpw ternary type usedby BitNet/TriLM-style models), which is currently CPU-only. This complements the
recently added
q2_0work — ternary models are where 2-bit weight traffic paysoff most (~8x smaller FFN weights vs F16).
What's included:
types.glsl:block_tq2_0(+_packed16) and quant constantsdequant_tq2_0.comp: dequant pipelinemul_mat_vec_tq2_0.comp: dedicated mmv shader. TQ2_0's qs group/level layoutmatches Q2_K's thread-access scheme, so this reuses the Q2_K thread layout with a
d * (Σ b·q − Σ b)factorization (the −1 offset is hoisted out of the inner loop).dequant_funcs.glsl/mul_mm_funcs.glsl: 2-wide dequant + tile loads for thefloat-dequant matmul path
dequant_funcs_cm2.glsl: coopmat2 decode callbacksvulkan-shaders-gen.cpp: type registration + dedicated-mmv predicateggml-vulkan.cpp: pipeline creation/dispatch switches,supports_op,GET_ROWSDeliberately not in this PR (follow-up material): MMQ/integer-dot path for
TQ2_0 and TQ1_0 support. The float-dequant path is correct and already useful;
int-dot is an optimization on top.
Block layout reference
TQ2_0 block = 256 elements:
qs[64]as two 32-byte groups, element indexg*128 + l*32 + mstored at bit position2*lof byteg*32 + m; codes map{0,1,2,3} → {-1,0,1,2}viaw = (q-1) * dwith one f16 scaledper block.The packer/layout was validated bit-exact against ggml's CPU reference.
Testing
logprobs are identical to 4 decimal places on Vulkan. (Notably tighter than the
CPU TQ2_0 path, whose Q8_K activation quantization introduces small logit noise —
the Vulkan float-dequant path avoids that.)
test-backend-ops test -p tq2_0: 21/21 pass on Vulkan0 vs CPU reference(MUL_MAT ×11, MUL_MAT_ID ×3, GET_ROWS ×4, CPY ×3). This PR also enables TQ2_0 in
the test type lists (was commented out as "TODO: implement for all backends" —
backends without support skip via the normal not-supported path; TQ1_0 stays
disabled).
this size is launch-overhead-bound, not weight-bandwidth-bound; the type exists
for larger BitNet-class models where the 8x weight-traffic cut dominates.
Happy to split the mmv shader into a follow-up or adjust to maintainer preferences
on pipeline wiring.
Additional information
Perf at 125M is parity with F16 (decode there is launch-overhead-bound); the type
targets larger BitNet-class models where the ~8x FFN weight-traffic reduction is
the decode lever. MMQ/integer-dot and TQ1_0 are deliberately left for follow-ups.
Requirements
AI usage disclosure: implementation and tests were co-developed with an AI assistant
(Claude); all correctness/performance results were produced and verified on real
hardware (AMD RX 9070 XT, Vulkan, test-backend-ops vs CPU reference).