KIVI
KIVI is VeloxQuant-MLX's re-implementation of the field's most widely-cited KV-cache quantization baseline: "KIVI: A Tuning-Free Asymmetric 2bit Quantization for KV Cache" (Liu, Yuan et al., ICML 2024). It is included so every other algorithm in this library can be measured against a recognized reference point.
:::info Why a baseline? KIVI is not the highest-compression method here (VecInfer-2bit reaches 8× key compression vs KIVI-2bit's ~5.8×). Its value is being calibration-free, deterministic, and the number reviewers expect to see compared against. :::
How it works
KIVI's insight is asymmetry between keys and values:
- Keys are quantized per channel — the quantization group runs along the
token axis, with one
(scale, zero)pair per channel-group. Key distributions have a few high-variance channels, so per-channel scales keep them accurate. - Values are quantized per token — the group runs along the channel axis,
one
(scale, zero)per token-group. - fp16 residual window — the most recent
residual_lengthtokens are kept at full precision. Newly generated tokens dominate attention and are cheap to keep exact; they are quantized only once they age out of the window.
Each group uses asymmetric min/max quantization:
zero = min(group)
scale = (max(group) - min(group)) / (2**b - 1)
q = round((group - zero) / scale) # uint, [0, 2**b - 1]
recon = q * scale + zero
KIVI is fully deterministic — no codebook training, no rotation, no RNG — so it adds no run-to-run variance.
Usage
import mlx_lm
from veloxquant_mlx import KVCacheConfig, KVCacheBuilder
model, tokenizer = mlx_lm.load("mlx-community/Llama-3.2-3B-Instruct-4bit")
config = KVCacheConfig(
method="kivi",
bit_width_inlier=2, # KIVI default
kivi_group_size=32, # min/max group size
residual_length=32, # recent tokens kept in fp16
)
caches = KVCacheBuilder.for_model(model, config)
model.make_cache = lambda *_a, **_k: caches
response = mlx_lm.generate(model, tokenizer, prompt="...", max_tokens=120)
Measured results
Apple M4, max_tokens≈120, residual_length=32, long-context prompt.
Source: figures/kivi/<model>/results.json.
| Model | KIVI-2bit key comp. | full-KV comp. | throughput vs fp16 |
|---|---|---|---|
| Llama-3.2-3B-4bit | 5.79× | 3.98× | 102% |
| Qwen2.5-7B-4bit | 5.78× | 3.98× | 100% |
| Mistral-7B-4bit | 5.76× | 4.03× | 106% |
Full-KV compression includes the fp16 residual window, so it is not inflated.
Honest scope
:::warning Memory, not raw speed; storage, not peak
- KIVI's published speedup comes from a CUDA kernel that does not port to Metal. On Apple Silicon the win is memory; throughput is at-or-near fp16 because the min/max arithmetic is cheap on a memory-bound decode path.
- Compression only manifests once context exceeds the residual window — at short prompts the whole prefill stays fp16 and the realized ratio is 1.0× (correct behavior, not a bug).
- Peak runtime memory is not reduced (sometimes marginally higher): keys are dequantized to fp16 before SDPA, so the compression is in cache-storage accounting, not the peak fp16 working set.
- At 2 bits, raw-key reconstruction cosine on synthetic unit-norm Gaussian keys is ~0.93 — KIVI 2-bit is genuinely lossy, which is exactly why the fp16 residual window exists. :::
See figures/kivi/fig4_vs_existing.png for the KIVI-vs-VecInfer comparison.
See also: NSNQuant — the other residual-window wrapper; it differs because it adapts the data to a fixed universal codebook (NSN + Hadamard Gaussianization) rather than fitting scalar min/max scales to the data.