15 · Inferència, decodificació i serving
Per a un enginyer, aquest és probablement el fitxer més útil de tots. El 99% del que faràs amb LLMs és inferència, i la diferència entre una implementació ingènua i una de bona és de 10-50× en cost.
1. Les dues fases (i per què tot depèn d'aquesta distinció)# sense fórmules
| Fase | Què fa | Limitada per | Paral·lelisme |
|---|---|---|---|
| Prefill | processa el prompt sencer | càlcul (compute-bound) | tots els tokens alhora |
| Decode | genera token a token | memòria (memory-bound) | 1 token per pas |
Mètriques que has de mesurar
- TTFT (time to first token): dominat pel prefill → creix amb la longitud del prompt.
- TPOT / ITL (time per output token): dominat per l'ample de banda.
- Throughput (tokens/s agregats): el que determina el teu cost per milió de tokens.
- Latència p50/p95/p99 sota càrrega real, no en buit.
Prefill contra decode
Les dues fases fan gairebé els mateixos FLOPs per token. Llavors, per què una va limitada per càlcul i l'altra per memòria?
El rellotge: el que es pot cronometrar aquí
- prefill sencer: creix amb L
- prefill, per token
- un pas de decode
Les dues línies de baix van juntes, i és el fet que importa: el cost per token és el mateix a les dues fases. La de dalt creix amb L perquè processa L tokens; la de punts gairebé no es mou perquè en processa un. I les dues pugen lentament amb el context, que és el cost de l'atenció contra el cache.
El comptador: bytes de pesos llegits per token generat
La línia del decode és plana: generi el que generi, cada token demana llegir tots els pesos. La del prefill baixa amb pendent −1, perquè la mateixa lectura es reparteix entre més tokens. La distància vertical entre les dues és el factor L, i a 512 tokens ja són 512×. Això és el que una GPU nota i el rellotge de dalt no pot ensenyar.
70,55 G paràmetres actius de 70,55 G totals.
990 TFLOPs en bf16 i 3.350 GB/s d'ample de banda.
Els pesos es llegeixen una vegada per a tot el lot.
- sostre en decode
- 23,3 tok/s
- TPOT
- 42,9 ms
- TTFT
- 1,17 s
- amb lot de 32
- 472 tok/s
- guany del lot
- 20,3×
- intensitat aritmètica
- 296 FLOP/byte
Això és el que surt de cronometrar-ho de debò amb el mini-GPT, i cal dir-ho abans de res perquè contradiu el que s'esperaria: en aquest entorn, un token de decode no és més lent que un de prefill. A 128 tokens de context, el prefill fa 76 µs per token i un pas de decode en fa 94.
I ha de ser així. Aquest codi és JavaScript escalar: no hi ha unitats vectorials ni milers de fils, de manera que no hi ha res a paral·lelitzar. El prefill de L tokens fa L vegades la feina d'un token i triga L vegades més. Que la ràtio surti ~1 no és un error de la mesura: és la demostració que les dues fases fan la mateixa feina aritmètica per token, que és justament la premissa que fa desconcertant el §1.
La diferència de la GPU, doncs, no pot sortir dels FLOPs. Surt de l'altra columna.
Aquest és el número que ho explica tot, i no s'estima: es compta. Un forward llegeix els pesos una vegada. En prefill, aquell forward processa L tokens, de manera que a cada token li toquen bytes. En decode, el forward treu un token i li toquen sencers. La ràtio és exactament :
| L | pesos/token prefill | pesos/token decode | ràtio | FLOPs/token prefill | FLOPs/token decode |
|---|---|---|---|---|---|
| 16 | 1,8 k | 28,3 k | 16× | 58,82 k | 60,74 k |
| 32 | 885,0 | 28,3 k | 32× | 60,86 k | 64,83 k |
| 64 | 442,5 | 28,3 k | 64× | 64,96 k | 73,02 k |
| 128 | 221,3 | 28,3 k | 128× | 73,15 k | 89,41 k |
| 256 | 110,6 | 28,3 k | 256× | 89,54 k | 122,18 k |
| 512 | 55,3 | 28,3 k | 512× | 122,30 k | 187,71 k |
Les dues últimes columnes gairebé no es mouen —els FLOPs per token són els mateixos a les dues fases— i les dues del mig es diferencien per un factor L. Aquí hi ha tot el §1: una màquina que pot fer 296 operacions per cada byte que llegeix es queda esperant memòria quan li demanes una operació per byte, i treballa a ple quan li'n demanes mil. El prefill li'n demana mil; el decode, una.
El text fa el càlcul a mà amb un 70B en fp16 i una GPU de 3 TB/s i en treu ~21 tokens/s. Aquí surt de decodeTokensPerSecond, amb els paràmetres comptats a partir de les formes del model card:
| Paràmetres actius | 70,552 G |
| × 2 bytes | 141,1 GB per token |
| Ample de banda de la H100 80 GB | 3.350 GB/s |
| Sostre, només pesos | 23,7 tokens/s |
| …amb el KV-cache de 8 k tokens inclòs | 23,3 tokens/s |
L'última fila és el que el §2 anticipa: el cache també s'ha de llegir a cada pas, i amb un context llarg comença a pesar. A 8 k tokens li menja 2 % del sostre.
I el batching: amb 32 seqüències, els pesos es llegeixen una sola vegada per a totes i el throughput puja a 472 tokens/s, que són 20,3×. Molt per sota del factor sencer: aquí el cache de les seqüències ja pesa més que els pesos, i el batching ha deixat de ser gratis. És el «fins a cert punt».
2. El KV-cache# sense fórmules
Sense cache, generar el token requeriria recalcular l'atenció de tots els anteriors: repetit vegades. Amb cache, guardes i de cada token i cada capa, i cada pas nou costa .
Exemple: Llama-3-70B (80 capes, 8 caps KV, =128) amb 8k tokens en fp16: GB per seqüència. Amb 100 usuaris concurrents, 270 GB només de cache.
Tècniques per gestionar-lo:
- GQA/MLA (reducció estructural, vegeu 12).
- PagedAttention (vLLM): gestiona el cache en blocs de mida fixa com les pàgines de memòria virtual d'un SO. Elimina la fragmentació (que malbaratava el 60-80% de la memòria) i permet compartir blocs entre seqüències amb prefix comú (copy-on-write).
- Quantització del cache a FP8: la meitat de memòria i la meitat de trànsit per pas d'atenció; amb formats e4m3 la pèrdua de qualitat és molt petita i l'acceleració en decode és directa.
- Prefix caching / prompt caching: reutilitzar el cache d'un prefix compartit (system prompt, documents, historial) entre peticions. És, de lluny, l'optimització amb més retorn per esforç en aplicacions reals: estalvis del 50-90% en cost i TTFT.
- Desallotjament i compressió (H2O, SnapKV, StreamingLLM): descartar tokens poc atesos.
El KV-cache en serving: fragmentació, pàgines i prefixos
El fitxer 12 va comptar quants bytes fa el cache. Aquí es veu què se'n fa: quina part es malbarata i quina es pot compartir.
Com queda la memòria amb cada mètode
Catorze seqüències, la mateixa escala a les dues files. La part plena és el que es fa servir i la buida és el que s'ha reservat i no es farà servir mai. Es veu a ull que la segona fila ocupa molt menys, i la taula de sota en diu quant.
2.048 escalars per token i capa.
El que una seqüència podria arribar a fer servir. El mètode contigu ho reserva sencer.
A 0, totes les seqüències fan el context màxim. És el control que ensenya d'on surt la fragmentació.
vLLM en fa servir 16. Més petita vol dir menys malbaratament i més metadades.
Quantitzar el cache a fp8 en divideix per dos els bytes i el trànsit del pas d'atenció.
System prompt, un document, l'historial d'una conversa.
- malbaratat, contigu
- 77 %
- malbaratat, paginat
- 0,8 %
- seqüències, contigu
- 59
- seqüències, paginat
- 253
- prefill estalviat
- 96 %
- TTFT amb prefix
- 245 → 5 ms
El text diu que la fragmentació «malbaratava el 60-80 % de la memòria». Aquí es reprodueix amb una simulació honesta: 300 seqüències amb longituds que van de 33 a 4.096 tokens, i una mitjana de 967.
| Mètode | reservat | fet servir | malbaratat | blocs | hi caben |
|---|---|---|---|---|---|
| Contigu (reserva el màxim) | 241,7 k | 54,6 k | 77 % | 59 | 59 |
| Paginat (16 tokens per pàgina) | 244,1 k | 242,1 k | 0,8 % | 15.258 | 253 |
La causa és una sola decisió: el cache contigu ha de créixer en un tros seguit i no es pot moure, de manera que cal reservar tot el que la seqüència podria arribar a fer servir abans de saber on s'aturarà. Una resposta que acaba als 33 tokens amb 4 k reservats en té la resta buits i inservibles.
El que queda amb pàgines és només el tros buit de l'última de cada seqüència: 15 tokens com a molt, que sobre 967 de mitjana és el 0,8 % que es llegeix a la taula. Amb pàgines d'un sol token seria exactament zero, i el que ho impedeix són les metadades, no la idea.
Aquest és el control que fa creïble la mesura. La fragmentació no surt d'haver reservat memòria: surt de la variabilitat de les longituds. Amb el control de variabilitat a zero, totes les seqüències fan exactament el context màxim, i llavors reservar el màxim és reservar el que caldrà: el malbaratament del mètode contigu és zero i els dos mètodes donen el mateix.
Ara mateix és a 100 %, i el mètode contigu en malbarata el 77 %. Les respostes d'un LLM tenen longituds molt desiguals —moltes curtes i una cua llarga— i per això aquest és el cas real i no el pitjor cas imaginat.
Amb 50 peticions sobre un prefix de 2 k tokens, processar-lo cada vegada costaria 102,00 k tokens de prefill; reutilitzant el cache del prefix, 4,00 k. El text diu que és «de lluny l'optimització amb més retorn per esforç» i en promet estalvis del 50-90 %: aquí en surt un 96 %.
El TTFT baixa en la mateixa proporció, i és la meitat que es nota des de fora: la primera petició espera 245 ms abans de veure la primera lletra i totes les altres, 5.
Quan no es pot aplicar, que és la part que costa cara si no es pensa: només es comparteixen blocs sencers i idèntics des del primer token. Amb pàgines de 16, d'aquest prefix se'n comparteixen 125 blocs —2.000 tokens— i la resta es recalcula sempre. I si al capdamunt del prompt hi poses la data i l'hora, el primer bloc ja no coincideix amb el de fa un minut i tot el cache queda invalidat. Les coses variables van al final del prompt, no al principi.
| Format | bytes/escalar | cache a 4 k | seqüències a 80 GB |
|---|---|---|---|
| FP32 | 4 | 2,68 GB | 29 |
| BF16 | 2 | 1,34 GB | 59 |
| FP8 (e4m3) | 1 | 0,67 GB | 119 |
| INT8 | 1 | 0,67 GB | 119 |
La fórmula del §2, , és lineal en els bytes: passar de bf16 a fp8 dobla les seqüències concurrents i, com que el cache també s'ha de llegir a cada pas d'atenció, en redueix el trànsit a la meitat. Amb e4m3 el text diu que la pèrdua de qualitat és molt petita, i el widget de la quantització ho mesura sobre pesos de debò.
3. Continuous batching# sense fórmules
El batching estàtic malbarata GPU: si una seqüència del batch acaba a 20 tokens i una altra a 2000, esperes la més llarga.
El continuous batching (o in-flight batching) opera a nivell d'iteració: quan una seqüència acaba, entra'n una de nova immediatament. Guany típic de 2-4× en throughput, i és el motiu principal pel qual has d'usar vLLM, SGLang o TensorRT-LLM en comptes d'un bucle amb model.generate().
Altres tècniques de sistema:
- Chunked prefill: trosseja prefills llargs perquè no bloquegin els decodes en curs (millora p99).
- Desagregació prefill/decode: executar cada fase en GPUs diferents, optimitzades per a cada perfil.
- Speculative decoding (secció 5).
Continuous batching
El text promet 2-4× de throughput. Simula la cua i mira't el número que en surt, i d'on surt: quantes ranures del lot estan esperant sense fer res.
- guany mesurat en aquesta simulació
- la franja 2-4× del text
Les ranures del lot, iteració a iteració
Cada quadret és una ranura del lot; les plenes són les que estan generant i les buides, les que esperen. Cada iteració costa el mateix tant si les ranures estan plenes com si no, i per això les buides són cares.
A 0, totes les respostes fan la mateixa llargada. És el control que ensenya d'on surt el guany.
Trosseja els prefills llargs perquè no bloquegin els decodes en curs.
- throughput estàtic
- 118 tok/s
- throughput continu
- 325 tok/s
- guany
- 2,75×
- ranures ocioses, estàtic
- 69 %
- ranures ocioses, continu
- 7 %
- p95 de latència
- 62,9 → 23,1 s
Amb 80 peticions, 8 ranures i longituds de sortida que van de 8 a 390 tokens, el batching estàtic treu 118 tokens/s i el continu, 325.
| throughput | iteracions | ranures ocupades | TTFT p95 | latència p95 | |
|---|---|---|---|---|---|
| Estàtic | 118 | 3.185 | 2,5 de 8 | 62,30 s | 62,85 s |
| Continu | 325 | 1.061 | 7,4 de 8 | 19,07 s | 23,07 s |
La columna que explica el guany és la de ranures ocupades. El batching estàtic en té 2,5 de 8 de mitjana i el continu, 7,4: és exactament la mateixa ràtio que el guany, perquè cada iteració costa el mateix passi el que passi i el que canvia és quants tokens en surten.
Aquesta és la GPU ociosa del §3, quantificada. Un lot estàtic no es toca fins que s'ha buidat del tot: si una seqüència acaba a 8 tokens i una altra a 390, la ranura de la primera es queda 382 iteracions sense fer res mentre la segona acaba. El continu hi fa entrar una petició nova el mateix instant que una acaba, i per això les seves ranures ocioses baixen al 7 %.
És també el motiu pel qual el guany no és el mateix a tots els lots. Amb una sola ranura no hi ha res a omplir i el guany és 1; a mesura que el lot creix, hi ha més ranures que poden quedar esperant i el continu en treu més. Mira el gràfic: la corba puja i després s'aplana quan el lot ja és més gran que la cua.
El control negatiu que fa creïble la resta. El continuous batching no accelera res per si mateix: omple ranures que altrament estarien buides. Si totes les respostes fan la mateixa llargada, cap ranura no espera ningú, i els dos modes donen exactament el mateix.
Això vol dir que el 2-4× del text no és una propietat de vLLM sinó una propietat de la teva càrrega. Si serveixes classificacions que sempre responen «sí» o «no», el continuous batching no et donarà res; si serveixes un xat on unes respostes fan deu tokens i unes altres en fan dos mil, te'n donarà molt. Abans de mesurar el guany, mira la distribució de longituds de sortida.
El chunked prefill no millora el throughput —la feina de prefill és la mateixa— i per això el seu efecte no es veu a cap de les columnes de dalt. El que millora és el pitjor interval entre dos tokens d'una petició que ja estava decodificant quan n'ha entrat una amb un prompt llarg: sense trossejar, aquell prefill es fa tot en una iteració i tothom es queda congelat.
Ara mateix està actiu amb trossos de 512 tokens, i el pitjor interval és de 97 ms (p99: 97 ms). Apaga'l i torna'l a engegar amb un prompt llarg a la cua: el throughput no es mou i aquest número sí. És per això que el text en parla en termes de p99 i no de mitjana.
4. Quantització# sense fórmules
| Format | Bits | Pèrdua típica | Ús |
|---|---|---|---|
| BF16/FP16 | 16 | cap (referència) | baseline |
| FP8 (e4m3) | 8 | mínima | estàndard a Hopper/Blackwell, també per al KV-cache |
| INT8 (SmoothQuant, W8A8) | 8 | molt petita | GPUs sense FP8 |
| INT4 / NF4 / AWQ / GPTQ | 4 | petita-moderada | execució local, models grans en poca VRAM |
| MXFP4 i formats de microescalat | 4 | petita | generació nova de hardware |
| 2-3 bits | 2-3 | notable | recerca, casos extrems |
Conceptes:
- PTQ (post-training quantization): quantitzes un model ja entrenat. GPTQ (aproximació de segon ordre per capes), AWQ (protegeix els canals "salients" identificats per activacions).
- QAT (quantization-aware training): simules la quantització durant l'entrenament; millor qualitat, molt més car.
- Outliers d'activació: el problema central. Uns pocs canals tenen magnituds enormes i arruïnen l'escalat; d'aquí SmoothQuant (mou dificultat de les activacions als pesos) i les tècniques de rotació (QuaRot, SpinQuant).
Quantització: menys bits, i què costa
Quantitza els pesos de debò i torna a mesurar la pèrdua. I després mira el problema central: què passa quan uns pocs canals tenen magnituds enormes.
Un mini-GPT de 32 dimensions i 2 capes —28.832 paràmetres— sobre el corpus en català. Un cop entrenat, els seus pesos es quantitzen de debò a cada format i es torna a fer el forward: la columna de pèrdua de la taula surt d'aquí i no de cap estimació d'error.
Una escala per a tot, o una per grup. Només importa amb quantització entera.
Els quantitzadors reals fan servir grups de 64 o 128.
Factor que s'aplica a tres canals de les activacions. A 1 no n'hi ha cap.
- pèrdua original
- —
- pèrdua a INT4
- —
- degradació
- —
- memòria
- —
- contra bf16
- —
- ràtio d’outliers
- —
Quantitzar un model sense entrenar no diu res: la pèrdua ja és la de l'atzar i arrodonir els pesos no la pot empitjorar gaire. El que fa informativa la mesura és tenir un model que prediu, i mirar quant li costa perdre bits.
5. Speculative decoding# sense fórmules
Idea: un model petit i ràpid (draft) proposa tokens; el model gran els verifica tots alhora en una sola passada (cosa que és barata, perquè el decode està limitat per memòria i no per càlcul) i accepta el prefix correcte.
- Garanteix la mateixa distribució de sortida que el model gran (amb el mostreig de rebuig adequat). No és una aproximació.
- Acceleració típica: 1,5–3×, depèn de com d'alineat estigui el draft.
- Variants sense model draft: Medusa (caps addicionals que prediuen , ...), EAGLE (predicció a nivell de característiques, la família més eficaç actualment), n-gram/prompt lookup (per a tasques amb molta còpia del prompt, com edició de codi — trivial d'implementar i sorprenentment efectiu), i multi-token prediction entrenada al propi model (DeepSeek-V3).
Speculative decoding
Un model petit proposa i el gran verifica. La promesa és que la sortida no canvia: quina de les dues lectures d'aquesta frase és certa?
Dos models sobre el mateix corpus en català: el gran, de 32 dimensions i 3 capes, i el draft, de 16 i 1. El draft s'entrena una fracció dels passos segons el control, que és el que fa que es pugui veure la taxa d'acceptació moure's.
Més propostes vol dir més potencial i més feina llençada quan una es rebutja.
El primer dona la seqüència idèntica; el segon accepta més sovint. No es poden tenir les dues coses.
Com d'alineat està el draft amb el model gran. D'això depèn tot.
- passos del gran
- 0
- passos del draft
- 0
- taxa d’acceptació
- —
- tokens per passada del gran
- —
- passades del gran
- —
- seqüència idèntica
- —
Calen dos models sobre el mateix corpus: un de gran (32 dimensions, 3 capes) i un draft petit (16 dimensions, 1 capa). La taxa d'acceptació depèn de com d'alineats estiguin, i el control «entrenament del draft» permet deixar-lo enrere a posta per veure-ho.
6. Decodificació: com es tria el token# sense fórmules
Tens una distribució sobre tokens. Què en fas?
| Mètode | Descripció | Quan |
|---|---|---|
| Greedy () | el més probable | tasques deterministes, avaluació |
| Temperatura | més conservador, més creatiu | |
| Top-k | només els millors | senzill, fix és poc adaptatiu |
| Top-p (nucleus) | el conjunt mínim amb massa acumulada | el més usat; adaptatiu |
| Min-p | llindar relatiu al token més probable | robust a temperatures altes |
| Penalitzacions | repetició, freqüència, presència | evita bucles; compte amb degradar la qualitat |
| Beam search | manté hipòtesis | traducció i tasques amb sortida curta; dolent per a text obert (genera text avorrit i repetitiu) |
Sortides estructurades: constrained decoding amb una gramàtica o esquema JSON (Outlines, XGrammar, guidance) força la validesa emmascarant els logits invàlids. És gairebé gratis i elimina tota una classe d'errors de parsing. Compte: restringir massa pot degradar la qualitat del contingut si el model "vol" escriure raonament abans.
Com es tria el token
Tens una distribució sobre el vocabulari. Cada mètode en talla un tros diferent: mira quanta massa deixa viva cadascun i com canvia el text que en surt.
Un mini-GPT de 32 dimensions i 2 capes sobre el corpus en català. Un cop prediu, la seva distribució a cada posició té la forma que fa interessants els mètodes de truncament: un pic i una cua llarga. La fita del bigrama sobre el mateix text és 2,30 nats.
Divideix els logits abans de la softmax. A 0 és greedy i no mostreja res.
El conjunt mínim amb massa acumulada ≥ p.
Llindar relatiu al token més probable: fora tot el que baixi de p·màxim.
Divideix els logits positius dels tokens ja vistos i multiplica els negatius.
Quina distribució es mira. N'hi ha de molt punxegudes i de molt planes.
- candidats vius
- —
- massa conservada
- —
- entropia
- —
- perplexitat
- —
- sense truncar
- —
- token més probable
- —
Sense entrenar, la distribució del model és gairebé uniforme i cap mètode de truncament no diu res: top-p amb 0,9 es quedaria amb el 90 % del vocabulari i la cua tallada no existiria. El que fa interessant la tria del token és tenir una distribució punxeguda amb cua llarga, i això demana un model que prediu.
7. Càlcul del que costa# sense fórmules
Memòria necessària ≈ pesos + KV-cache + activacions + fragmentació:
pesos = N_params × bytes_per_param
kv_cache = 2 × capes × caps_kv × d_head × context × batch × bytes
overhead ≈ 10-20%
Tokens/s màxims en decode ≈ ample_de_banda / bytes_llegits_per_token.
La calculadora del que costa
Pesos, cache, activacions i overhead d'una banda; ample de banda dividit per bytes de l'altra. Hi cap el model, i quant et costa el milió de tokens?
On va la memòria a mesura que creix el context
- pesos: no depenen del context
- cache de 32 seqüències
- total
- el que tens
Les dues escales són logarítmiques. La línia dels pesos és plana i la del cache té pendent 1: on es creuen és el punt a partir del qual afegir context costa més que tenir el model. On la corba del total travessa la ratlla vermella és el context màxim que aquesta màquina pot servir amb 32 seqüències concurrents.
| Model | pesos | cache/seq | seq. que hi caben | tok/s (1 seq) | TTFT | dòlars/M |
|---|---|---|---|---|---|---|
| GPT-2 XL | 3 GB | 2,52 GB | 219 | 4.630,8 | 0,00 s | 0,65 |
| Llama 3 70B | 141 GB | 2,68 GB | 154 | 186,4 | 0,15 s | 1,76 |
| Mixtral 8×7B | 93 GB | 1,07 GB | 431 | 998,8 | 0,03 s | 0,47 |
| DeepSeek-V3 | 1.333 GB | 0,51 GB | no hi cap | 402,0 | 0,07 s | — |
La columna de dòlars per milió és la que es compara amb els preus d'una API, i la de «seqüències que hi caben» és la que diu si el número de l'altra vol dir res. Fixa't en DeepSeek-V3: té els pesos més grossos de tots amb diferència i tanmateix genera de pressa, perquè el que es llegeix per token són els 33,08 G paràmetres actius i no els 666,55 G totals. Les dues xifres van en sentits contraris i totes dues són certes: aquesta és la fitxa del MoE en una fila.
Les dues fórmules del §7
La primera decideix si hi cap i la segona, com de ràpid va. No són independents: quantitzar baixa les dues alhora, i allargar el context les puja les dues. L'única decisió que millora la segona sense tocar la primera és pujar el lot, i el §3 explica per què no és gratis del tot.
70,55 G paràmetres totals, 70,55 G actius.
640 GB i 26.800 GB/s en total.
Fragmentació, buffers del runtime, gràfics de CUDA. El text en diu 10-20 %.
- memòria necessària
- 261 GB
- hi cap?
- sí
- seqüències que hi caben
- 154
- throughput
- 3.778 tok/s
- TTFT / TPOT
- 0,15 s / 5,4 ms
- per milió de tokens
- 1,76 dòlars
Les quatre línies de la primera fórmula del §7, amb el que aporta cadascuna. La conclusió del fitxer 12 —que el cache, i no els pesos, és el que limita quantes converses pots servir— es llegeix directament a la segona fila quan el context és llarg:
| Component | bytes | del total | d'on surt |
|---|---|---|---|
| Pesos | 141,1 GB | 54 % | 70,55 G × 2 bytes |
| KV-cache | 85,9 GB | 33 % | 2.048 esc./token·capa × 80 × 8 k × 32 |
| Activacions | 0,35 GB | 0,1 % | buffers d'un pas de decode |
| Overhead (15 %) | 34,1 GB | 13 % | fragmentació i runtime |
Les activacions d'inferència són un 0,1 % i es poden oblidar —en entrenament són el terme dominant, i és el tema del fitxer 17. Aquí la partida es juga entre les dues primeres files, i el context decideix quina guanya: a 8 k tokens i 32 seqüències, el cache és un 61 % dels pesos. El creuament per a una sola seqüència és als 430,6 k tokens; amb 32 concurrents arriba 32× abans, als 13,5 k.
La segona fórmula del §7, amb els dos números que la formen. Per generar un token cal llegir tots els pesos actius —70,55 G paràmetres a 2 bytes, que són 141,1 GB— i tot el cache de la seqüència, que a 8 k tokens són 2,68 GB. Dividit per l'ample de banda, això dona 186,4 tokens/s per seqüència, i no hi ha kernel que ho pugui passar.
Amb 32 seqüències concurrents els pesos es llegeixen una sola vegada per a totes i el throughput agregat puja a 3.778 tokens/s, que són 20,3× i no 32×: la diferència és el cache, que sí que es multiplica. Aquí ja s’ha perdut la major part del factor: el cache pesa més que els pesos i afegir seqüències ja no és gratis.
I el número que explica per què les dues fases del §1 es comporten diferent: aquesta H100 80 GB pot fer 296 operacions per cada byte que llegeix. Un pas de decode en demana ~2, de manera que la màquina passa el 99,3 % del temps esperant memòria.
La xifra amb què es prenen les decisions. 8 × 3,00 dòlars l'hora són 24,00 dòlars l'hora, i a 3.778 tokens/s surten 13,60 M tokens per hora.
Les tres palanques, i quina val més en aquest punt: quantitzar els pesos redueix els bytes per token i allibera memòria per a més cache; escurçar el context redueix el cache directament; i pujar el lot és gratis fins que el cache passa davant dels pesos. Prova-les amb els controls: la que més mou el cost depèn d'on siguis, i aquest és tot el motiu de tenir una calculadora en comptes d'una regla.
8. Comprova que ho has entès#
0 de 5 resoltes
- Per què el decode està limitat per memòria i el prefill per càlcul?
- Calcula el KV-cache d'un model de 32 capes, 8 caps de KV i , amb un context de 32.768 tokens, batch 16, en fp16. Dona el resultat en bytes.bytesMarge acceptat: 2 %
- Per què speculative decoding no canvia la distribució de sortida?
- Quin efecte té el prefix caching sobre el TTFT i sobre el cost, i quan no es pot aplicar?
- Per què beam search és mala idea per a generació de text obert, si busca la seqüència més probable?
9. Recursos#
Kwon et al. (2023), Efficient Memory Management for LLM Serving with PagedAttention (vLLM).
Documentació de vLLM i SGLang (i els seus blogs tècnics: són excel·lents).
Leviathan et al. (2023), Fast Inference from Transformers via Speculative Decoding.
Holtzman et al. (2019), The Curious Case of Neural Text Degeneration (nucleus sampling).
Lin et al. (2023), AWQ; Frantar et al. (2022), GPTQ; Xiao et al. (2022), SmoothQuant.