KV cache a prompt cache: różnice i zastosowanie
Wyjaśniamy techniczne różnice między KV cache a prompt cache. Dowiedz się, dlaczego KV cache powoduje błędy ładowania modelu, a prompt cache wpływa wyłącznie na koszty API.
KV cache a prompt cache: krótka odpowiedź
KV cache oraz prompt cache dostawcy dzielą jedynie nazwę, nie mając ze sobą niemal nic wspólnego. KV cache to pamięć robocza dla pojedynczego żądania. Znajduje się ona w pamięci RAM lub VRAM serwera przez cały czas trwania żądania i rośnie wraz z długością kontekstu oraz liczbą jednocześnie przetwarzanych zapytań. Prompt caching u dostawcy to funkcja związana z rozliczeniami i opóźnieniami. Stały prefiks promptu jest przechowywany na serwerach dostawcy, a przy ponownym wysłaniu naliczana jest za niego obniżona opłata.
Jedno to pamięć, którą kupuje się jako sprzęt. Drugie to pamięć, którą przechowuje ktoś inny, pobierając za to opłatę.
Praktyczna różnica jest ważniejsza niż definicja. Zasoby KV cache mogą się wyczerpać; w takim przypadku model odmawia załadowania lub żądanie jest odrzucane. Zasoby prompt cache nie mogą się wyczerpać. Można jedynie nie trafić w pamięć podręczną, co skutkuje naliczeniem pełnej opłaty.
Zawartość pamięci podręcznej KV i przyczyny jej istnienia
Model typu transformer generujący token numer 500 musi uwzględnić wszystkie 499 poprzedzające go tokeny. Dla każdego z tych tokenów każda warstwa wymaga wektora klucza (key vector) oraz wektora wartości (value vector). Ponowne obliczanie wszystkich tych wartości dla każdego nowego tokena powodowałoby, że czas generowania rósłby kwadratowo wraz z długością sekwencji, dlatego środowisko uruchomieniowe przechowuje je w pamięci. Ten magazyn to KV cache (pamięć podręczna kluczy/wartości).
Jest to stan przypisany do konkretnego żądania, ponieważ budowany jest na podstawie dokładnej sekwencji tokenów tego żądania. Dwaj użytkownicy wysyłający różne prompty nie mogą współdzielić tej pamięci, chyba że środowisko uruchomieniowe stosuje mechanizm prefix caching, który jest odrębną funkcją opisaną w dalszej części.
Obsługa żądań odbywa się w dwóch fazach. Faza prefill odczytuje cały prompt i wypełnia pamięć podręczną, a jej wydajność jest ograniczona mocą obliczeniową. Faza decode generuje po jednym tokenie na raz i dopisuje je do pamięci podręcznej, a jej wydajność jest ograniczona przepustowością pamięci RAM. Ten podział jest powodem, dla którego przetwarzanie promptu i generowanie tokenów wykazują różne prędkości, gdy mierzy się liczbę tokenów na sekundę na własnym sprzęcie.
Ile pamięci zajmuje pamięć podręczna KV?
Nie należy szukać tabeli producenta. Rozmiar wynika z obliczeń, które można powtórzyć dla każdego modelu:
bytes per token = 2 * layers * kv_heads * head_dim * bytes per elementLiczba 2 uwzględnia klucz oraz wartość. Każda inna liczba pochodzi z pliku config.json modelu, opublikowanego na jego stronie w serwisie Hugging Face.
Weźmy pod uwagę model Llama 3.1 8B. Jego konfiguracja wskazuje num_hidden_layers 32 oraz num_key_value_heads 8. Wartość hidden_size wynosząca 4096, rozłożona na 32 głowice uwagi (attention heads), daje wymiar głowicy równy 128. W formacie f16 każdy element zajmuje 2 bajty:
2 * 32 * 8 * 128 * 2 = 131072 bytes = 128 KiB per tokenPomnóż to przez wymagany kontekst, a następnie przez liczbę żądań obsługiwanych jednocześnie.
The data behind this chart
[
{
"label": "2k context",
"one_request_gib": 0.25,
"four_requests_gib": 1
},
{
"label": "8k context",
"one_request_gib": 1,
"four_requests_gib": 4
},
{
"label": "32k context",
"one_request_gib": 4,
"four_requests_gib": 16
},
{
"label": "128k context",
"one_request_gib": 16,
"four_requests_gib": 64
}
]Przy kontekście 8k pamięć podręczna zajmuje 1 GiB. Przy 32k jest to 4 GiB, co mieści się w tym samym zakresie, co same wagi 4-bitowe. Przy pełnym kontekście modelu wynoszącym 128k, dla jednego żądania jest to 16 GiB, a 64 GiB, jeśli cztery żądania wypełnią ją w całości. Wagi nie uległy zmianie. Zmieniła się tylko pamięć podręczna.
Mechanizm Grouped Query Attention (GQA) znacząco wpływa na tę wartość. Llama 3.1 8B posiada 8 głowic klucza/wartości obsługujących 32 głowice zapytania, więc cztery głowice zapytania współdzielą jedną zapisaną parę klucz/wartość. Model, w którym num_key_value_heads jest równe num_attention_heads, zużywa czterokrotnie więcej pamięci podręcznej przy tej samej liczbie parametrów. Należy sprawdzić to pole przed założeniem, że dwa modele 8B mają taki sam koszt obsługi.
Dlaczego model działający przy 2k odmawia załadowania przy 32k
Środowisko uruchomieniowe rezerwuje pamięć podręczną KV w momencie ładowania modelu. Jej rozmiar jest określony przez skonfigurowaną długość kontekstu, a nie przez długość faktycznie wysyłanego promptu. Domyślne okno kontekstowe Ollama wynosi 4096 tokenów. Zwiększenie go do 32k powoduje żądanie przydzielenia 4 GiB dodatkowej pamięci, zanim nadejdzie choćby jeden token.
OLLAMA_CONTEXT_LENGTH=32768 ollama serveTo samo ustawienie dla sesji, z poziomu interaktywnego promptu:
ollama run llama3.1:8b
/set parameter num_ctx 32768Błąd wygląda inaczej w zależności od stosu technologicznego. vLLM sprawdza obliczenia przy starcie i odmawia uruchomienia:
ValueError: The model's max seq len (131072) is larger than the maximum number of tokens that can be stored in KV cache (78336). Try increasing `gpu_memory_utilization` or decreasing `max_model_len` when initializing the engine.Na serwerze VPS działającym wyłącznie na CPU takie sprawdzenie nie występuje, ponieważ przydział dotyczy zwykłej pamięci RAM systemu. Mechanizm out-of-memory killer jądra systemu przerywa proces, pozostawiając ślad w buforze pierścieniowym jądra:
dmesg -T | grep -i "killed process"Linia wskazująca na proces serwujący oznacza, że serwer obiecał więcej pamięci, niż posiadał. Rozwiązaniem jest mniejszy kontekst, a nie większy plik wymiany (swap): pamięć podręczna KV przeniesiona do pliku wymiany jest odczytywana przy każdym wygenerowanym tokenie, co spowalnia generowanie do poziomu bezużyteczności. Wybór rozsądnej wartości opisano w naszym przewodniku dotyczącym num_ctx i długości kontekstu w Ollama.
Wpływ współbieżności na liczbę
Każde żądanie w trakcie przetwarzania posiada własną pamięć podręczną KV. Jest to czynnik, który większość planów wydajnościowych pomija. Czterech użytkowników, z których każdy utrzymuje 32k kontekstu, potrzebuje łącznie 16 GiB pamięci, poza wagami modelu.
Środowiska uruchomieniowe różnią się pod względem rygorystyczności tego podejścia. Ollama oraz llama.cpp rezerwują zadeklarowany kontekst w momencie ładowania modelu, więc pamięć jest przydzielona niezależnie od tego, czy jest faktycznie wykorzystywana. vLLM dzieli pulę na bloki o stałym rozmiarze i przydziela je w miarę wzrostu żądania, dzięki czemu żądanie o długości 500 tokenów zajmuje miejsce odpowiadające tylko 500 tokenom. W obu przypadkach pula jest ograniczona, a po jej wyczerpaniu nowe żądania trafiają do kolejki zamiast być przetwarzane. Wpływ tego kolejkowania na czas odpowiedzi został omówiony w ilu współbieżnych użytkowników może obsłużyć samodzielnie hostowany LLM.
Cztery sposoby na zmniejszenie pamięci podręcznej KV
- Zmniejszenie długości kontekstu. Jest to najskuteczniejsza metoda, zazwyczaj najmniej kosztowna. Większość zadań typu chat nigdy nie zbliża się do limitu 32k.
- Kwantyzacja samej pamięci podręcznej. Domyślne ustawienie
OLLAMA_KV_CACHE_TYPEw Ollama tof16; obsługiwane są równieżq8_0, które zużywa około połowy pamięci, orazq4_0, które zużywa około jednej czwartej. Odpowiedniki w llama.cpp to-ctk q8_0oraz-ctv q8_0. - Wybór modelu z mniejszą liczbą głowic klucza/wartości lub mniejszą liczbą warstw. Przed pobraniem 40 GB wag należy zapoznać się z
config.json. - Obsługa mniejszej liczby żądań jednocześnie i kolejkowanie pozostałych.
Przy q4_0 wartość dla modelu Llama 3.1 8B spada ze 128 KiB na token do około 32 KiB, dzięki czemu kontekst 32k zajmuje około 1 GiB zamiast 4 GiB. Ta oszczędność nie jest darmowa. Klucze i wartości są przechowywane z mniejszą precyzją, dlatego przed wdrożeniem rozwiązania należy porównać wyniki wygenerowane dla własnych promptów.
Co faktycznie daje mechanizm cache'owania promptów u dostawcy
Cache'owanie promptów u dostawcy to odrębny produkt, rozliczany w inny sposób. Oznacza się stały prefiks, który dostawca przechowuje, a kolejne wywołania powtarzające ten sam prefiks są rozliczane według obniżonej stawki zamiast pełnej ceny za tokeny wejściowe.
Opublikowane przez Anthropic mnożniki, aktualne na sierpień 2026: zapis w cache z 5-minutowym okresem ważności kosztuje 1,25 ceny bazowej tokena wejściowego. Zapis 1-godzinny kosztuje 2 razy więcej, a odczyt z cache kosztuje 0,1 ceny bazowej. Po podstawieniu 20 000 tokenów promptu systemowego pod te wartości, rachunek staje się oczywisty.
The data behind this chart
[
{
"label": "No caching, every call",
"billed_token_equivalents": "20,000"
},
{
"label": "First call, 5 minute cache write",
"billed_token_equivalents": "25,000"
},
{
"label": "First call, 1 hour cache write",
"billed_token_equivalents": "40,000"
},
{
"label": "Every later call, cache hit",
"billed_token_equivalents": "2,000"
}
]Należy to interpretować jako działanie arytmetyczne. Premia za zapis 5-minutowy wynosi równowartość 5 000 tokenów przy pierwszym wywołaniu: 25,000 w porównaniu do 20,000 w przypadku wysyłki bez cache'owania. Każde kolejne wywołanie wewnątrz tego okna czasowego jest rozliczane jako 2,000 zamiast 20,000, co daje oszczędność 18 000 tokenów. Zatem 5-minutowy cache staje się opłacalny od drugiego wywołania.
Cache 1-godzinny to inne założenie. Przy zapisie naliczane jest 40,000, co stanowi premię równą 20 000 tokenów, więc aby inwestycja się zwróciła, wymagane są dwa trafienia w ciągu godziny. Jest to kwestia wzorca ruchu, a nie samego modelu. Pełne obliczenia, w tym sposób doboru okna czasowego, znajdują się w matematyce progu rentowności dla cache'owania promptów Claude.
Dwa szczegóły decydują o tym, czy cache w ogóle zadziała. Po pierwsze, prefiks krótszy niż minimalna długość wymagana przez model nie jest cache'owany bez żadnego komunikatu: na sierpień 2026 udokumentowane minimum wynosi 512 tokenów dla Claude Opus 5 oraz 1 024 tokeny dla Claude Sonnet 5, a krótsze żądanie jest przetwarzane normalnie bez zwracania błędu. Po drugie, czas życia jest mierzony od początku żądania, które zapisuje lub odczytuje wpis, a każdy odczyt odświeża go bez dodatkowych kosztów. Zatem obciążony punkt końcowy utrzymuje 5-minutowy cache w nieskończoność. Punkt końcowy wywoływany raz na dziesięć minut płaci premię za zapis przy każdym wywołaniu i nigdy nie odnosi korzyści.
Należy weryfikować odpowiedź, zamiast zakładać poprawność działania. Obiekt usage raportuje cache_creation_input_tokens oraz cache_read_input_tokens. Liczba odczytów równa zero przy każdym wywołaniu oznacza, że płacisz za zapisy, nie otrzymując żadnych korzyści.
Miejsce styku dwóch pamięci podręcznych
Długi prompt systemowy jest miejscem, w którym te dwa mechanizmy się spotykają, obciążając zasoby po obu stronach jednocześnie.
Lokalnie, prompt systemowy o długości 20 000 tokenów zajmuje około 2,4 GiB pamięci podręcznej KV na serwerze Llama 3.1 8B przy precyzji f16, przy czym dzieje się to oddzielnie dla każdego równoległego żądania, które go zawiera. Zdalnie, ten sam prefiks kosztuje jeden zapis do pamięci podręcznej, a następnie 0,1 kosztu wejścia przy każdym kolejnym wywołaniu. Koszt lokalny skaluje się wraz z liczbą użytkowników. Koszt zdalny skaluje się wraz z natężeniem ruchu i zeruje w czasie bezczynności.
Istnieje funkcja lokalna, która przypomina buforowanie promptów u dostawcy i jest z nim stale mylona: buforowanie prefiksów (prefix caching). Dokumentacja vLLM opisuje automatyczne buforowanie prefiksów jako przechowywanie „pamięci podręcznej KV istniejących zapytań, dzięki czemu nowe zapytanie może bezpośrednio użyć pamięci podręcznej KV, jeśli współdzieli ten sam prefiks z jednym z istniejących zapytań”. Serwer llama.cpp domyślnie utrzymuje pamięć podręczną promptów dla każdego slotu, a --cache-reuse N określa najmniejszy fragment, który będzie próbował ponownie wykorzystać.
Buforowanie prefiksów pozwala zaoszczędzić moc obliczeniową potrzebną na fazę prefill. Prompt systemowy o długości 20 000 tokenów jest przetwarzany raz, a nie przy każdym żądaniu, co znacząco skraca czas do uzyskania pierwszego tokena. W vLLM współdzielone bloki są ponownie wykorzystywane, a nie powielane, co poprawia również wykorzystanie pamięci. Mechanizm ten nigdy nie zmniejsza jednak pamięci podręcznej, którą należy utrzymywać dla aktualnie aktywnych tokenów. Utrzymywanie wag w pamięci między żądaniami jest powiązanym, ale odrębnym zagadnieniem, omówionym w utrzymywanie modelu Ollama w pamięci między żądaniami.
Co mierzyć na własnym serwerze
Załaduj model w docelowym kontekście, a następnie odczytaj rzeczywiste wartości zamiast polegać na szacunkach.
ollama ps
nvidia-smi --query-gpu=memory.used,memory.total --format=csv
free -gollama ps wyświetla załadowany model wraz z jego rozmiarem oraz informacją, czy działa on na GPU, czy na CPU. Jeśli model, który miał w całości mieścić się w pamięci GPU, wykazuje podział na CPU, oznacza to, że KV cache wypchnął jego część, co spowoduje odpowiedni spadek prędkości generowania. nvidia-smi podaje rzeczywistą wartość VRAM, a free -g wykonuje to samo zadanie na serwerze VPS działającym wyłącznie na CPU. Zwiększaj kontekst stopniowo, przeładowuj model i obserwuj zmiany wartości. Twoje obliczenia oraz raportowana wartość powinny być zbliżone. Jeśli tak nie jest, różnica zazwyczaj wynika z buforów obliczeniowych środowiska uruchomieniowego, a nie z błędu we wzorze.
Jeśli te liczby skłaniają Cię ku sprzętowi, którego wolałbyś nie wynajmować, porównanie z modelem płatności za token znajduje się w GPU VPS a tokeny API.
FAQ
Czy KV cache to to samo co prompt caching?
Nie. KV cache to pamięć przypisana do pojedynczego żądania wewnątrz procesu serwującego, przechowująca wektory kluczy i wartości dla każdego tokena w bieżącym kontekście. Znajduje się ona w pamięci RAM lub VRAM i jest zwalniana po zakończeniu żądania. Prompt caching u dostawcy to funkcja rozliczeniowa, która przechowuje stały prefiks promptu w infrastrukturze dostawcy i nalicza obniżoną opłatę przy jego ponownym wysłaniu. Brak miejsca w KV cache uniemożliwia załadowanie modelu. Brak prompt cache skutkuje jedynie wyższą fakturą i dłuższym czasem oczekiwania na pierwszy token.
Dlaczego model ładuje się przy kontekście 2k, a przy 32k kończy się błędem?
Ponieważ środowisko uruchomieniowe alokuje cały KV cache w momencie ładowania, rezerwując rozmiar zgodny ze skonfigurowaną długością kontekstu, a nie z długością wysyłanego promptu. Dla modelu Llama 3.1 8B w formacie f16 cache zajmuje 128 KiB na token, więc kontekst 2k kosztuje 0.25 GiB, a 32k kosztuje 4 GiB. Wagi mieszczą się w obu przypadkach. Problemem jest rezerwacja pamięci. vLLM zgłasza to jako ValueError, podając maksymalną liczbę tokenów, które mógłby przechować, i sugeruje zwiększenie gpu_memory_utilization lub zmniejszenie max_model_len. Na serwerze działającym wyłącznie na CPU mechanizm OOM killer jądra systemu przerywa proces, co można potwierdzić za pomocą dmesg -T | grep -i "killed process".
Jak obliczyć rozmiar KV cache dla mojego modelu?
Należy pomnożyć 2 przez liczbę warstw, liczbę głowic kluczy/wartości, wymiar głowicy oraz liczbę bajtów na element. Wynik to liczba bajtów na token. Następnie należy pomnożyć tę wartość przez długość kontekstu oraz liczbę jednoczesnych żądań. Liczbę warstw i głowic można odczytać z pliku config.json modelu. Dla formatów f16 lub bf16 należy przyjąć 2 bajty na element. Cache q8_0 zajmuje około połowę tej wartości, a q4_0 około jedną czwartą.
Czy prompt caching zmniejsza zapotrzebowanie na pamięć mojego serwera?
Prompt caching u dostawcy nie wpływa na sprzęt lokalny, ponieważ dane są przechowywane po stronie dostawcy. Lokalnym odpowiednikiem jest prefix caching, oferowany zarówno przez vLLM, jak i serwer llama.cpp. Funkcja ta ponownie wykorzystuje już obliczone wektory kluczy i wartości dla wspólnego prefiksu, co oszczędza moc obliczeniową fazy prefill i skraca czas do uzyskania pierwszego tokena. W vLLM współdzielone bloki są ponownie używane zamiast duplikowania, co poprawia również wykorzystanie pamięci. Żadna z tych funkcji nie zmniejsza cache wymaganego dla tokenów aktualnie przetwarzanych, więc obliczenia oparte na kontekście i współbieżności nadal wyznaczają minimalne wymagania.
Czy warto stosować caching dla promptu, który wysyłam tylko raz?
Nie. Zapis w cache kosztuje więcej niż zwykłe wejście – według stanu na sierpień 2026 r. jest to 1,25 stawki podstawowej dla opcji 5-minutowej – więc prefiks, którego nie wysyłasz ponownie w tym oknie czasowym, generuje czystą stratę. Caching opłaca się, gdy ten sam prefiks jest powtarzany, na przykład w przypadku długiego promptu systemowego lub dokumentu, do którego będziesz zadawać wiele pytań. Sprawdź cache_read_input_tokens w odpowiedzi API, aby potwierdzić, że uzyskujesz trafienia (hits), zamiast płacić za zapisy.