feat: TurboQuant KV cache (TBQ3_0/TBQ4_0) with CPU + CUDA support - #21
Merged
Merged
Conversation
) * docs: add ht-fork documentation, branding, and discussion links Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * convert: support LoRA conversion for MLA kv_b_proj Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * ci: add fork sync automation * feat: add --remap-developer-role flag to translate developer→system Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: support LCO-Embedding-Omni (Qwen2.5 Omni Thinker) GGUF conversion Register Qwen2_5OmniThinkerForConditionalGeneration architecture for text and mmproj GGUF conversion. Handle config structure difference where the Thinker-only variant has vision/audio configs at the top level. Add pooling type detection for embedding use cases. Fix audio tensor routing to base MmprojModel class. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * ci: add ht branch to flake8 lint workflow triggers * feat: welcome agentic contributions, remove upstream AI restrictions - Delete AGENTS.md (upstream's anti-AI contributor guidelines) - Replace restrictive AI Usage Policy with welcoming Agentic Contributions section - Update README to highlight fork's pragmatic stance on AI contributions Unlike upstream, we evaluate code by quality, not by how it was written. --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* webui: add cancel button for in-progress model loading Allow users to cancel a model that is stuck loading or taking too long in the router mode model selector. The cancel button appears next to the loading spinner in both the model selector dropdown/sheet trigger and within individual model option rows. Uses the existing /models/unload endpoint which already supports unloading models in LOADING state. The frontend polling loop is interrupted via AbortController to prevent stale error toasts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * webui: add cancelling state indicator and fix cancel polling - Show orange "Cancelling" indicator with spinner while cancel is in progress - Poll until server confirms model is no longer in LOADING state before clearing the cancelling indicator - Guard against redundant unload calls on already-unloaded models - Keep loadingModelId alive during cancel so selector trigger shows the cancelling state correctly Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(webui): color-coded spinners for model load/unload/cancel states - Loading: green spinner, clockwise - Unloading: red spinner, reverse direction with "Unloading" label - Cancelling: orange spinner, reverse direction - Track unloading state separately in models store Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(webui): address PR review feedback for cancel model loading - Remove duplicated cancel logic from ModelsSelector and ModelsSelectorSheet by deriving loading/cancelling state from the store (issue #1) - Fix race condition: no longer set isLoadingModel=false before cancel completes, preventing brief UI flash (issue #2) - Add MAX_CANCEL_POLL_ATTEMPTS (60) timeout to cancel polling loop to prevent infinite polling if server never transitions (issue #3) - Replace div cancel buttons with proper <button> elements for keyboard accessibility and screen reader support (issue #4) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CUDA dequantize kernels with 128x128 Householder rotation (inverse) - CUDA quantize kernels (two-pass: norm reduction + rotate/quantize/pack) - Rotation matrix generated host-side, uploaded to device global memory - Lazy initialization on first use - Registered in CUDA backend: type traits, CPY dispatch, convert dispatch - Flash attention uses dequant-before-FA path (graph-level cast) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add TBQ3_0/TBQ4_0 dispatch in SET_ROWS using bulk quantize via cpy path - Copy row indices to host for indirection (needed for rotation-based quant) - Disable CUDA graph capture when TBQ SET_ROWS nodes are present (cudaStreamSynchronize is incompatible with graph capture) - Re-enable TBQ in SET_ROWS op support check Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace host-device sync SET_ROWS path with fully on-device fused kernel: - Reads row indices directly on GPU (no cudaMemcpy/cudaStreamSynchronize) - Fused norm computation + rotation + quantization + packing per row - CUDA graph compatible — re-enable graph capture for TBQ types Performance improvement (Qwen2.5-3B, RTX 3090): - pp512: 342 -> 2,109 t/s (6.2x faster, now 2.1x slower than f16) - tg128: 3.6 -> 7.7 t/s (2.1x faster) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
marksverdhei
force-pushed
the
feat/turboquant-kv-cache
branch
from
March 31, 2026 11:50
e87ebe2 to
2254e22
Compare
Author
Performance & Quality Review — TurboQuant KV CacheSpeed Regression Root Cause (CRITICAL)The 9.5x token generation slowdown (7.7 vs 73.5 t/s) is caused by full KV cache dequantization in global memory before attention ( k = ggml_cast(ctx0, k, tbq_attn_type); // dequant ENTIRE K cache to f16
v = ggml_cast(ctx0, v, tbq_attn_type); // dequant ENTIRE V cache to f16Three compounding factors:
pp512 is "only" 2.1x slower because dequant cost amortizes across the batch. q4_0 loses only 2% because its dequant is one multiply per element with no rotation. Task Quality: CORRECT
Security: ADEQUATE
Minor issues:
Optimization Path Assessment
Unchecked Test Items
VerdictSolid correctness baseline — numerical implementation is right, security is adequate. Not production-ready due to the 9.5x tg regression. The rotated-query vec_dot optimization should be the next priority, as it would fundamentally change the performance profile. Recommend merging to ht as a foundation, with a follow-up PR for the optimized attention path. 🤖 Review by ht-fork-manager agent |
…leak Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.
Summary
What is TurboQuant?
Google Research paper (ICLR 2026) for KV cache compression. Decomposes vectors into norm + unit direction, applies random orthogonal rotation (Householder QR), then scalar Lloyd-Max quantization. Achieves 3-5x KV cache compression with minimal quality loss.
Changes
Cherry-picked from upstream PR ggml-org#21089 (elusznik):
GGML_TYPE_TBQ3_0(3.06 bpw) andGGML_TYPE_TBQ4_0(4.06 bpw) type definitionsblock_tbq3_0(98 bytes/256 elem),block_tbq4_0(130 bytes/256 elem)Security hardening (ht-specific):
GGML_ASSERTNULL checks after all malloc calls in turboq codeCUDA backend (ht-specific):
Benchmark Results (Qwen2.5-3B Q4_K_M, RTX 3090, FA=on, ngl=99)
TBQ4_0 prompt processing is 2.1x slower than f16 baseline with 3.9x memory compression. Token generation is ~9.5x slower due to full KV cache dequant in the attention path.
Future optimization opportunities:
dot(q, k) = dot(Q*q, codebook_values)— rotate query once, then cheap codebook dot productsSecurity Audit
Foreign code (upstream PR ggml-org#21089) audited before integration:
References
Test plan
🤖 Generated with Claude Code