Workspace/Lesson workspace
Loading progress
Foundations40 min

KV caches, grouped heads, and compute budgets

Lesson 3 of 3
How to study this lesson

Read one section, trace the worked example, and try the knowledge check. Bookmark saves a shortcut; notes appear in My notebook. Mark lesson complete is your own assessment and does not mark its lab passed.

By the end, you can

  • Estimate KV cache memory with explicit dimensions and units.
  • Distinguish prefill from incremental decoding.
  • Explain what scaling-law evidence does and does not imply.

Prefill and decoding perform different work

When a request starts, the model processes the prompt to construct hidden states and attention keys and values. This prompt-processing stage is often called prefill. During generation, the model repeatedly processes a new token and attends to the previous context. A key/value cache retains earlier representations so they need not be recomputed from scratch at every generation step.

Caching works for a causal decoder because an earlier position's representation does not change when later tokens are appended. The new token still needs to compare its query with relevant cached keys and combine values. Caching removes repeated prefix computation; it does not make long-context attention free. Memory traffic, cache organization, and batch scheduling can strongly influence token-generation latency.

Count the cache before choosing concurrency

For a conventional dense KV layout, cache bytes are approximately 2 times layers times batch size times sequence length times KV heads times head dimension times bytes per element. The factor two accounts for keys and values. This estimate excludes metadata, padding, allocator overhead, temporary activations, and model weights. State those exclusions when using the number for capacity planning.

For 24 layers, batch size 1, 4,096 cached tokens, 8 KV heads, head dimension 64, and two bytes per element, the cache is 201,326,592 bytes, or 192 MiB. Doubling sequence length doubles this cache estimate. Increasing batch size to four quadruples it. A service that fits one request can therefore fail when several long conversations decode concurrently, even though the model weights themselves are unchanged.

Grouped-query attention changes what is stored

In ordinary multi-head attention, each query head has its own key and value head. Multi-query attention shares a single key/value head across query heads. Grouped-query attention lies between these cases: several query heads share each key/value head. The number of query heads can remain unchanged while the cache stores fewer key/value heads.

In the previous example, reducing KV heads from eight to two reduces the approximate cache from 192 MiB to 48 MiB. It does not imply every compute component becomes four times faster, because query projections, feed-forward layers, and other operations remain. Sharing representations is an architectural tradeoff that must be trained or adapted appropriately. You cannot simply discard heads from an arbitrary checkpoint and assume quality is preserved.

Memory and arithmetic are different constraints

Full attention over a prompt has a quadratic number of query-key relationships in sequence length, while each incremental query compares against a growing prefix. Specialized kernels can reduce materialized intermediate memory and improve execution without changing the underlying attention definition. Cache quantization can reduce bytes per stored element, but introduces its own implementation and accuracy considerations.

Model weight memory is another term: a billion parameters at two bytes each require about two billion bytes before extra serving state. Training adds gradients, optimizer states, and activations, so an inference memory estimate does not describe training memory. Keep all units explicit, especially decimal GB versus binary GiB. A clean resource worksheet separates weights, KV cache, temporary memory, and operational headroom.

Scaling laws guide experiments, not guarantees

Empirical scaling research studies how loss changes with model size, training data, and compute within measured regimes. Compute-optimal training results show that allocating a fixed budget only to larger models can undertrain them; model size and token count must be considered together. These are fitted relationships under specific experimental choices, not universal guarantees about every architecture, dataset, or downstream agent task.

The code compares cache estimates for two head layouts and two context lengths. The project extends this into a budget inspector that records assumptions rather than advertising benchmark results. Use the same discipline when reading a scaling paper: identify the objective, data distribution, compute definition, experimental range, and extrapolation. A better language-model loss may help an agent, but end-to-end tool use and task completion still need their own evaluations.

Work through the code

The worksheet uses explicit positive integer dimensions and binary MiB. Reducing KV heads divides the cache term, while doubling tokens doubles it. This is an architectural estimate, not a measurement of a serving engine. Add model-weight and headroom terms before using it for concurrency planning.

m04_lesson_3.py
python
def cache_bytes(layers, batch, tokens, kv_heads, head_dim, element_bytes):
    dimensions = (layers, batch, tokens, kv_heads, head_dim, element_bytes)
    if any(type(x) is not int or x < 1 for x in dimensions):
        raise ValueError("positive integer dimensions required")
    return 2 * layers * batch * tokens * kv_heads * head_dim * element_bytes

layers, head_dim, element_bytes = 24, 64, 2
for tokens in (4096, 8192):
    for heads in (8, 2):
        size = cache_bytes(layers, 1, tokens, heads, head_dim, element_bytes)
        print(f"tokens={tokens} kv_heads={heads} cache={size / 2**20:.1f} MiB")

full = cache_bytes(24, 1, 4096, 8, 64, 2)
grouped = cache_bytes(24, 1, 4096, 2, 64, 2)
print("cache reduction factor:", full // grouped)
print("excludes weights, temporary memory, and allocator overhead")
EXPECTED / ILLUSTRATIVE OUTPUT
tokens=4096 kv_heads=8 cache=192.0 MiB
tokens=4096 kv_heads=2 cache=48.0 MiB
tokens=8192 kv_heads=8 cache=384.0 MiB
tokens=8192 kv_heads=2 cache=96.0 MiB
cache reduction factor: 4
excludes weights, temporary memory, and allocator overhead

Run Python snippets locally with the prerequisites named above. The module coding lab runs directly in your browser.

Pause and reason

A cache budget is 384 MiB. Using the lesson’s 24-layer, 8-KV-head, 64-dimension, two-byte configuration at 4096 tokens, what is the maximum idealized batch size? What if KV heads become two?

Check your understanding

What does a KV cache primarily avoid during incremental generation?

Your notes

Explain the mechanism in your own words. Add a failure you want to test.

Saved notes appear in your notebook

Go deeper with primary sources

Practice this module