## Pętla generowania i KV cache

*Jak model pisze i ile to kosztuje*

*Ostatnia zmiana: 28 września 2026*

Generowanie to pętla: jeden przebieg modelu daje jeden token, który trafia na koniec wejścia. KV cache trzyma klucze i wartości attention wszystkich wcześniejszych tokenów, więc każdy krok liczy tylko nowy token, a ceną jest pamięć GPU, która ogranicza długość kontekstu i liczbę równoległych rozmów.

**Po ludzku:** Piszesz list i przy każdym nowym słowie zerkasz do notatek z tego, co już napisałeś. Nie czytasz listu od nowa, ale notatki przeglądasz za każdym razem. KV cache to te notatki. Leżą na biurku, czyli w pamięci karty graficznej, i im dłuższy list, tym więcej miejsca zajmują.

*Interaktywny widżet na stronie: Przejdź krok po kroku z cache’em i bez. Porównaj, ile tokenów liczy każdy krok.*

### Co jest w KV cache’u i czemu to działa

- W każdej warstwie attention token ma trzy rzuty swojego stanu: zapytanie Q, klucz K i wartość V (temat „Attention”). Nowy token porównuje swoje Q z kluczami wszystkich wcześniejszych i bierze ważoną sumę ich wartości. KV cache to K i V wszystkich dotychczasowych tokenów, osobno dla każdej warstwy i głowy KV.
- Q nie trafia do cache’u. Zapytanie tokena jest potrzebne tylko w kroku, w którym ten token jest ostatni. Późniejsze tokeny czytają już tylko jego K i V.
- Cache można trzymać dzięki masce przyczynowej: K i V tokena *i* zależą tylko od tokenów 0…*i*, więc nowe tokeny ich nie zmieniają. To memoizacja. Bez niej krok *n* liczyłby od nowa wszystkie *n* tokenów, więc wygenerowanie *N* tokenów kosztowałoby pracę projekcji i MLP rosnącą z *N*² zamiast z *N* (a koszt attention z *N*³ zamiast z *N*²).
- Działa to też w drugą stronę: K i V tokena zależą od wszystkich tokenów przed nim i od jego pozycji (RoPE). Zmiana jednego tokena unieważnia cache wszystkiego za nim. Dlatego dostawca może ponownie użyć cache’u tylko dla identycznego prefiksu, a agent historię tylko dopisuje (temat „Prompt caching”).

### Ile pamięci zajmuje KV cache

- Na token: 2 (K i V) × warstwy × głowy KV × wymiar głowy × bajty. Llama 3.1 70B w FP16: 2 × 80 × 8 × 128 × 2 B = 327 680 B, ok. 0,33 MB. Pełne 128 tys. tokenów to 43 GB na jedną rozmowę, przy 141 GB samych wag.
- Kształt cache’u ustala architektura. GQA daje jedną parę K, V na grupę głów zapytań (MQA to skrajność: jedna para na warstwę). Llama 3 70B ma 8 głów KV na 64 głowy Q. Bez GQA cache 128 tys. tokenów zająłby 344 GB. MLA z DeepSeek zapisuje jeden skompresowany wektor na warstwę: w DeepSeek-V3 to 61 × 576 × 2 B ≈ 70 KB na token, prawie 5 razy mniej niż Llama 70B, choć model ma 671 mld parametrów.
- Okno przesuwne: część warstw widzi tylko ostatnie W tokenów, więc ich cache nie rośnie ponad W. Gemma 3 ma pięć takich warstw (W = 1024) na jedną pełną, co przy 128 tys. tokenów daje ok. 17% cache’u tego samego modelu z samymi pełnymi warstwami. Modele hybrydowe zastępują część warstw attention warstwami SSM o stałym rozmiarze stanu.
- Przy serwowaniu gotowego modelu da się jeszcze zmienić precyzję: cache w FP8 zajmuje połowę pamięci. To przybliżenie, więc sprawdź jakość na własnych ewaluacjach.

*Interaktywny widżet na stronie: Wybierz model, kontekst i liczbę rozmów. Zobacz, ile kart H100 zajmuje sam cache.*

### Prefill i decode

- Prefill przetwarza cały prompt w jednym przebiegu. Mnoży wagi przez macierz wszystkich tokenów naraz, więc każdy przeczytany bajt wag służy setkom tokenów i karta liczy pełną mocą. Tu powstaje cache promptu. Prefill wyznacza czas do pierwszego tokena (TTFT), a attention rośnie z kwadratem długości, więc bardzo długi prompt wydłuża TTFT szybciej niż liniowo.
- Decode daje jeden token na rozmowę na krok i mnoży te same wagi przez jeden wektor. W FP16 to ok. 1 operacja na bajt wag na każdą rozmowę w batchu, a H100 potrzebuje ok. 300, żeby wąskim gardłem stało się liczenie (temat „Czemu GPU się nudzi”). Czas kroku ≈ (wagi + cache wszystkich rozmów w batchu) / przepustowość pamięci.
- Wagi czyta się raz na cały batch, cache każdej rozmowy osobno. Dlatego serwery sklejają wiele rozmów w jeden krok (temat „Continuous batching”), długi kontekst jednej rozmowy spowalnia cały batch, a token wyjściowy kosztuje u dostawców kilka razy więcej niż wejściowy.

*Interaktywny widżet na stronie: Zwiększ kontekst i liczbę rozmów w batchu. Zobacz, kiedy czytanie cache’u zaczyna dominować nad czytaniem wag.*

### Cache na serwerze

- PagedAttention (vLLM) dzieli cache na bloki po kilkanaście tokenów, jak strony pamięci wirtualnej. Serwer nie rezerwuje miejsca na maksymalną długość, a wspólny prefiks wielu rozmów trzyma raz. Prompt caching to te same bloki zachowane po requeście i odszukane po hashu prefiksu.
- Gdy pamięci brakuje, serwer wstrzymuje część rozmów i albo zwalnia ich cache i później liczy go od nowa, albo zrzuca go do RAM-u lub na dysk i wczytuje z powrotem. Oba sposoby kosztują czas, ale nie zmieniają wyniku.
- Wyrzucanie części tokenów z cache’u (StreamingLLM, H2O: zostają pierwsze, ostatnie i te, które dostają najwięcej attention) oszczędza pamięć, ale jest przybliżeniem. Model traci dostęp do wyrzuconego tekstu, więc jakość na długich zadaniach spada.

### Sprawdź się

**Pytanie:** Czym jest KV cache i czym różni się prefill od decode?

**Krótka odpowiedź:** Generowanie to pętla: jeden przebieg modelu daje jeden token. Attention potrzebuje kluczy i wartości wszystkich wcześniejszych tokenów, a dzięki masce przyczynowej one się nie zmieniają, więc liczy się je raz i trzyma w pamięci GPU. Prefill liczy cały prompt równolegle, jest ograniczony obliczeniami i wyznacza czas do pierwszego tokena. Decode idzie token po tokenie i czeka na pamięć, bo każdy krok czyta wagi i cały cache. Cache to ok. 0,33 MB na token dla 70B z GQA, więc to on ogranicza kontekst i batch. Zmniejszają go GQA, MLA, okno przesuwne i FP8.

### Pytania pogłębiające

- **Od czego zależy czas do pierwszego tokena, a od czego prędkość pisania?** Czas do pierwszego tokena zależy od kolejki i od tego, ile promptu trzeba policzyć poza trafionym cache’em, bo to prefill. Prędkość pisania zależy od rozmiaru wag, długości kontekstu, liczby rozmów w batchu i przepustowości pamięci, bo to decode.
- **Czemu długi kontekst spowalnia pisanie, skoro każdy krok liczy jeden token?** Bo każdy krok czyta cały cache. Dla modelu 70B przy 128 tys. tokenów to 43 GB na krok, prawie jedna trzecia tego, co ważą wagi, a decode jest ograniczony przepustowością pamięci.
- **vLLM pada z braku pamięci przy długich kontekstach. Co robisz?** Policz budżet: wagi plus cache na token × maksymalny kontekst × liczba równoległych sekwencji. Potem ogranicz maksymalną długość i liczbę sekwencji, włącz cache w FP8 i współdzielenie prefiksów, a gdy to za mało, podziel model na więcej kart. Tensor parallelism dzieli cache GQA po głowach KV, więc najwyżej na tyle kart, ile jest głów KV (8 w Llama 3.1 70B). Cache MLA jest powielany na każdej karcie, dlatego modele w stylu DeepSeek używają zamiast tego data-parallel attention.
- **Jak zmniejszyć KV cache?** Przy projektowaniu modelu: GQA lub MQA, MLA, okno przesuwne, warstwy SSM. Przy serwowaniu: FP8, współdzielenie prefiksów, zrzut do RAM-u, wyrzucanie tokenów. Wyniku nie zmieniają tylko współdzielenie i zrzut.

### Źródła

- [kipply: Transformer Inference Arithmetic](https://kipp.ly/p/transformer-inference-arithmetic)
- [How To Scale Your Model: rozdział o inferencji](https://jax-ml.github.io/scaling-book/inference/)
- [DeepSeek-V2: skąd się wzięło MLA (arXiv)](https://arxiv.org/abs/2405.04434)
- [Efficient Memory Management for LLM Serving with PagedAttention (arXiv)](https://arxiv.org/abs/2309.06180)
- [Sebastian Raschka: Understanding and Coding the KV Cache in LLMs from Scratch (2025)](https://magazine.sebastianraschka.com/p/coding-the-kv-cache-in-llms)

Strona interaktywna: https://howaiworks.dev/pl/kvcache/
