Skip to content

transformers: quantized KV-cache storage op (int8/int4)#2458

Open
czoli1976 wants to merge 3 commits into
sonos:mainfrom
czoli1976:feature/kv-cache-storage-quant
Open

transformers: quantized KV-cache storage op (int8/int4)#2458
czoli1976 wants to merge 3 commits into
sonos:mainfrom
czoli1976:feature/kv-cache-storage-quant

Conversation

@czoli1976

@czoli1976 czoli1976 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Adds QuantizedDynKeyValueCache, a drop-in for DynKeyValueCache that stores the resident KV cache in int8/int4 and dequantizes on read — cutting decode KV memory (~2× int8, ~4× int4 vs an f16 cache) while leaving RoPE/mask/attention untouched. It's opt-in via the quantize_kv_storage / quantize_kv_storage_int4 transforms, which walk each Sdpa's K/V inputs back through the cache-read plumbing (broadcast/reshape/cast/on-read RoPE) and swap those caches; because it stays a cache-shaped op, the past-length symbol still resolves.

KIVI layout: Keys per channel (quantized in fixed-size blocks with frozen scales for streaming consistency), Values per token. The first few tokens are kept in f32 (attention sinks carry outsized weight), a constant tiny cost that matters most for int4. Distinct from the block_quant weight-quantization path — this quantizes the activation cache, not the weights.

Validated end-to-end on OpenELM-270M (f16): the transform fires on all layers and runs, and the dequant-on-read adds only ~1–4% to a decode step (the step is FFN-dominated). Perplexity and next-token agreement over an 80-token greedy continuation:

perplexity top-1 agreement vs f16
f16 1.595
int8 1.597 100%
int4 1.592 99%

Both are perplexity-lossless; int4's single top-1 disagreement is a statistical tie (the reference distribution puts the two candidates at p=0.019 vs 0.018). Unit tests cover accumulation vs a plain concat, block-wise per-channel consistency over a growing range, and NNEF round-trip.

🍍

🤖 Generated with Claude Code

czoli1976 and others added 3 commits July 8, 2026 19:47
Add QuantizedDynKeyValueCache, a drop-in for DynKeyValueCache that keeps the
resident KV cache in int8 or int4 (KIVI layout: Keys quantized per channel in
frozen fixed-size blocks, Values per token) and dequantizes back to the input
dtype on read, so downstream RoPE/mask/attention are unchanged and the
past-length symbol still resolves. The opt-in quantize_kv_storage transform
walks each Sdpa's K and V inputs back through the cache-read plumbing and swaps
those caches for the quantized op.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a cost() reporting values-per-token and resident quantized bytes-per-token,
so the memory footprint of a quantized cache shows up in `dump` like the plain
DynKeyValueCache it replaces.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e cache

The first few tokens act as attention sinks whose keys and values carry outsized
weight, so quantizing them costs disproportionate accuracy. Keep the first
SINK_TOKENS in f32 in both the per-token Value store and the block-wise
per-channel Key store — a constant, tiny cost that makes int4 essentially
lossless (perplexity-matched, ~99% top-1 agreement on OpenELM, the remaining
disagreement being a statistical tie in the reference distribution).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant