Il modello entra in memoria, ma quattro conversazioni no
Un assistente aziendale risponde correttamente a una domanda breve. Poi quattro persone gli inviano documenti lunghi: la memoria disponibile finisce prima che le risposte siano complete. Il file del modello non è cambiato. Che cosa sta crescendo? Una parte importante è la memoria del contesto: durante la generazione il modello conserva rappresentazioni del testo già elaborato, per poterle riutilizzare. La domanda è capire quanto costano queste rappresentazioni e perché il numero delle conversazioni conta insieme alla loro lunghezza.
Abstract. Deriviamo il costo della cache key-value per un decoder causale con attenzione densa, partendo dalla forma dei dati. Un esempio sintetico con 32 strati e otto teste KV richiede 4 GiB per quattro sequenze da 8.192 token. Confrontiamo condivisione delle teste, prefissi comuni, quantizzazione e un bilancio di capacità che include anche pesi e spazio di lavoro. Tutti i numeri sono conteggi, non misure di memoria allocata o latenza. Il risultato permette di capire il vincolo, ma non certifica che una GPU o un runtime specifici possano eseguire il carico.
Che cosa viene conservato, e perché
Un modello autoregressivo produce un token alla volta, usando il contesto precedente. Nell’attenzione, il token corrente genera una query, cioè una rappresentazione con cui confrontare il passato. I token già elaborati forniscono chiavi, o keys, per quel confronto e valori, o values, da combinare. Non sono frasi copiate in una rubrica: sono vettori numerici interni al modello. Le diverse teste di attenzione eseguono più confronti in parallelo, con rappresentazioni diverse.
Nel decoder causale che consideriamo, un token precedente non può vedere quelli futuri. A modello e convenzioni di posizione fissi, le sue chiavi e i suoi valori possono essere riutilizzati ai passi successivi. La cache KV conserva questi dati per ciascuno strato. Evita di ricalcolarli, ma non elimina il confronto della nuova query con il passato. Il testo continua quindi ad avere un costo anche dopo essere stato letto: ciò che risparmiamo in ricalcolo viene in parte conservato in memoria.
Contare i numeri prima di contare i gigabyte
Definiamo L come numero degli strati che conservano la cache, B come numero delle sequenze simultanee, T come token conservati per sequenza, H_KV come numero delle teste di chiavi e valori, d come dimensione di ogni testa e s come byte per numero. Assumiamo la stessa lunghezza T e la stessa configurazione per tutti gli strati; nessuna condivisione dei prefissi e nessuna compressione. Un tensore K per strato ha B×T×H_KV×d elementi, e V ne ha altrettanti.
Il fattore 2 conta K e V; L ripete il conteggio per ogni strato. Ogni altro fattore descrive una dimensione fisica dei tensori. È memoria dei dati della cache, non memoria totale del processo. Non include i pesi appresi, le attivazioni temporanee, il vocabolario dei logit, i metadati del gestore di memoria o le riserve del runtime. Una formula utile deve dire anche che cosa lascia fuori.
Il caso numerico: quattro documenti da 8.192 token
Scegliamo L=32, B=4, T=8.192, H_KV=8, d=128 e s=2, cioè due byte per elemento. Sono parametri illustrativi, non la scheda tecnica di un modello nominato. Sostituendo nella formula otteniamo 4.294.967.296 byte, esattamente 4 GiB. Un GiB vale 2³⁰ byte: non è lo stesso di un GB decimale, pari a un miliardo di byte. Per una sola sequenza lo stesso conto dà 1 GiB.
Ogni token aggiuntivo costa 2×32×8×128×2=131.072 byte per sequenza, ossia 128 KiB. Se tutte e quattro le conversazioni crescono di un token, il totale aumenta di 512 KiB. T comprende ciò che il runtime mantiene, quindi il prompt più i token generati ancora conservati; non soltanto il testo inviato all’inizio. Un budget costruito sulla sola domanda può essere superato mentre il modello completa una risposta lunga.
| Sequenze B | Token T | Teste KV | Cache GiB |
|---|---|---|---|
| 1 | 8192 | 8 | 1 |
| 4 | 8192 | 8 | 4 |
| 4 | 8192 | 32 | 16 |
| 4 | 16384 | 8 | 8 |
Perché contano le teste KV, non soltanto quelle delle query
Non sempre ogni testa di query possiede chiavi e valori separati. Nell’attenzione multi-head classica, MHA, i conteggi coincidono. Nella grouped-query attention, GQA, più teste di query condividono una testa KV. Nel nostro confronto manteniamo 32 teste di query ma riduciamo le teste KV da 32 a 8: la cache scende da 16 a 4 GiB. La riduzione deriva direttamente dal numero di vettori distinti da conservare. Non significa che si possano cancellare tre quarti dei tensori di un modello arbitrario senza modificarne il comportamento.
Il lavoro GQA di Ainslie e colleghi è pubblicato a EMNLP 2023; abbiamo letto metodi, esperimenti e limiti della versione arXiv v3 del 23 dicembre 2023. Gli esperimenti riguardano T5 encoder-decoder, con misure temporali su TPUv4 e adattamento dopo la conversione. Non trasferiamo quei risultati a una GPU o a un modello decoder-only e non presentiamo GQA come una novità del 2026. Qui usiamo la distinzione architetturale per un conto di memoria verificabile.

Leggere la pendenza significa chiedersi quanto costa allungare il contesto. Raddoppiare T raddoppia questa parte di memoria; raddoppiare B fa altrettanto. Non stiamo rappresentando una matrice T×T dei punteggi di attenzione. Evitare di materializzare quella matrice e conservare una cache KV sono problemi distinti: un’implementazione efficiente dell’attenzione non rende automaticamente gratuito il passato conservato.
Un bilancio completo cambia il numero di richieste ammesse
Aggiungiamo un modello ipotetico di sei miliardi di parametri, tutti a due byte. I soli pesi occupano 12 miliardi di byte, circa 11,1759 GiB. Supponiamo inoltre una riserva di 2 GiB per il resto dell’esecuzione e una capacità disponibile di 16 GiB. Anche questi sono numeri scelti, non misurati. Con quattro sequenze il totale è 11,1759+2+4≈17,1759 GiB: non entra nel budget. Con tre è circa 16,1759, ancora troppo; con due è 15,1759.
Dire che due sequenze “entrano nel conto” non è promettere che funzionino. La riserva reale dipende da runtime, kernel, fase di lettura del prompt, dimensioni intermedie, frammentazione e altre allocazioni. Il conteggio è una condizione di pianificazione utile, da confrontare con il picco misurato. Non abbiamo eseguito il modello, individuato una GPU o misurato quel picco; il JSON chiama esplicitamente il risultato arithmeticFits, compatibilità aritmetica.
Quattro copie dello stesso prefisso sono sempre necessarie?
Le quattro conversazioni possono iniziare con le stesse istruzioni e lo stesso documento. Se un runtime consente la condivisione sicura di un prefisso immutabile, possiamo contare quel prefisso una volta e i suffissi separatamente. Poniamo P=4.096 token comuni su T=8.192 totali per ciascuna sequenza. Senza condivisione conserviamo BT=32.768 posizioni; con condivisione ideale:
Il risparmio teorico è 1,5 GiB rispetto ai 4 precedenti. Non basta però vedere lo stesso testo sullo schermo: devono coincidere token, pesi e adattatori usati, posizioni e condizioni di attenzione che determinano la cache. Un cambiamento nelle istruzioni precedenti può cambiare anche le rappresentazioni successive. La gestione deve inoltre separare i suffissi quando le risposte divergono. Non abbiamo implementato tale runtime; il conto indica il massimo beneficio del caso ipotizzato, prima di metadati e blocchi di allocazione.
Quantizzare la cache non equivale a dimezzare tutta la memoria
Nel caso iniziale ogni numero KV occupa due byte. Se ipotizziamo una rappresentazione a otto bit, il solo payload dei dati scende da 4 a 2 GiB. Ma una quantizzazione può richiedere scale e altri metadati. Nel nostro schema puramente contabile aggiungiamo una scala di due byte ogni 64 valori e nessun punto zero: l’overhead è 2/64 byte per valore. Il totale diventa 2×(1+2/64)=2,0625 GiB, non esattamente 2.
Questa è una formula di spazio, non un algoritmo di quantizzazione valutato. Non abbiamo quantizzato tensori reali, misurato errori sulle risposte o dimostrato accelerazioni. Eventuali copie temporanee dequantizzate possono influire sul picco. Inoltre dimezzare KV lascia intatti, nel nostro confronto, pesi e riserva: il risparmio percentuale sul totale è minore. La scelta richiede una verifica numerica e applicativa del metodo concreto.
Che cosa cambia nel runtime, e che cosa non segue dal conto
La documentazione Hugging Face descrive cache dinamiche, statiche e a finestra scorrevole. Le prime crescono; una cache statica può riservare la capacità prima che tutti i token siano presenti; una finestra limita ciò che resta conservato. Abbiamo letto anche la gestione delle maschere e il ciclo di generazione. Qui non abbiamo eseguito quei componenti. La formula con T va adattata alla lunghezza realmente allocata e agli strati effettivamente coinvolti.
Una finestra più corta non è una compressione gratuita della stessa informazione: elimina accesso diretto a token precedenti, secondo l’architettura e la politica adottata. Spostare la cache fuori dalla memoria dell’acceleratore modifica dove stanno i dati, non ne annulla il volume, e può aggiungere trasferimenti. Analogamente, avere quattro volte meno dati KV non garantisce quattro volte meno latenza: proiezioni, calcolo dell’attenzione, banda, sincronizzazione e implementazione contribuiscono al tempo complessivo.
La risposta e il protocollo che manca per una misura reale
Il testo già letto occupa memoria perché il modello conserva rappresentazioni utili ai token successivi, in ogni strato e per ogni sequenza. Nel nostro esempio quella memoria raggiunge 4 GiB prima di contare i pesi; aggiungendo il resto, quattro richieste superano il budget ipotetico di 16 GiB. Il punto operativo è dimensionare insieme lunghezza, concorrenza e rappresentazione della cache, non guardare soltanto la dimensione del modello scaricato. È una considerazione progettuale per assistenti aziendali, non la descrizione di un’infrastruttura EL-AI misurata.
Per passare dal conto a una prova servirebbe fissare checkpoint, tokenizer, precisioni di pesi e KV, GPU, runtime e versione, backend di attenzione, numero di richieste, lunghezze di ingresso e uscita e strategia di allocazione. Si misurerebbero separatamente memoria allocata e riservata, picco durante il prompt e durante la generazione, latenza e throughput. Questo protocollo è proposto, non eseguito. Il nostro pacchetto contiene invece conti interi deterministici, senza seed, assert sui GiB e dati delle figure: ogni configurazione richiede O(1) operazioni aritmetiche, senza simulare i tensori.
Fonti primarie e codice
Hugging Face Transformers — How caching works.
Ainslie, Lee-Thorp, de Jong, Zemlyanskiy, Lebrón, Sanghai — GQA, arXiv v3, 23 December 2023.
GQA — EMNLP 2023, ACL Anthology.
GiB = 2**30
layers, kv_heads, head_dim, bytes_per_value = 32, 8, 128, 2
def cache_bytes(batch, tokens):
return 2 * layers * batch * tokens * kv_heads * head_dim * bytes_per_value
for batch in (1, 2, 3, 4):
kv = cache_bytes(batch, 8192)
total = 6_000_000_000 * 2 + 2 * GiB + kv
print(batch, kv/GiB, total/GiB, total <= 16*GiB)
Codice, dati e istruzioni · JSON. Calcoli didattici eseguiti con Python 3.14.0; figure con Matplotlib 3.11.2. Analisi con assistenza AI, senza dichiarare peer review o revisione umana. Copertina originale ImageGen, illustrativa: non documenta persone, sedi o installazioni EL-AI. Fonti consultate il 3 ottobre 2026.

