Cos'è la KV-cache, in un paragrafo
Un transformer genera un token alla volta, e ogni nuovo token "presta attenzione" a ogni token precedente. Senza cache, produrre il token 1.000 significherebbe ricalcolare i vettori key e value dei 999 token che lo precedono — a ogni singolo passo. La KV-cache memorizza quei vettori key/value così ciascuno è calcolato una volta e riusato. È l'unica ottimizzazione che rende pratica la generazione — e il prezzo è memoria che cresce con ogni token nel contesto.
La formula
Leggendo ogni termine:
- 2 — un set per le key, uno per i value.
- context_length — i token tenuti in contesto. È il termine che controlli, e scala tutto linearmente.
- n_layers × n_kv_heads × head_dim — l'architettura del modello. Nota n_kv_heads, non le teste di attenzione — è qui che la GQA risparmia memoria (sotto).
- bytes_per_value — 2 per cache FP16, 1 per Q8, ~0,5 per Q4. È la leva della quantizzazione.
- batch — numero di richieste simultanee. Ogni utente concorrente ha bisogno di una cache separata completa — è il termine che tutti dimenticano.
Un esempio pratico: Llama 3.3 70B
Llama 3.3 70B ha 80 layer, 8 teste KV (usa grouped-query attention) e una dimensione di testa di 128. Per una singola richiesta con cache FP16:
Quell'ultima riga è il nocciolo della questione. I pesi Q4 sono ~40GB; a pieno contesto 128K la cache raddoppia il tuo fabbisogno di memoria, portandolo a ~80GB. Ecco perché la nostra guida VRAM insiste nel dimensionare per pesi più cache — e perché il "ci sta" di un calcolatore che conta solo i pesi è una trappola. Ora moltiplica per la concorrenza: dieci utenti simultanei a 32K di contesto ciascuno richiedono dieci cache — ~100GB di sola cache.
Perché la GQA ha cambiato tutto (e le vecchie formule mentono)
Le formule più vecchie della KV-cache usano il numero di teste di attenzione. I modelli moderni usano la
grouped-query attention (GQA), dove molte teste query condividono un piccolo numero di teste
key/value — Llama 70B ha 64 teste query ma solo 8 teste KV. Poiché la cache memorizza le teste KV, la GQA
taglia la dimensione della cache di quel rapporto (8× qui). Una formula pre-GQA predirebbe otto volte la memoria e
ti farebbe scartare per paura un modello che in realtà ci sta. Usa sempre n_kv_heads, che trovi
nel config.json del modello come num_key_value_heads.
Le due leve che rendono sostenibile il contesto lungo
1. Quantizzazione della cache.
La cache non deve essere FP16. Memorizzarla a Q8 (1 byte/valore) dimezza la memoria della cache; Q4 la riduce a un
quarto. In Ollama, attivala con la variabile d'ambiente OLLAMA_KV_CACHE_TYPE=q8_0; in llama.cpp usa
--cache-type-k/--cache-type-v; vLLM e SGLang hanno opzioni di cache FP8. In pratica la
cache Q8 è quasi lossless ed è l'impostazione che rende di più nel lavoro locale a contesto lungo — trasforma il
nostro esempio Llama a 128K da ~40GB di cache a ~20GB.
2. Dimensiona giusto la finestra.
Un modello che pubblicizza 256K di contesto non ti obbliga ad allocare 256K. Imposta il contesto al tuo bisogno
reale: un assistente di chat raramente usa più di 8–16K; un sistema document-RAG dimensionato al suo
budget di chunk potrebbe servire 32K, non 256K. In Ollama è num_ctx; in vLLM,
--max-model-len. Allocare margine che non usi mai è pura VRAM sprecata — il rovescio del consiglio
"alza num_ctx" nelle nostre pagine sull'assistente di coding e Qwen3-Coder, dove l'errore va nella direzione opposta.
La checklist pratica
- Dimensiona la VRAM come pesi + KV-cache + ~1–2GB di overhead — mai i soli pesi.
- Calcola la cache sul TUO contesto e sulla TUA concorrenza, non sul massimo del modello.
- Attiva prima la cache Q8 — costa quasi nulla in qualità ed è ciò che libera più spazio in assoluto.
- Per il serving, ricorda che ogni richiesta concorrente moltiplica la cache — è questo a limitare il numero di utenti, non i pesi.
- Preferisci i modelli GQA (praticamente tutti quelli attuali) e leggi num_key_value_heads da config.json per il conto esatto.