Stessa GPU, stesso modello, due lamentele opposte nella stessa settimana. Il team di prodotto scrive che l’assistente «è diventato lento, gli utenti se ne accorgono». Il team di finanza scrive che «il conto del cloud è raddoppiato, così non regge». Il primo istinto è cercare l’errore, il colpevole, la regressione da correggere. Ma non c’è nessun bug: i due team stanno guardando due numeri diversi che tirano in direzioni opposte, e migliorarne uno peggiora quasi sempre l’altro. Questi due numeri si chiamano latenza e throughput, e la loro tensione è il cuore di ogni decisione su come servire un modello.
Come si scompone la latenza — prefill e decode, TTFT, TPOT, goodput — è spiegato nel libro.
Leggi «Misurare la generazione: TTFT, TPOT e goodput» nel libro →
Due numeri, non uno
La latenza è il tempo che passa dal momento in cui una richiesta arriva a quando è servita. È un’esperienza personale: riguarda il singolo utente che aspetta. Per un LLM si scompone in due sotto-misure che vale la pena tenere separate: il time to first token, quanto aspetti prima di vedere apparire la prima parola, e il tempo per token, quanto velocemente scorre il resto della risposta.
Il throughput è tutt’altra prospettiva: quante richieste — o quanti token — il sistema serve al secondo nel complesso, sommando tutti gli utenti. È una misura collettiva, e determina direttamente il costo: se una GPU serve il doppio delle richieste al secondo, il costo per richiesta si dimezza.
L’analogia più onesta è quella di un casello autostradale. La latenza è quanto tempo impiega la tua auto ad attraversare la barriera. Il throughput è quante auto passano in un’ora, contando tutte le corsie. Sono legate, ma non sono la stessa cosa: puoi rendere velocissimo il passaggio di ogni singola auto tenendo aperta una sola corsia con un casellante fulmineo, e avere comunque un throughput miserabile perché le altre corsie sono chiuse. Oppure puoi aprire venti corsie e far passare fiumi di auto, mentre ciascuna aspetta un po’ di più nella coda che si forma prima delle barriere.
Il batch: la leva che li mette uno contro l’altro
La ragione per cui questi due numeri litigano ha un nome preciso: il batching, cioè processare più richieste insieme in un solo passaggio attraverso il modello. Sulle GPU conviene, e molto: caricare i pesi del modello dalla memoria costa uguale che si serva una richiesta o trentadue, quindi impacchettarne di più significa spalmare quel costo fisso su molte più risposte. Il throughput cresce quasi linearmente con la dimensione del batch.
Ma c’è un prezzo, e lo paga la latenza. Per formare un batch il sistema aspetta che si accumulino abbastanza richieste, e ogni richiesta nel gruppo deve condividere la GPU con le altre: il tempo per generare il singolo token si allunga. È di nuovo il casello: raggruppare le auto in convogli fa passare più veicoli all’ora, ma ogni automobilista aspetta che il convoglio si formi prima di partire.
Non esiste una configurazione che sia insieme la più veloce per il singolo e la più economica per tutti: si sceglie sempre un punto lungo un compromesso.
Questo è il nocciolo controintuitivo: latenza e throughput non si ottimizzano insieme. Ogni intervento che li fa salire entrambi esiste — ne parliamo tra poco — ma prima o poi la leva del batch riappare, e allora bisogna decidere da che parte stare.
La curva del compromesso
Il modo più chiaro di ragionarci è disegnare la relazione. Su un asse la latenza per richiesta, sull’altro il throughput del sistema. Al crescere della dimensione del batch ci si muove lungo una curva: batch piccolo significa latenza bassa ma throughput basso; batch grande significa throughput alto ma latenza alta. E la curva non sale all’infinito: dopo un certo punto raddoppiare il batch aumenta la latenza senza quasi più far crescere il throughput, perché la GPU è ormai satura.
Al crescere del batch si scivola lungo la curva: più throughput, più latenza, con rendimenti decrescenti. Fissato un budget di latenza (la linea tratteggiata), il punto operativo migliore è il throughput più alto che resta dentro quel budget.
Ecco perché disegnare la curva cambia il modo di decidere. Non si cerca «il» punto migliore in assoluto — non esiste — ma il punto migliore dato un vincolo. Di solito il vincolo è la latenza: il prodotto stabilisce che l’utente non deve mai aspettare più di, poniamo, un secondo per la prima parola. Quello diventa il budget, la linea tratteggiata. E il lavoro di ottimizzazione si riformula così: massimizzare il throughput restando a sinistra di quella linea. Il throughput che rispetta il budget ha persino un nome dedicato, goodput: le richieste servite abbastanza in fretta da contare davvero.
Le altre leve
Il batch non è l’unica manopola, e alcune delle altre hanno il pregio raro di spingere entrambi i numeri nella direzione giusta. La quantizzazione — rappresentare i pesi del modello con meno bit, per esempio 8 o 4 invece di 16 — riduce la memoria da spostare a ogni passaggio: la generazione diventa più rapida (meno latenza) e sulla stessa GPU entrano più richieste (più throughput). Il prezzo qui non lo paga nessuno dei due numeri, ma la qualità: una compressione troppo aggressiva degrada le risposte, e va misurata.
La KV cache — conservare i calcoli di attention già fatti invece di rifarli a ogni token — è ormai la norma e abbatte la latenza di generazione, ma occupa memoria, e quella memoria è esattamente ciò che limita quanto grande può essere il batch: ecco che il compromesso rientra dalla finestra. Il decoding speculativo, in cui un modello piccolo e veloce propone più token che il modello grande verifica in blocco, riduce la latenza percepita ma consuma cicli di calcolo extra, quindi può intaccare il throughput sotto carico pesante. E il parallelismo — spezzare il modello su più GPU — abbassa la latenza dei modelli enormi al costo di comunicazione tra le schede, che sotto una certa dimensione non ripaga.
Il filo comune è che quasi ogni ottimizzazione si «spende» da qualche parte: memoria, qualità, complessità, o proprio l’altro dei due numeri. Non esistono pasti gratis, esistono compromessi ben scelti.
Misurare quello che conta
Una trappola frequente è ottimizzare sulla media. La latenza media è quasi inutile da sola: nasconde la coda. Ciò che rovina l’esperienza non è la richiesta tipica, è quella lenta — e «quanto lenta è la peggiore su cento» (il novantanovesimo percentile, o p99) racconta una storia che la media addolcisce fino a mentire. Un sistema con latenza media ottima e p99 pessimo ha un utente su cento infuriato, e su grandi numeri quell’uno per cento è un esercito.
Il metodo sano è invertire l’ordine delle domande. Prima si fissa il vincolo di esperienza — il budget di latenza, misurato sul percentile giusto e non sulla media. Poi, e solo poi, si spinge il throughput il più in alto possibile senza sfondare quel budget. Ogni scelta tecnica — dimensione del batch, quantizzazione, hardware — si valuta con lo stesso righello: quanto goodput mi dà, a parità di promessa fatta all’utente?
Perché ti riguarda
Se paghi un’API a token, questa tensione spiega scelte che altrimenti sembrano capricci del fornitore: perché esistono tier «veloci» più cari e tier «batch» scontatissimi ma lenti (stanno vendendoti punti diversi sulla stessa curva), perché la latenza peggiora nelle ore di punta (il fornitore alza il batch per reggere il carico), perché a volte conviene mandare i lavori non urgenti in coda notturna.
Se invece servi un modello in proprio, il messaggio è ancora più diretto: la domanda giusta non è «quanto è veloce» né «quanto costa», ma «qual è la latenza che devo garantire, e qual è il throughput massimo che ci sta dentro». Chi si pone la domanda in questi termini smette di inseguire un numero solo e comincia a governare un compromesso. Ed è lì, molto più che nella scelta del modello, che si decide se un progetto di AI in produzione sarà sostenibile o brucerà budget a ogni richiesta.

I commenti sono riservati agli iscritti.
Accedi per commentare