Een bedrijf zet een lokaal 13B-model op, opent het voor tien interne gebruikers. De eerste week verloopt soepel. Dan komt een tweede team, voegt een nieuwe use case toe, het aantal gelijktijdige verzoeken schiet omhoog naar 20–30 en de responstijd klimt van drie seconden naar veertig. Iemand stelt voor een krachtigere GPU te kopen. Gedaan. De responstijd daalt naar 25 seconden. Het probleem is niet de hardware — het probleem is wat er met de KV-cache en batching onder de motorkap gebeurt.
Throughput en latentie van LLM-inferentie zijn niet alleen een hardwarevraag. Ze zijn architecturaal. Hoe het serving framework geheugen beheert, hoe het verzoeken batcht, hoe het tussenresultaten deelt — dát bepaalt of u uit één GPU een rendement haalt dat overeenkomt met de aanschafprijs, of dat u betaalt voor prestaties die nooit worden benut. Dit artikel licht de mechanismen toe en laat zien waar de knoppen zitten om te verbeteren zonder extra hardware aan te schaffen.
Waar prestaties verloren gaan: anatomie van LLM-inferentie
Om te begrijpen waar optimalisatie mogelijk is, moet u eerst begrijpen wat er tijdens tekstgeneratie gebeurt. LLM-inferentie verloopt in twee fasen.
De prefill-fase verwerkt de volledige invoerprompt in één keer — een paralleliseerbare operatie die de GPU belast als matrixvermenigvuldiging. Het resultaat zijn sleutel- en waardevectoren (KV) voor elk invoertoken, die in het geheugen worden opgeslagen. De tijd die deze fase kost, heet TTFT — Time to First Token. Juist de TTFT bepaalt wanneer de gebruiker het eerste teken van het antwoord ziet.
De decode-fase genereert tokens één voor één, waarbij elk nieuw token afhankelijk is van alle vorige. Dit is een sequentiële operatie — niet te paralleliseren binnen één verzoek. De generatiesnelheid wordt gemeten in tokens per seconde (tokens/sec).
Cruciaal inzicht: prefill is compute-bound (belast GPU-kernen), decode is memory-bound (belast geheugenbandbreedte). Deze twee fasen hebben verschillende knelpunten en vragen verschillende optimalisaties. De meeste naïeve implementaties offeren throughput op voor eenvoud.
Statische batching: waarom het faalt bij gemengde belastingen
De klassieke aanpak voor batching is eenvoudig: wacht tot een batch gevuld is, stuur ze allemaal tegelijk, het model verwerkt ze parallel, de resultaten worden teruggegeven. Dan wachten op de volgende batch.
Het probleem ontstaat wanneer verzoeken van verschillende lengte zijn — wat de dagelijkse realiteit is. Het ene verzoek wil een eenregelig antwoord, het andere genereert 500 tokens. Statische batching wacht totdat het langste verzoek in de batch klaar is. GPU-kernen die al met een nieuw verzoek hadden kunnen beginnen, staan werkeloos stil. Het effectieve GPU-gebruik daalt naar 20–40 % bij een typische gemengde belasting.
In de praktijk betekent dit dat de GPU waarvoor u betaalt het grootste deel van de tijd staat te wachten. Niet op data, niet op het netwerk — op één traag verzoek.
Continuous batching: de batch dynamisch aanvullen
Continuous batching lost dit probleem anders op. In plaats van te wachten tot een batch klaar is, wordt elke gegenereerd token een kans: de server kijkt in de wachtrij van openstaande verzoeken en voegt direct nieuwe toe aan de huidige batch. Een verzoek dat net klaar is, maakt onmiddellijk plaats voor een nieuw — zonder onderbreking.
Het resultaat is dramatisch: op dezelfde hardware behaalt continuous batching doorgaans 2–3× hogere throughput ten opzichte van statische batching bij gemengde belasting. Niet omdat de GPU sneller is, maar omdat onbenutte cycli worden opgevuld met nieuwe verzoeken.
vLLM, SGLang en TGI implementeren continuous batching native. Ollama doet dit niet volledig — het is geoptimaliseerd voor single-user desktopgebruik, niet voor productie-multi-user-belastingen. Voor een team met een tiental gelijktijdige gebruikers is het verschil merkbaar: bij acht parallelle verzoeken is vLLM aanzienlijk sneller dan Ollama op dezelfde hardware. Een uitgebreide vergelijking van serving frameworks staat in het artikel vLLM vs SGLang vs Ollama.
KV-cache: waar VRAM "verdwijnt" bij lange contexten
Elk token dat het model verwerkt — zowel in de prefill- als in de decode-fase — produceert een sleutel- en waardevector voor elke attention-laag. Deze vectoren moeten worden opgeslagen zodat ze bij het genereren van het volgende token niet opnieuw berekend hoeven te worden. Deze opslag heet KV-cache.
De omvang van de KV-cache groeit lineair met de contextlengte. Ter indicatie: een 70B-model met een contextvenster van 128K heeft alleen voor de KV-cache al tientallen gigabytes nodig — bovenop de VRAM die het model zelf inneemt. Voor vier parallelle verzoeken met dezelfde contextlengte is dat vier keer zoveel.
Moderne modellen implementeren doorgaans Grouped Query Attention (GQA), waarmee de KV-cache-omvang drastisch wordt verminderd ten opzichte van klassieke multi-head attention, doordat groepen "query"-koppen gemeenschappelijke "key"- en "value"-vectoren delen. De meeste actuele modelfamilies (Llama, Qwen, Mistral) beschikken over GQA — het is tegenwoordig standaard, niet uitzonderlijk.
Een verdere knop is KV-cache-kwantisatie: de nauwkeurigheid van KV-vectoren terugbrengen naar INT8 of FP8 kan de omvang halveren met minimaal kwaliteitsverlies in de uitvoer. Productie-serving-frameworks ondersteunen dit als optionele optimalisatie.
Praktische consequentie: als uw VRAM tekortschiet en u werkt met lange contexten, is de KV-cache de eerste plek om te zoeken. Voordat u een GPU bijkoopt, loont het te meten hoeveel VRAM de KV-cache bij uw typische belasting werkelijk inneemt.
PagedAttention: virtueel pagineren voor de KV-cache
Naïef KV-cachebeheer introduceert nog een probleem: het reserveert geheugen voor de maximaal mogelijke contextlengte vooraf, ook als de meeste verzoeken veel korter zijn. Stel dat u 128K slots toewijst omdat het model dat technisch aankan, maar het gemiddelde verzoek slechts 2000 tokens beslaat. Het grootste deel van het gereserveerde geheugen staat leeg — en door fragmentatie is het niet efficiënt te hergebruiken.
PagedAttention, de sleutelinnovatie van vLLM, lost dit op met inspiratie uit besturingssystemen. Net zoals een OS RAM pagineert in fysieke blokken en die virtueel mapt, verdeelt PagedAttention de KV-cache in blokken van vaste grootte (pagina's) die dynamisch worden toegewezen naargelang een verzoek daadwerkelijk tokens genereert. Geheugen wordt on-demand gealloceerd, niet vooraf.
Het resultaat: KV-cache-fragmentatie daalt van typisch 60–80 % verspilling naar minder dan 4 %. Dat betekent dat dezelfde VRAM een veelvoud aan parallelle verzoeken aankan, of hetzelfde aantal verzoeken met langere context.
Voor productie-deployment is dit een verschil dat rechtstreeks van invloed is op hoeveel GPU's u nodig heeft — en dus op de servingkosten.
Latentie vs. throughput: het zijn niet hetzelfde
Een van de meest voorkomende begripsverwarringen: optimaliseren voor throughput en optimaliseren voor latentie zijn deels tegenstrijdige doelen.
Throughput meet hoeveel tokens (of verzoeken) de server per seconde aggregaat genereert — relevant voor batch-workloads, offline documentverwerking, API's met hoog volume.
Latentie meet hoe snel een specifieke gebruiker zijn antwoord ontvangt — TTFT en de totale tijd tot het einde van de generatie. Relevant voor interactieve applicaties, chatbots en copilots.
Het probleem ontstaat wanneer continuous batching te agressief wordt ingezet: de server kan de verzending van een antwoord bewust uitstellen om een grotere batch te kunnen vullen — hogere throughput, slechtere latentie voor de individuele gebruiker. De meeste productie-frameworks hebben parameters (--max-num-seqs, scheduler-delay-factor) waarmee deze afweging op prioriteit kan worden ingesteld.
Voor interactieve applicaties is de juiste strategie doorgaans om TTFT te prioriteren — een gebruiker die binnen twee seconden de eerste tokens ziet, ervaart het systeem als "snel", ook als de volledige generatie langer duurt. Streaming van de respons (SSE of WebSocket) verbetert deze beleving verder zonder de werkelijke throughput te wijzigen.
Bij het vergelijken van serving-oplossingen: SGLang behaalt op prefix-heavy workloads ruwweg een vijfde lagere TTFT dan vLLM op dezelfde hardware — een merkbaar verschil voor interactieve applicaties. Bij batch-serving is het verschil kleiner.
Prefix caching en het delen van de KV-cache
Wanneer meerdere verzoeken beginnen met hetzelfde systeemprompt — wat bij productieapplicaties heel gewoon is — herberekent elk verzoek hetzelfde prefix opnieuw. Dat is verspilling: prefill van dezelfde 500 tokens systeemprompt duizend keer per dag.
Prefix caching (ook wel prompt caching) lost dit op: de server slaat de KV-resultaten van het prefix op en leest ze bij een volgend verzoek met een identiek begin rechtstreeks uit de cache in plaats van ze te herberekenen. Het effect is tweeledig — het verlaagt zowel de latentie (TTFT voor een gecachet prefix is vrijwel nul) als de rekenkosten.
SGLang implementeert dit via RadixAttention — een LRU-cache van KV-waarden georganiseerd in een radixboom, die automatisch het langste gemeenschappelijke prefix tussen verzoeken vindt en deelt. Bij workloads met lange herhalende prefixen (RAG met dezelfde context, multi-turn chatbot met hetzelfde systeemprompt) is de throughputwinst meetbaar.
Aan de cloud-API-kant implementeren aanbieders een vergelijkbare functie: automatische prompt caching verlaagt de kosten voor invoertokens bij herhalende prefixen met ruwweg 50–90 %. Dit is ook relevant voor hybride architecturen waarbij sommige verzoeken naar de cloud gaan — een correct gestructureerde prompt (constant systeemprompt vooraan, dynamische inhoud achteraan) kan de factuur aanzienlijk drukken. Het artikel Prompt caching en kosten gaat hier uitgebreid op in.
Meer uit één GPU halen zonder extra hardware
Een overzicht van de praktische knoppen die wij in deployments tegenkomen — in volgorde van typische impact:
- 1.Stap over op een productie-serving-framework — als u
Ollamadraait met meerdere gelijktijdige gebruikers, is de overstap naarvLLMofSGLangde meest ingrijpende verbetering die u kunt doorvoeren. Continuous batching en PagedAttention zijn ingebouwd.
- 1.Stel de juiste kwantisatie in — Q4_K_M of AWQ 4-bit verkleint de geheugenvoetafdruk van het model aanzienlijk ten opzichte van FP16 (doorgaans tot een derde à een kwart), wat VRAM vrijmaakt voor grotere batches, bij een kwaliteitsverlies van minder dan 5–8 %. Meer over de formaten in het artikel Kwantisatie van LLM (GGUF, AWQ, GPTQ).
- 1.Zet KV-cache-kwantisatie aan — FP8 of INT8 KV-cache halveert de geheugenvoetafdruk met minimale invloed op de uitvoer. In
vLLMis dit de parameter--kv-cache-dtype.
- 1.Optimaliseer voor uw context — als uw verzoeken gemiddeld slechts 20 % van de ingestelde maximale contextlengte benutten, maakt het verlagen van
--max-model-lenVRAM vrij voor meer parallelle verzoeken.
- 1.Benut prefix caching — als het systeemprompt of de RAG-context gelijk is over verzoeken heen, stuur ze dan naar een framework met RadixAttention (SGLang) of zet prefix caching aan in
vLLM.
- 1.Houd het werkelijke GPU-gebruik bij —
nvidia-smi dmonen de metreken van het serving framework laten zien waar het knelpunt zit. Laag GPU-rekengebruik bij hoge latentie signaleert doorgaans een probleem met KV-cachebeheer of suboptimale batching.
Wanneer meerdere GPU's zinvol zijn
Horizontale schaling (tensor-parallelisme over meerdere GPU's) is gerechtvaardigd in twee situaties: het model past fysiek niet in één GPU, zelfs niet na kwantisatie, of u heeft alle optimalisaties op één GPU uitgeput en de throughput is nog steeds onvoldoende.
Tensor-parallelisme (--tensor-parallel-size 2 in vLLM) verdeelt de modelmatrices over GPU's — elke GPU verwerkt een deel van de berekening, de resultaten worden gesynchroniseerd. De schaling verloopt bijna lineair voor de prefill-fase, minder efficiënt voor decode (vanwege synchronisatie-overhead).
Voor de meeste bedrijfsdeployments met één model en tientallen gelijktijdige gebruikers is een goed geconfigureerde enkele GPU met het juiste serving framework krachtiger en economisch voordeliger dan twee GPU's met Ollama. Hardware bijkopen vóór softwareoptimalisatie is een veelgemaakte, maar onnodige vergissing.
Voor de vraag welke GPU u concreet moet kiezen en hoeveel VRAM u nodig heeft voor een gegeven model en belasting, verwijzen we naar het praktische overzichtsartikel Welke GPU voor LLM-inferentie.
Veelgestelde vragen
Wat is TTFT en waarom is het belangrijk?
TTFT (Time to First Token) is de tijd tussen het verzenden van een verzoek en het moment waarop de server begint met het streamen van de eerste tokens van het antwoord. Voor interactieve applicaties (chatbots, copilots) is TTFT bepalend voor de gepercipieerde snelheid — een gebruiker die binnen 1–2 seconden het eerste token ontvangt, ervaart het systeem als responsief, ook als het volledige antwoord 10 seconden kost. Een lange context en een volle KV-cache verlengen de TTFT, omdat de prefill-fase meer tijd vraagt.
Wat is het verschil tussen PagedAttention en continuous batching?
Het zijn twee orthogonale optimalisaties die verschillende problemen oplossen. PagedAttention lost geheugenbeheer op — het vermindert KV-cache-fragmentatie door deze dynamisch in pagina's toe te wijzen in plaats van vooraf te reserveren. Continuous batching lost requestplanning op — in plaats van te wachten tot een batch klaar is, worden nieuwe verzoeken continu aan de huidige run toegevoegd. Beide werken samen en zijn geïmplementeerd in productie-frameworks als vLLM.
Loont prefix caching ook voor kleinere deployments?
Ja, als u een consistent systeemprompt of RAG-context heeft die zich herhaalt over verzoeken heen. Zelfs bij een tiental gelijktijdige gebruikers kan prefix caching de TTFT met 40–70 % verlagen voor verzoeken met een lang gecachet prefix. Aan de cloud-API-kant heeft dit direct invloed op de kosten — een correct gestructureerde prompt met een constant prefix kan tientallen procenten besparen op invoertokens.
Wanneer is het zinvol om van Ollama naar vLLM over te stappen?
Zodra u meer dan één gelijktijdige gebruiker heeft in een productieomgeving. Ollama is uitstekend voor de ontwikkelaarsdesktop, prototyping en single-user lokale toegang. Bij meerdere parallelle verzoeken leveren continuous batching en PagedAttention in vLLM aanzienlijk hogere throughput voor dezelfde hardwarekosten. De overstap is relatief eenvoudig — vLLM biedt een OpenAI-compatibele API, zodat het aanpassen van de URL en de sleutel doorgaans volstaat.
Hoe beïnvloedt de contextlengte de throughput?
Lineair en aanzienlijk. Een langere context betekent een grotere KV-cache per verzoek, wat het aantal verzoeken dat gelijktijdig in de beschikbare VRAM past direct verlaagt. Een model met een contextvenster van 1M tokens heeft voor één lang verzoek ruwweg honderd gigabytes aan KV-cache nodig — onhaalbaar voor gangbare productie-GPU's. Voor de meeste reële use cases is RAG met kortere context economisch aanzienlijk voordeliger dan een lang contextvenster gevuld met het volledige document. Meer over de keuze tussen RAG en lange context staat in het artikel Contextvenster — wanneer helpen 1M tokens.
*MP Industrial Solutions helpt bedrijven bij het ontwerpen van een LLM-serving-architectuur die past bij de belasting en het budget — van de keuze voor het serving framework tot de configuratie van KV-cache en kwantisatie. Als u te maken heeft met een trager wordend bestaand deployment of een productie-deployment plant, bekijken we graag de concrete cijfers en stellen een optimalisatie voor.*
