← All posts

Can 4-bit keys and 2-bit values hit a 1M-token memory budget? Simulated KV cache, 4B model, 64K

No. Every arm with 2-bit values was far worse than plain 4-bit keys and values: the target arm at 3.125 bits per value had mean KL 0.0959 vs 0.0251, worse in all 6 spans. Even rotated 3-bit values with group-128 scales (3.625 bits) lost in all 6. Rotated 4-bit keys on their own cost little (0.0165), so the values are where the budget breaks.

ExperimentE22L
Date2026-10-30
BenchmarkNext-token KL against the same model with an unquantized cache, plus top-1 agreement. Six WikiText-2 test spans (the 4,096 tokens ending at token 72K, 108K, 144K, 180K, 216K and 252K of the Hugging Face tokenization), each scored with 65,536 tokens of context before it, chunked prefill.
ModelQwen3.5-4B in Hugging Face transformers, NF4 weights (bitsandbytes), bf16 compute. Reference is the same model with an unquantized bf16 cache.
HardwareOne of my dedicated test rigs: RTX 3080 10 GB, 128 GB system RAM
VerdictFAIL

What I tested

Quantize then dequantize post-RoPE keys and values as they enter the cache, on the 8 full-attention layers only, absmax quantization with an fp16 scale per group. 'Rotated' means a 256-wide Hadamard rotation before quantizing. Fitting a 1M-token cache on a 10 GB card needs roughly 3.1 bits per value, so the arms use larger groups (64 or 128 values per scale) to cut scale overhead: A rotated 4-bit keys and 2-bit values, group 128 (3.125 bits); B the same with group 64 (3.25); C 4-bit keys with rotated 2-bit values, group 32 (3.5); D rotated 4-bit keys and 3-bit values, group 128 (3.625); plus a keys-only arm to isolate the key side. Bits per value are averaged over keys and values and include the scales.

Pass bar, set before the run

Set before the run: the 1M budget counts as reachable with these known methods if arm A has mean KL at or below plain 4-bit keys and values (group 32, 4.5 bits) in at least 5 of 6 spans. If A fails but B or C passes, report the bits needed. If all fail, the next round targets values below 3 bits.

Result

ArmBits per valueMean KLTop-1 agreementAt or below plain 4-bitPer-span KL (72K to 252K)
Plain 4-bit keys and values (group 32)4.50.025195.86%-0.0302 / 0.0460 / 0.0272 / 0.0137 / 0.0074 / 0.0263
A: rotated 4-bit keys + rotated 2-bit values, group 1283.1250.095988.88%0 of 60.0956 / 0.1233 / 0.1119 / 0.0830 / 0.0689 / 0.0928
B: rotated 4-bit keys + rotated 2-bit values, group 643.250.089989.41%0 of 60.0794 / 0.1333 / 0.1103 / 0.0755 / 0.0613 / 0.0796
C: 4-bit keys + rotated 2-bit values, group 323.50.071590.96%0 of 60.0627 / 0.1157 / 0.0859 / 0.0560 / 0.0435 / 0.0650
D: rotated 4-bit keys + rotated 3-bit values, group 1283.6250.032494.00%0 of 60.0305 / 0.0478 / 0.0394 / 0.0220 / 0.0175 / 0.0369
Keys only: rotated 4-bit, group 128 (values unquantized)-0.016596.44%6 of 60.0207 / 0.0281 / 0.0204 / 0.0084 / 0.0052 / 0.0164

Arm A was worse than plain 4-bit in all 6 spans, about 3.8 times the KL, so the pre-set bar was not met. B and C also lost in all 6, so none of these known setups reaches the 1M budget.

The keys-only arm (rotated 4-bit keys, group 128, values left unquantized) had mean KL 0.0165, below plain 4-bit in 6 of 6. Larger key groups are cheap once keys are rotated, so the loss comes from the 2-bit values.

Even 3-bit rotated values with group-128 scales (arm D) lost in all 6 spans, by a small margin in the 72K and 108K spans and by more elsewhere.

The 2-bit grid here is the ggml-style signed absmax grid (levels -2, -1, 0, 1 times the scale), which leaves one side with fewer levels. The next run checks whether a symmetric grid or Gaussian Lloyd-Max levels on rotated values close the gap.

The plain 4-bit arm reproduces E22i, E22j and E22k exactly (0.0251).

This is a simulation in Hugging Face transformers, not llama.cpp, so it says nothing yet about speed or real memory use.

Per-span numbers for every arm, with p99 KL, top-1 agreement, perplexity and timing, are in results.csv in the code folder.

What happened

Seven arms over six spans at 64K took about 28 minutes. The goal was to see whether plain known tricks (rotation, uneven key and value bits, bigger quantization groups) get the cache down to the roughly 3.1 bits per value a 1M-token context needs on a 10 GB card. They don't: every 2-bit value arm roughly tripled or quadrupled the KL of plain 4-bit and dropped top-1 agreement from about 96% to 89% to 91%. Keys at 4 bits with large groups were fine once rotated, so the problem is narrowed to the values. The next run tests better 2-bit and 3-bit value grids before any new method is tried.

How to rerun

python e22l.py smoke
python e22l.py

Code

All code for E22L: e22l.py, results.csv.

Prior work

No novelty claim. This is a baseline; uneven key and value bit widths (for example KIVI and KVTuner), group-wise scales and Hadamard rotation before quantization are all known.

Written by Andrew Rose with help from AI tools. Every number comes from the run logs.

Questions or ideas? me@andrewroseit.com or @AndrewRoseIT.