Strona główna / Rozdział 7 · Model na produkcji
Ostatnia zmiana · 5 min czytania
Czemu GPU się nudzi
Przy generowaniu tekstu karta graficzna przez większość kroku czeka, aż wagi i KV cache przyjadą z pamięci, a liczy tylko przez ułamek tego czasu. To jedno zjawisko tłumaczy batching, kwantyzację, dekodowanie spekulacyjne i to, czemu tokeny wyjściowe są droższe od wejściowych.
Po ludzkuKucharz jest błyskawiczny, ale po każdy składnik biega do magazynu na drugim końcu budynku. Gotując dla jednej osoby, głównie biega. Gotując dla stu naraz, biega tyle samo, a gotuje sto razy więcej.
Zwiększaj batch i patrz, kiedy liczenie dogoni czekanie na pamięć. Potem przełącz na prefill
Jeden krok modelu
Model poglądowy: 8 mld parametrów w BF16 (16 GB wag), jedna H100 SXM z pamięcią 3,35 TB/s i ok. 400 TFLOPS realnie osiąganej mocy (katalogowo ok. 990 TFLOPS dense BF16). Czytanie i liczenie nakładają się w czasie, więc krok trwa tyle, ile dłuższe z nich. Pasek pomija KV cache i narzuty, więc liczby pokazują najlepszy przypadek; komunikat dolicza cache przy 4000 tokenów kontekstu na rozmowę.
Dlaczego decode jest memory-bound
- Krok decode daje jeden token na rozmowę, ale czyta z pamięci HBM wszystkie wagi. Model 8B w BF16 to 16 GB, które przy 3,35 TB/s czyta się ok. 4,8 ms. Sufit to więc ok. 210 tokenów na sekundę dla jednej rozmowy, niezależnie od mocy obliczeniowej karty.
- Intensywność arytmetyczna to liczba operacji na bajt przeczytany z pamięci. Mnożenie wektora przez wagi to 2 operacje (mnożenie i dodawanie) na parametr, czyli na 2 bajty w BF16: ok. 1 operacja na bajt na każdą rozmowę w batchu. H100 SXM potrzebuje ok. 295 operacji na bajt katalogowo (989 TFLOPS / 3,35 TB/s) i ok. 120 przy realnie osiąganych 400 TFLOPS. Jeśli uwzględnić same wagi, dopiero batch rzędu 100–300 rozmów zrównuje liczenie z czytaniem.
- Prefill przetwarza cały prompt w jednym przebiegu: wagi przeczytane raz obsługują tysiące tokenów, więc już jeden długi prompt w pełni zajmuje kartę. Prefill wyznacza czas do pierwszego tokena (TTFT), a decode – czas między kolejnymi tokenami.
Gdzie batch przestaje być darmowy
- Batch dzieli koszt czytania wag, ale nie KV cache’u: każda rozmowa czyta własny, więc attention w decode zostaje ograniczone pamięcią przy każdym rozmiarze batcha. W modelu 8B z GQA (jak Llama 3 8B) cache zajmuje ok. 0,125 MB na token, więc rozmowa z 32 tys. tokenów kontekstu to 4 GB. Szesnaście takich rozmów czyta w każdym kroku 64 GB cache’u i tylko 16 GB wag. Mechanizm opisuje temat „Pętla generowania i KV cache”.
- Za punktem przecięcia każda kolejna rozmowa wydłuża krok wszystkim: łączna przepustowość rośnie coraz wolniej, a czas między tokenami rośnie. Dostawca wybiera punkt na tej krzywej. Ruch interaktywny wymaga mniejszego batcha i szybkich tokenów, a ruch wsadowy można pakować ciasno.
- W modelu MoE jeden token czyta tylko wybranych ekspertów, ale tokeny z batcha rozchodzą się po różnych ekspertach, więc przy większym batchu czytasz prawie cały model (zobacz „Mixture of Experts”).
Co z tego wynika dla serwowania
- Prędkość jednego strumienia ≈ przepustowość pamięci / bajty czytane na token (aktywne wagi plus KV cache). Stąd kwantyzacja wag do 8 lub 4 bitów przyspiesza decode prawie proporcjonalnie (zobacz „Kwantyzacja”), a dekodowanie spekulacyjne daje kilka tokenów z jednego czytania wag (zobacz „Dekodowanie spekulacyjne”).
- Koszt na token to przede wszystkim rozmiar batcha. Continuous batching i PagedAttention istnieją po to, żeby w ograniczonej pamięci zmieścić jak największy batch (zobacz „Continuous batching”).
- Tokeny wyjściowe są w cennikach kilka razy droższe od wejściowych (u dużych dostawców zwykle 4–8 razy). Wejście przechodzi przez prefill równolegle, a każdy token wyjścia to osobny krok decode, który zajmuje miejsce w batchu.
- Prefill i decode mają inne wąskie gardła i przeszkadzają sobie na wspólnej karcie. Duże wdrożenia rozdzielają je na osobne pule (disaggregated serving): raport techniczny DeepSeek-V3 opisuje prefill na jednostkach po 32 GPU, a decode po 320 GPU. Taki podział obsługują też vLLM, SGLang i NVIDIA Dynamo. Na jednym serwerze konflikt łagodzi chunked prefill.
- Do decode kartę wybiera się po przepustowości i pojemności pamięci, nie po TFLOPS. H200 ma tę samą moc obliczeniową co H100, ale 4,8 TB/s i 141 GB pamięci, więc przy dużych modelach i długich kontekstach generuje szybciej. Blackwell B200 (w sprzedaży, stan na wrzesień 2026) ma 180 GB i 8 TB/s, ale jego moc obliczeniowa urosła tak samo (ok. 280 operacji na bajt w BF16), więc wnioski z tej strony dalej obowiązują.
Sprawdź się
Dlaczego decode jest memory-bound i co z tego wynika dla serwowania?
Każdy krok decode czyta z pamięci wszystkie wagi i KV cache, a liczy tylko jeden token na rozmowę: w BF16 to ok. 1 operacja na bajt, a H100 potrzebuje ok. 300, żeby wąskim gardłem stało się liczenie. Sufit prędkości jednej rozmowy to przepustowość pamięci podzielona przez bajty na token. Wagi przeczytane raz obsługują cały batch, więc batching podnosi przepustowość prawie za darmo, dopóki nie skończy się pamięć na KV cache albo budżet opóźnienia. Kwantyzacja i dekodowanie spekulacyjne przyspieszają decode. Z prefillem jest odwrotnie: ogranicza go moc obliczeniowa.
In English
Every decode step reads all the weights and the KV cache from memory but computes only one token per conversation: in BF16 that is about 1 operation per byte, while an H100 needs about 300 before compute becomes the bottleneck. The speed ceiling for one conversation is memory bandwidth divided by bytes read per token. Weights read once serve the whole batch, so batching raises throughput almost for free until KV cache memory or the latency budget runs out. Quantisation and speculative decoding speed up decode. Prefill is the opposite: it is compute-bound.
Pytania pogłębiające (4)
- Oszacuj maksymalną prędkość modelu 70B w FP8 na jednej H100.
- 70 GB wag przy 3,35 TB/s to ok. 21 ms na krok, czyli najwyżej ok. 48 tokenów na sekundę na rozmowę, zanim doliczysz KV cache. Na cache zostaje ok. 10 GB z 80, więc batch będzie mały. W praktyce taki model dzieli się na 2–4 karty (tensor parallelism), co sumuje przepustowość i pamięć.
- Masz SLO 50 ms między tokenami. Jak dobierasz rozmiar batcha?
- Czas kroku to w przybliżeniu (wagi + KV cache całego batcha) / przepustowość pamięci, dopóki liczenie się w nim mieści. Zwiększasz batch, aż czas kroku zbliży się do 50 ms, z zapasem na p99, albo zabraknie pamięci na cache. Dalej przepustowość rośnie już kosztem opóźnienia wszystkich.
- Po co rozdzielać prefill i decode na osobne maszyny?
- Prefill jest ograniczony mocą i przychodzi falami, decode jest ograniczony pamięcią i trwa ciągle. Na wspólnej karcie długi prefill zatrzymuje cudze generowanie, a osobne pule można skalować i dzielić na karty niezależnie. Ceną jest przesyłanie KV cache’u z puli prefill do puli decode.
- Wykorzystanie mocy obliczeniowej w decode wynosi kilka procent. To problem?
- Niekoniecznie. Przy małym batchu właściwą miarą jest wykorzystanie przepustowości pamięci (MBU): bajty wag i cache’u na krok podzielone przez czas kroku i szczytową przepustowość. Wykorzystanie mocy (MFU) optymalizujesz w prefillu i treningu.