KV cache su disco: esattezza, ripristino e persistenza
Nel turno 2, il client invia l’intera conversazione ("Ciao" + "Ciao!" + "Come stai?"). Il server dovrebbe fare il prefill di tutti questi token da zero, anche se i primi due messaggi sono identici al turno 1.
Senza cache KV su disco, il server sprecherebbe tempo e potenza di calcolo. Con la cache, riconosce che il prefisso "Ciao" + "Ciao!" è già stato elaborato in precedenza, carica lo stato salvato e fa solo il prefill dei nuovi token ("Come stai?").
Come funziona la cache in ds4-server
Section titled “Come funziona la cache in ds4-server”1. Chiave: SHA1 del prompt renderizzato
Section titled “1. Chiave: SHA1 del prompt renderizzato”Il prompt ricevuto dal client viene renderizzato (cioè convertito nel formato che DeepSeek V4 si aspetta, con i tag speciali per i tool, ecc.) e poi trasformato in una stringa di testo (decodifica dei token). Di questa stringa viene calcolato l’SHA1.
Il nome del file cache è <sha1>.kv.
Perché SHA1 della stringa renderizzata e non dei token IDs?
Perché due prompt diversi possono tokenizzare in modo diverso a causa di spazi, maiuscole, ecc. La stringa renderizzata è più stabile e confrontabile. Inoltre, l’utente può ispezionare il contenuto del file .kv con hexdump e leggere il prompt originale.
2. Formato del file .kv
Section titled “2. Formato del file .kv”Un file .kv è composto da:
- Header fisso (48 byte) con magic
KVC, versione, quant dei routed expert, motivo del salvataggio, flag, conteggi. - Testo renderizzato (UTF-8), preceduto dalla sua lunghezza (4 byte).
- Payload DS4 (lo stato della sessione: token IDs, logits, tensori KV raw e compressi).
- Mappa tool-id → DSML (opzionale, se il flag extension bit 0 è impostato).
L’header contiene campi importanti:
| Offset | Campo | Significato |
|---|---|---|
| 0-2 | magic KVC | Identifica il file come cache KV |
| 4 | routed expert quant bits | 2 o 4; usato per evitare di mischiare quant diversi |
| 8 | token count | Numero di token nel checkpoint salvato |
| 12 | hit count | Quante volte questo checkpoint è stato riutilizzato (statistica) |
| 16 | context size | Dimensione del contesto al momento del salvataggio |
| 24 | creation time | Unix timestamp |
| 32 | last-used time | Unix timestamp |
| 40 | payload byte count | Dimensione del payload DS4 |
3. Payload DS4: lo stato della sessione
Section titled “3. Payload DS4: lo stato della sessione”Il payload inizia con un header di 13 interi (magic DSV4, versione, dimensioni) e poi contiene:
- Token IDs del checkpoint (sequenza di
u32) - Logits del token successivo (array di
float32di lunghezzavocab_size) - Tensori KV raw (per la finestra scorrevole recente)
- Tensori KV compressi (per la cache compressa 4:1)
- Tensori dell’indexer (per il meccanismo di compressione)
Importante: I logits vengono salvati insieme al checkpoint. Quando si ripristina la cache, il server ha già pronta la distribuzione di probabilità per il token successivo, senza dover eseguire un passo aggiuntivo di inferenza.
4. Tipi di salvataggio (save reason)
Section titled “4. Tipi di salvataggio (save reason)”Nel campo save reason (offset 5 dell’header) vengono codificate quattro situazioni:
| Valore | Nome | Quando avviene |
|---|---|---|
| 1 | cold | Dopo il primo prefill di un prompt lungo (di solito dopo la prima richiesta) |
| 2 | continued | Quando la generazione o il prefill raggiunge una frontiera assoluta (es. ogni 10k token) |
| 3 | evict | Prima che una nuova sessione sostituisca quella corrente in memoria |
| 4 | shutdown | Quando il server termina pulitamente |
I salvataggi cold sono i più importanti: definiscono un checkpoint di base su cui possono innestarsi i successivi. I salvataggi continued creano checkpoint intermedi durante conversazioni molto lunghe, così se il server si interrompe si può ripartire da uno stato recente.
5. Allineamento e troncamento
Section titled “5. Allineamento e troncamento”Per evitare problemi di confini di tokenizzazione (BPE), i checkpoint non vengono salvati a qualsiasi posizione, ma allineati a multipli di --kv-cache-boundary-align-tokens (default 2048) e troncati di --kv-cache-boundary-trim-tokens (default 32) token dalla fine.
Esempio: se la conversazione ha 10500 token, viene salvato un checkpoint a 10240 token (il multiplo di 2048 inferiore) meno 32 token di trim = 10208 token. In questo modo, se un client aggiunge qualche parola, la nuova stringa renderizzata avrà buona probabilità di avere come prefisso esatto il checkpoint salvato.
Configurazione della cache
Section titled “Configurazione della cache”Ecco i parametri più utili da passare a ds4-server:
./ds4-server \ --kv-disk-dir /tmp/ds4-kv \ --kv-disk-space-mb 8192 \ --kv-cache-min-tokens 512 \ --kv-cache-cold-max-tokens 30000 \ --kv-cache-continued-interval-tokens 10000 \ --kv-cache-boundary-trim-tokens 32 \ --kv-cache-boundary-align-tokens 2048Parametri aggiuntivi:
--tool-memory-max-ids N(default 100000) – controlla la dimensione massima della mappa di replay DSML per i tool call.--kv-cache-reject-different-quant– di default i checkpoint sono riutilizzabili tra quant diversi (es. q2 e q4). Con questo flag, li accetta solo se il quant corrisponde esattamente.
Exact replay dei tool call e cache
Section titled “Exact replay dei tool call e cache”Come accennato nel capitolo 2, ds4 conserva una mappa che lega l’ID univoco di una tool call al blocco DSML esatto che il modello ha generato. Questa mappa viene salvata dentro il file .kv (nella sezione opzionale KTM). Quando un checkpoint viene ripristinato, anche la mappa viene ricaricata in memoria.
In questo modo, anche dopo un riavvio del server, se un client rimanda indietro lo stesso ID di tool call, il server può rigenerare il DSML originale, mantenendo la coerenza della cache KV.
Formato della sezione tool-id map:
magic "KTM" (3 byte)version 1 (1 byte)entry count (4 byte)per ogni entry: - tool id byte length (4 byte) - sampled DSML byte length (4 byte) - tool id (bytes) - sampled DSML (bytes)Esempio pratico di funzionamento
Section titled “Esempio pratico di funzionamento”- Avvi il server con
--kv-disk-dir /tmp/ds4-kv. - Un client (es. Codex CLI) invia una richiesta con un prompt di 5000 token.
- Il server esegue il prefill (tempo: qualche secondo) e poi risponde.
- Terminata la risposta, il server fa un cold save: crea un file
/tmp/ds4-kv/<sha1>.kvcon lo stato dopo i primi 5000 token (allineati e trimmati). - Il client invia una seconda richiesta con la conversazione estesa (5000 token vecchi + 200 nuovi).
- Il server calcola lo SHA1 del prompt renderizzato. I primi 5000 token corrispondono esattamente al checkpoint salvato? Quasi sempre sì (grazie al trim e all’allineamento). Quindi carica il file
.kve ripristina lo stato. - Ora deve solo fare il prefill dei 200 token nuovi e poi generare la risposta. Il tempo di prefill è ridotto a frazioni di secondo.
- Durante la generazione, se si superano i 10000 token totali, fa un continued save (un secondo checkpoint più avanti).
- Se il server viene riavviato, il file
.kvesiste ancora. La prossima volta che un client invia lo stesso prompt (ad esempio un agente che ricostruisce la conversazione), il server lo troverà e ripartirà istantaneamente.
Limitazioni e note
Section titled “Limitazioni e note”- Cache solo per il server, non per il CLI: Il CLI ds4 non usa la cache su disco (ma può salvare le sessioni con
/savenel caso dell’agente nativo, che è un meccanismo diverso). - Una sola sessione attiva in memoria: Il server tiene una sola conversazione attiva alla volta. Quando arriva una richiesta con un prompt diverso, la sessione corrente viene salvata su disco (evict) e sostituita. Questo limita il parallelismo ma semplifica la gestione della memoria.
- I checkpoint cold sono troncati: Come detto, vengono sempre tagliati di
--kv-cache-boundary-trim-tokenstoken per evitare problemi di confine. Questo significa che il checkpoint non include l’ultimo pezzo della conversazione. Va bene così perché quel pezzo verrà ricalcolato come nuovo prefill. - Compatibilità tra versioni di ds4: Il formato
.kvnon è ancora stabile. Aggiornamenti di ds4 potrebbero rendere i vecchi file incompatibili. Se noti comportamenti strani, cancella la directory della cache.
Ispezionare i file cache
Section titled “Ispezionare i file cache”Puoi usare hexdump o un editor esadecimale per guardare dentro un file .kv:
hexdump -C /tmp/ds4-kv/*.kv | head -80Vedrai l’header, poi il testo renderizzato (leggibile), poi i dati binari del payload DS4. La sezione tool-id map è opzionale.
Per capire perché un checkpoint non viene riutilizzato, avvia il server con --trace /tmp/trace.txt. Nel trace troverai righe come:
KV cache: miss for sha1=abc123 (reason: prefix mismatch at byte 1234)KV cache: hit for sha1=def456 (loaded 10208 tokens)Conclusione
Section titled “Conclusione”La cache KV su disco è una delle funzionalità più utili di ds4-server per chi usa agenti di coding. Trasforma richieste che richiederebbero decine di secondi di prefill in operazioni quasi istantanee, specialmente quando la conversazione è lunga.
Consiglio pratico: Abilitala sempre con una directory dedicata (es. --kv-disk-dir /tmp/ds4-kv). Imposta --kv-disk-space-mb in base allo spazio che vuoi dedicare (8 GB sono un buon inizio). Se lavori con molti progetti diversi, la cache crescerà, ma l’eviction automatica dei file meno recenti la terrà sotto controllo.
💡 La cache persiste anche tra riavvii del server
A differenza della cache in memoria, i file .kv restano su
disco. Se riavvii ds4-server con la stessa directory, le
conversazioni già elaborate ripartiranno velocemente. Utile per ambienti di
sviluppo dove il server viene riavviato spesso.