Firma nasadí lokálny 13B model, zapne ho pre desať interných používateľov. Prvý týždeň beží v pohode. Potom príde druhý tím, pridá ďalší use-case, počet súbežných requestov vyskočí na 20–30 a odozva sa zdvihne z troch sekúnd na 40. Niekto navrhne kúpiť silnejší GPU. Kúpia. Odozva klesne na 25 sekúnd. Problém nie je hardware — problém je to, čo sa deje s KV-cache a dávkovaním pod kapotou.
Throughput a latencia LLM inferencie nie sú len hardwarová otázka. Sú architektonická. Ako serving framework spravuje pamäť, ako batuje requesty, ako zdieľa medzivýsledky — to rozhoduje, či z jednej GPU dostanete efektivitu zodpovedajúcu jej cenovke, alebo či platíte za výkon, ktorý sa nikdy nevyužije. Tento článok vysvetlí mechanizmy a ukáže, kde sú páky na zlepšenie bez nutnosti dokúpiť hardware.
Kde sa stráca výkon: anatomia LLM inferencie
Aby sme pochopili, kde sa dá optimalizovať, treba najprv pochopiť, čo sa pri generovaní textu deje. LLM inference prebieha v dvoch fázach.
Prefill fáza spracuje celý vstupný prompt naraz — ide o paralelizovateľnú operáciu, ktorá zaťaží GPU ako maticová násobenie. Výsledkom sú kľúčové a hodnotové vektory (KV) pre každý token vstupu, ktoré sa uložia do pamäte. Čas potrebný na túto fázu sa nazýva TTFT — Time to First Token. Práve TTFT rozhoduje, kedy používateľ uvidí prvý znak odpovede.
Decode fáza generuje tokeny jeden po druhom, pričom každý nový token závisí od všetkých predchádzajúcich. Ide o sekvenčnú operáciu — nedá sa paralelizovať v rámci jedného requestu. Rýchlosť generovania sa meria v tokenoch za sekundu (tokens/sec).
Kľúčový insight: prefill je compute-bound (zaťažuje GPU jadra), decode je memory-bound (zaťažuje šírku pásma pamäte). Tieto dve fázy majú rôzne bottlenecky a vyžadujú rôzne optimalizácie. Väčšina naivných implementácií obetuje throughput pre jednoduchosť.
Statický batching: prečo nefunguje pri zmiešaných záťažiach
Klasický prístup k dávkovaniu je jednoduchý: počkáme, kým sa naplní dávka requestov, odošleme ich všetky naraz, model ich spracuje paralelne, výsledky sa vrátia. Potom sa čaká na ďalšiu dávku.
Problém nastane, keď sú requesty rôzne dlhé — čo je bežná realita. Jeden request chce jednoriadkovú odpoveď, iný generuje 500 tokenov. Statický batching čaká, kým najdlhší request v dávke neskončí. Ostatné GPU jadra, ktoré mohli začať nový request, sedia nečinne. Efektívne využitie GPU klesá na 20–40 % pri typickej zmiešanej záťaži.
Na reálnych workloadoch to znamená, že GPU, za ktorú platíte, väčšinu času čaká. Nie na data, nie na sieť — čaká na jeden pomalý request.
Continuous batching: dynamické dopĺňanie dávky
Continuous batching rieši tento problém inak. Namiesto čakania na koniec dávky sa každý vygenerovaný token stáva príležitosťou: server sa pozrie do fronty čakajúcich requestov a okamžite pridá nové do aktuálnej dávky. Request, ktorý práve skončil, uvoľní miesto pre nový — bez prestávky.
Výsledok je dramatický: na rovnakom hardware dosahuje continuous batching typicky 2–3× vyšší throughput oproti statickému batchingu pri zmiešanej záťaži. Nie preto, že by bol GPU rýchlejší, ale preto, že sa nevyužité cykly zapĺňajú novými requestmi.
vLLM, SGLang a TGI implementujú continuous batching natívne. Ollama ho v plnej miere neimplementuje — je optimalizovaný pre single-user desktop použitie, nie pre produkčné multi-user záťaže. Pre tím s desiatkou súbežných používateľov je rozdiel znateľný: na ôsmich paralelných requestoch je vLLM podstatne výkonnejší ako Ollama za rovnaký hardware. Podrobné porovnanie serving frameworkov je v článku vLLM vs SGLang vs Ollama.
KV-cache: kde "zmizne" VRAM pri dlhých kontextoch
Každý token, ktorý model spracuje — či už v prefill alebo decode fáze — produkuje kľúčový a hodnotový vektor pre každú attention vrstvu. Tieto vektory sa musia uložiť, aby sa pri generovaní ďalšieho tokenu nemuseli prepočítavať. Toto úložisko sa nazýva KV-cache.
Veľkosť KV-cache rastie lineárne s dĺžkou kontextu. Pre orientáciu: 70B model pri 128K kontextovom okne potrebuje na samotný KV-cache rádovo desiatky gigabajtov — nad rámec VRAM, ktorú model samotný zaberá. Pre štyri paralelné requesty pri rovnakej dĺžke kontextu sa to znásobí štvornásobne.
Moderné modely väčšinou implementujú Grouped Query Attention (GQA), ktoré dramaticky znižuje veľkosť KV-cache oproti klasickému multi-head attention tým, že skupiny "query" hláv zdieľajú spoločné "key" a "value" vektory. Väčšina aktuálnych rodín (Llama, Qwen, Mistral) GQA má — je to dnes štandard, nie výnimka.
Ďalšia páka je KV-cache kvantizácia: zníženie presnosti KV vektorov na INT8 alebo FP8 môže zmenšiť ich veľkosť na polovicu s minimálnou stratou kvality výstupov. Produkčné serving frameworky toto podporujú ako voliteľnú optimalizáciu.
Praktický dôsledok: ak vám VRAM nestačí a uvažujete o dlhom kontexte, KV-cache je prvé miesto, kde treba hľadať príčinu. Pred dokúpením GPU sa oplatí zmerať, koľko VRAM KV-cache skutočne zaberá pri vašej typickej záťaži.
PagedAttention: virtuálne stránkovanie pre KV-cache
Naivná správa KV-cache robí ďalší problém: rezervuje pamäť pre maximálnu možnú dĺžku kontextu vopred, aj keď väčšina requestov je oveľa kratšia. Predstavte si, že alokujete 128K slotov, pretože model to technicky zvládne, no priemerný request je 2000 tokenov. Väčšina alokovanej pamäte leží prázdna — a kvôli fragmentácii sa nedá efektívne recyklovať.
PagedAttention, kľúčová inovácia vLLM, rieši tento problém inšpiráciou z operačných systémov. Rovnako ako OS stránkuje RAM do fyzických blokov a mapuje ich virtuálne, PagedAttention rozdeľuje KV-cache na bloky pevnej veľkosti (stránky), ktoré sa prideľujú dynamicky podľa toho, koľko tokenov request skutočne vygeneroval. Pamäť sa alokuje on-demand, nie vopred.
Výsledok: fragmentácia KV-cache klesá z typických 60–80 % plytvania na pod 4 %. To znamená, že rovnaká VRAM obsluží násobne viac paralelných requestov, alebo rovnaký počet requestov s dlhším kontextom.
Pre produkčné nasadenie je toto rozdiel, ktorý priamo ovplyvňuje, koľko GPU potrebujete — a teda aj náklady na serving.
Latencia vs. throughput: nie je to to isté
Jeden z najčastejších pojmových zmätkov: optimalizácia throughputu a optimalizácia latencia sú čiastočne protichodné ciele.
Throughput meria, koľko tokenov (alebo requestov) server vygeneruje za sekundu v agregáte — zaujíma vás pri batch workloadoch, offline spracovaní dokumentov, API s vysokým volumom.
Latencia meria, ako rýchlo dostane konkrétny používateľ svoju odpoveď — TTFT a celkový čas do konca generovania. Zaujíma vás pri interaktívnych aplikáciách, chatbotoch, copilotoch.
Problém nastáva, keď sa continuous batching aplikuje agresívne: server môže zámerne pozdržať odoslanie odozvy, aby stihnul naplniť väčšiu dávku — vyšší throughput, horšia latencia pre jednotlivca. Väčšina produkčných frameworkov má parametre (--max-num-seqs, scheduler-delay-factor), ktorými sa tento trade-off nastavuje podľa prioritít.
Pre interaktívne aplikácie je obvykle správna stratégia uprednostniť TTFT — používateľ, ktorý vidí prvé tokeny do dvoch sekúnd, vníma systém ako "rýchly" aj keď celková generácia trvá dlhšie. Streaming odozvy (SSE alebo WebSocket) toto vnímanie ďalej zlepšuje bez zmeny skutočného throughputu.
Pri porovnávaní serving riešení: SGLang dosahuje na prefix-heavy workloadoch rádovo o pätinu nižší TTFT ako vLLM pri rovnakom hardware, čo je pri interaktívnych aplikáciách znateľný rozdiel. Pre batch serving je rozdiel menší.
Prefix caching a zdieľanie KV-cache
Keď viacero requestov začína rovnakým systémovým promptom — čo je pri produkčných aplikáciách bežné — každý request prepočítava rovnaký prefix odznova. To je plytvanie: prefill rovnakých 500 tokenov systémového promptu 1000-krát denne.
Prefix caching (alebo prompt caching) rieši tento problém: server si uloží KV výsledky prefixu a pri ďalšom requeste so zhodným začiatkom ich len prečíta z cache namiesto prepočítania. Efekt je dvojaký — znižuje latenciu (TTFT pre cached prefix je blízky nule) aj výpočtové náklady.
SGLang implementuje toto cez RadixAttention — LRU cache KV hodnôt organizovanú do radixového stromu, kde sa automaticky nachádza a zdieľa najdlhší spoločný prefix medzi requestmi. Na workloadoch s dlhými opakovanými prefixmi (RAG s rovnakým kontextom, multi-turn chatbot s rovnakým systémovým promptom) je zlepšenie throughputu merateľné.
Na strane cloud API poskytovatelia implementujú analogickú funkciu: automatické prompt caching znižuje náklady na vstupné tokeny pri opakovaných prefixoch rádovo o 50–90 %. Toto je relevantné aj pre hybridné architektúry, kde niektoré requesty idú na cloud — správne štruktúrovanie promptu (konštantný systémový prompt vpredu, dynamický obsah vzadu) môže výrazne znížiť faktúru. Detailne sa tomu venuje článok Prompt caching a cost.
Ako z jednej GPU dostať viac bez dokúpenia hardware
Zhrnutie praktických pákov, ktoré vídame v nasadeniach — v poradí podľa typického dopadu:
- 1.Prejdite na produkčný serving framework — ak bežíte na
Ollamas viacerými súbežnými používateľmi, prechod navLLMaleboSGLangje najvýraznejšia zmena, akú môžete urobiť. Continuous batching a PagedAttention sú zabudované.
- 1.Nastavte správnu kvantizáciu — Q4_K_M alebo AWQ 4-bit výrazne zmenší pamäťový footprint modelu oproti FP16 (typicky na tretinu až štvrtinu), čo uvoľní VRAM pre väčšie dávky, pri strate kvality pod 5–8 %. Viac o formátoch v článku Kvantizácia LLM (GGUF, AWQ, GPTQ).
- 1.Zapnite KV-cache kvantizáciu — FP8 alebo INT8 KV-cache zmenší pamäťový footprint na polovicu s minimálnym vplyvom na výstupy. V
vLLMje to parameter--kv-cache-dtype.
- 1.Optimalizujte pre váš kontext — ak vaše requesty priemernou dĺžkou využívajú iba 20 % nastaveného maximálneho kontextu, zníženie
--max-model-lenuvoľní VRAM pre viac paralelných requestov.
- 1.Využite prefix caching — ak systémový prompt alebo RAG kontext je rovnaký naprieč requestmi, nasmerujte ich na framework s RadixAttention (SGLang) alebo zapnite prefix caching vo
vLLM.
- 1.Sledujte skutočné využitie GPU —
nvidia-smi dmona metriky serving frameworku ukážu, kde je bottleneck. Nízke využitie GPU výpočtovej kapacity pri vysokej latencii typicky signalizuje problém so správou KV-cache alebo suboptimálny batching.
Kedy má zmysel viacero GPU
Horizontálne škálovanie (tensor parallelizmus naprieč viacerými GPU) je opodstatnené v dvoch situáciách: model sa fyzicky nezmestí do jednej GPU ani po kvantizácii, alebo ste vyčerpali všetky optimalizácie na jednej GPU a throughput stále nestačí.
Tensor parallelizmus (--tensor-parallel-size 2 vo vLLM) rozdelí matice modelu naprieč GPU — každá GPU spracuje časť výpočtu, výsledky sa synchronizujú. Škáluje takmer lineárne pre prefill fázu, menej efektívne pre decode (kvôli synchronizačnej réžii).
Pre väčšinu firemných nasadení s jedným modelom a desiatkami súbežných používateľov je dobre nakonfigurovaná jedna GPU so správnym serving frameworkom výkonnejšia a ekonomicky prijateľnejšia ako dve GPU s Ollama. Dokúpenie hardware pred optimalizáciou softvéru je bežná, ale zbytočne drahá chyba.
Pre otázku, akú GPU konkrétne zvoliť a koľko VRAM potrebujete pre daný model a záťaž, odkazujeme na praktický rozcestník v článku Aká GPU na inferenciu LLM.
Časté otázky
Čo je TTFT a prečo na ňom záleží?
TTFT (Time to First Token) je čas od odoslania requestu po to, kedy server začne streamovať prvé tokeny odpovede. Pre interaktívne aplikácie (chatboty, copiloty) je TTFT rozhodujúci pre percepciu rýchlosti — používateľ, ktorý dostane prvý token do 1–2 sekúnd, vníma systém ako responzívny aj keď celá odpoveď trvá 10 sekúnd. Dlhý kontext a plná KV-cache TTFT predlžujú, pretože prefill fáza trvá dlhšie.
Aký je rozdiel medzi PagedAttention a continuous batching?
Sú to dve ortogonálne optimalizácie, ktoré riešia rôzne problémy. PagedAttention rieši správu pamäte — znižuje fragmentáciu KV-cache tým, že ju alokuje dynamicky v stránkach namiesto rezervácie vopred. Continuous batching rieši schedulovanie requestov — namiesto čakania na koniec dávky pridáva nové requesty do aktuálneho behu priebežne. Obe fungujú spolu a sú implementované v produkčných frameworkoch ako vLLM.
Oplatí sa prefix caching aj pre menšie deployments?
Áno, ak máte konzistentný systémový prompt alebo RAG kontext, ktorý sa opakuje naprieč requestmi. Aj pri desiatke súbežných používateľov môže prefix caching znížiť TTFT o 40–70 % pre requesty s dlhým cachovaným prefixom. Na strane cloud API to priamo ovplyvňuje náklady — správne štruktúrovaný prompt s konštantným prefixom môže ušetriť desiatky percent na vstupných tokenoch.
Kedy má zmysel prejsť od Ollama na vLLM?
Keď máte viac ako jedného súbežného používateľa v produkčnom prostredí. Ollama je excelentný pre developer desktop, prototypovanie a single-user lokálny prístup. Pri viacerých paralelných requestoch continuous batching a PagedAttention vo vLLM poskytnú výrazne vyšší throughput za rovnakú cenu hardware. Prechod je relatívne jednoduchý — vLLM poskytuje OpenAI-kompatibilné API, takže zmena URL a kľúča zvyčajne postačí.
Ako ovplyvňuje dĺžka kontextu throughput?
Lineárne a výrazne. Dlhší kontext znamená väčší KV-cache na request, čo priamo znižuje počet requestov, ktoré sa zmestia do dostupnej VRAM naraz. Model s 1M kontextovým oknom potrebuje na jeden dlhý request rádovo sto gigabajtov KV-cache — čo je pre bežné produkčné GPU nedosiahnuteľné. Pre väčšinu reálnych use-casov je RAG s kratším kontextom ekonomicky výrazne výhodnejší ako dlhé kontextové okno naplnené celým dokumentom. Viac o rozhodovaní medzi RAG a dlhým kontextom v článku Context window — kedy 1M tokenov pomôže.
*MP Industrial Solutions pomáha firmám navrhnúť LLM serving architektúru primeranú záťaži a rozpočtu — od výberu serving frameworku po konfiguráciu KV-cache a kvantizácie. Ak riešite spomalenie existujúceho nasadenia alebo plánujete produkčný deployment, radi sa pozrieme na konkrétne čísla a navrhneme optimalizáciu.*
