kv-cache: per-layer KV types, measured bit allocation - #5
Conversation
Layers do not benefit equally from an extra bit of KV precision, so spending a fixed average budget unevenly beats spending it uniformly. The cache already supported this structurally — tensors are created per layer and every consumer reads the type off the tensor — so this only adds a way to say which layer gets what. Measured on Qwen3.5-9B (8 attention layers). Upgrading one layer at a time to turbo4 produces deltas of -0.0016 to +0.0004 against a 1.5337 all-turbo3 baseline; layer 0 comes out positive, which is impossible and places the noise floor near +/-0.0005. No single number is strong enough to allocate from. The ranking they form is. Two runs at an identical 4.0 bpw differing only in which four layers are upgraded land 0.0030 apart — six times the noise floor. Walking that ranking: four layers capture 85% of the full turbo4 gain at half the extra cost, six reach all of it at 4.25 bpw instead of 4.5. The head_dim guard now checks the effective per-layer type. It previously read the global one, which a per-layer override would have bypassed. An entry count that does not match the number of KV layers still works — the last entry covers the remainder — but warns, since it usually means a list written for a different model. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q5jMcvdevae8j4T36C9h2z
A bit budget can be spent on the K/V axis as well as the layer axis, and --cache-type-k / --cache-type-v have always allowed it. Work on KV quantization usually finds K the more sensitive of the two. Measured at 4.0 bpw, the two directions land 0.0002 apart — noise. Nothing to exploit, which is what the Hadamard transform is for: every output is a signed sum of all 128 inputs, so an outlier channel is spread across the vector rather than dominating one dimension. That leaves the layer axis as the productive one; it beats either K/V split by 0.0026 at identical cost. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q5jMcvdevae8j4T36C9h2z
|
Added a measurement of the other axis a bit budget could go to, since it decides whether this PR is aimed at the right one.
The two K/V directions are 0.0002 apart — noise. No asymmetry to exploit, which is what the Hadamard transform is for: every output is a signed sum of all 128 inputs, so an outlier channel gets spread across the vector instead of dominating one dimension. The asymmetry other methods rely on has already been flattened here. That makes the layer axis the productive one: 0.0026 better than either K/V split at identical cost, five times the noise floor. |
What
LLAMA_KV_TYPE_PER_LAYER— a comma-separated ggml type name per KV layer, so different layers can use different KV quantization. Unset, nothing changes.The cache already created tensors per layer and every consumer reads the type off the tensor, so the change is small: pick a type per layer, and check the TurboQuant head_dim guard against the effective type rather than the global one. That guard was the one real correctness issue — a per-layer override would have bypassed it.
Why, and does it actually help
Measured on Qwen3.5-9B, which has 8 attention layers (the other 24 are linear attention with no KV cache).
Upgrading exactly one layer to turbo4, against a 1.5337 all-turbo3 baseline:
Layer 0 gets worse with more bits, which is impossible — that puts the noise floor around ±0.0005 and makes every individual row too weak to act on.
The ranking is a different matter. Same memory, different allocation:
0.0030 between two configurations costing exactly the same, six times the noise floor. Sorting cancels part of the per-measurement error.
Walking the ranking gives the curve:
Six beating eight is 0.0005 — exactly the noise floor, so that ordering means nothing on its own. But the two layers left out are 0 and 1, the only two whose single-layer measurement came out positive. Two independent measurements agreeing is worth more than either.
Verified
Caveats
🤖 Generated with Claude Code
https://claude.ai/code/session_01Q5jMcvdevae8j4T36C9h2z