KV Cache API¶
Byte layouts for compressed KV blocks and pages.
estimate_persistent_bytes ¶
Estimate persistent cache bytes for a geometry/layout pair.
Source code in turboquant/kv/layout.py
Grouped value quantization and packed storage.
quantize_values ¶
quantize_values(values: Tensor, *, bits: int, group_size: int = 32, scale_layout: ScaleLayout = 'group', allow_v3: bool = False, scale_dtype: dtype = torch.float16) -> ValueData
Quantize values with per-group or per-token affine metadata.
Source code in turboquant/kv/values.py
dequantize_values ¶
Reconstruct values from packed affine metadata.
Source code in turboquant/kv/values.py
value_data_nbytes ¶
Return byte counts for a ValueData payload.
Source code in turboquant/kv/values.py
value_formula_nbytes ¶
value_formula_nbytes(shape: tuple[int, ...], *, bits: int, group_size: int, scale_dtype_bytes: int = 2, zero_dtype_bytes: int = 2, scale_layout: ScaleLayout = 'group') -> dict[str, int]
Compute expected value bytes for a shape without allocating tensors.
Source code in turboquant/kv/values.py
TurboQuant compressed KV store - owns the quantized historical segment.
Design rules: - Chunks are stored in lists; concatenation is deferred (lazy flatten). - Flat cache is materialized on first read and invalidated on write. - No per-token overhead; all writes are chunk-based.
ValueQuantized
dataclass
¶
Quantized value cache (bit-packed).
FlatCache ¶
Bases: NamedTuple
Flattened view of compressed KV for fast read access.
CompressedKVStore ¶
CompressedKVStore(head_dim: int, num_kv_heads: int, key_bits: int = 3, value_bits: int = 2, value_group_size: int = 32, device: device = None, layer_idx: int = 0)
Chunked compressed KV store with lazy flattening.
Keys are quantized via TurboQuantCompressorV2 (unbiased inner-product estimator). Values use TurboQuantCompressorMSE. Chunks are kept in lists until a flat view is requested.
Source code in turboquant/kv/store.py
append_chunk ¶
Quantize and store a chunk of KV pairs.
key/value: (chunk_len, num_kv_heads, head_dim)
Source code in turboquant/kv/store.py
get_flat_cache ¶
Return a flattened view of all compressed tokens. Cached until next write.
Source code in turboquant/kv/store.py
memory_bytes ¶
Estimate GPU memory used by compressed data.
Source code in turboquant/kv/store.py
TurboQuantKVCache ¶
TurboQuantKVCache(head_dim: int, key_bits: int = 3, value_bits: int = 2, value_group_size: int = 32, buffer_size: int = 128, device: device = None, dtype: dtype = torch.float16, layer_idx: int = 0)
KV cache using TurboQuant for keys and group quantization for values.
Drop-in replacement concept for a standard KV cache with compression.
Usage: cache = TurboQuantKVCache(head_dim=128, key_bits=3, value_bits=2)
# During prefill:
cache.prefill(key_states, value_states)
# During decode (one token at a time):
cache.append(new_key, new_value)
# Compute attention:
scores = cache.attention_scores(query_states)
output = cache.attend(query_states, scores_after_softmax)
Source code in turboquant/kv/store.py
prefill ¶
Process prefill tokens.
Args: keys: (batch, n_heads, seq_len, head_dim) values: (batch, n_heads, seq_len, head_dim)
Source code in turboquant/kv/store.py
append ¶
Append a single decode token.
Args: key: (batch, n_heads, 1, head_dim) value: (batch, n_heads, 1, head_dim)
Source code in turboquant/kv/store.py
attention_scores ¶
Compute attention logits using asymmetric estimator.
Args: query: (batch, n_heads, n_q, head_dim) scale: attention scale factor (default: 1/sqrt(head_dim))
Returns: scores: (batch, n_heads, n_q, seq_len)
Source code in turboquant/kv/store.py
attend ¶
Compute attention output: out = softmax(scores) @ values.
Args: attn_weights: (batch, n_heads, n_q, seq_len) - already softmaxed
Returns: output: (batch, n_heads, n_q, head_dim)
Source code in turboquant/kv/store.py
memory_bytes ¶
Estimate memory usage of the cache.
Source code in turboquant/kv/store.py
get_seq_length ¶
unpack_value_data ¶
Unpack bit-packed value data to uint8 per-element.
Source code in turboquant/kv/store.py
quantize_value_chunk ¶
Symmetric group quantization for value vectors.
Args: v: (..., seq_len, d) value vectors bits: quantization bits (2 or 4) group_size: number of elements per quantization group
Source code in turboquant/kv/store.py
dequantize_value_chunk ¶
Dequantize value vectors from bit-packed format.
Source code in turboquant/kv/store.py
Production compressors for TurboQuant KV cache.
This module provides the main compressor implementations: - TurboQuantCompressorV2: Full compressor with asymmetric attention score estimation - TurboQuantCompressorMSE: Simpler MSE-only compressor for values - MSECompressor: Single-stage MSE-optimal compressor (no QJL) - TurboQuantV3: Community-informed compressor with residual windowing
Key insight from the TurboQuant paper:
~=
+ ||r_k|| * sqrt(pi/2)/m * <S@q, sign(S@r_k)>
This is unbiased with variance O(1/d), even though k_mse itself has high per-vector error. The estimator works because QJL corrects the bias in the inner product space, not in the vector space.
CompressedKeys
dataclass
¶
CompressedKeys(indices: Tensor, norms: Tensor, qjl_signs: Tensor, residual_norms: Tensor, original_dtype: dtype = torch.float16)
Compressed key representation for asymmetric attention.
CompressedValues
dataclass
¶
Compressed value representation.
TurboQuantCompressorV2 ¶
Compressor supporting direct inner product computation without full decompression.
Stores compressed representations AND supports asymmetric attention scores. This is the full TurboQuant algorithm from the paper.
Source code in turboquant/kv/compressors.py
compress ¶
Compress states: (batch, heads, seq, head_dim) -> CompressedKeys.
Source code in turboquant/kv/compressors.py
decompress ¶
Decompress to full tensors (MSE component only).
Source code in turboquant/kv/compressors.py
asymmetric_attention_scores ¶
Compute attention scores directly from compressed K.
Uses the asymmetric estimator:
~=
+ ||r_k|| * sqrt(pi/2)/m * <S@q, signs_k>
Args: queries: (batch, heads, seq_q, head_dim) compressed: CompressedKeys from compress()
Returns: scores: (batch, heads, seq_q, seq_k)
Source code in turboquant/kv/compressors.py
TurboQuantCompressorMSE ¶
Simpler MSE-only compressor for values (no QJL needed).
Source code in turboquant/kv/compressors.py
compress ¶
Compress states: (batch, heads, seq, head_dim) -> CompressedValues.
Source code in turboquant/kv/compressors.py
decompress ¶
Decompress back to full tensors.
Source code in turboquant/kv/compressors.py
MSECompressor ¶
Single-stage MSE-optimal compressor with bit packing.
Used for both keys and values. No QJL — all bits go to reconstruction quality. This is the core building block for TurboQuantV3.
Source code in turboquant/kv/compressors.py
compress ¶
Compress (B, H, S, D) -> dict with bit-packed indices + norms.
Source code in turboquant/kv/compressors.py
decompress ¶
Decompress back to (B, H, S, D) tensor.
Source code in turboquant/kv/compressors.py
memory_bytes ¶
Actual memory usage in bytes.
Source code in turboquant/kv/compressors.py
TurboQuantV3 ¶
TurboQuantV3(head_dim: int, key_bits: int = 4, value_bits: int = 2, residual_window: int = 128, layer_idx: int = 0, n_layers: int = 36, protected_layers: int = 4, protected_bits: int = 8, seed: int = 42, device: str = 'cpu')
Community-informed KV cache compressor.
Key improvements over V2: - MSE-only: no QJL, all bits go to reconstruction quality - Asymmetric: separate bit-widths for keys vs values - Residual window: recent tokens kept in fp16 - Layer-adaptive: configurable per-layer bit overrides
Usage: compressor = TurboQuantV3(head_dim=128, key_bits=4, value_bits=2, device="cuda") compressed_k, compressed_v = compressor.compress_kv(keys, values) keys_out, values_out = compressor.decompress_kv(compressed_k, compressed_v)
Source code in turboquant/kv/compressors.py
compress_kv ¶
Compress key and value tensors.
Input: keys, values - both (B, H, S, D)
If S > residual_window, the last residual_window tokens are kept
in fp16 (uncompressed) for generation quality.
Source code in turboquant/kv/compressors.py
decompress_kv ¶
Decompress back to full tensors.
Source code in turboquant/kv/compressors.py
memory_bytes ¶
Report actual memory usage including residual window.
Source code in turboquant/kv/compressors.py
KV cache byte accounting.
ByteLedger ¶
bytes_from_tensors ¶
report_from_components ¶
report_from_components(*, dense_kv_bytes: int, compressed_k: Tensor | None = None, compressed_v: Tensor | None = None, scales: Tensor | None = None, zeros: Tensor | None = None, norms: Mapping[str, Tensor] | Tensor | None = None, centroids: Mapping[str, Tensor] | Tensor | None = None, rotation: Mapping[str, Tensor] | Tensor | None = None, metadata_bytes: int = 0, recent_window: Tensor | None = None, boundary: Tensor | None = None, sparse: Tensor | None = None, outlier: Tensor | None = None, workspace: Mapping[str, Tensor] | Tensor | None = None, peak_allocated_bytes: int = 0, formula_total_bytes: int = 0) -> MemoryReport
Build a MemoryReport from actual tensors.