KV cache и prompt cache: в чем разница
Разбираем отличия KV cache и prompt cache. KV cache потребляет VRAM сервера и вызывает ошибку Out of Memory, тогда как prompt caching снижает стоимость API запросов.
KV cache и prompt cache: краткий ответ
KV cache и prompt cache у провайдера имеют общее слово в названии, но практически ничего общего в остальном. KV cache — это рабочая память для конкретного запроса. Она находится в RAM или VRAM вашего сервера в течение всего времени обработки одного запроса и увеличивается по мере роста длины контекста и количества одновременных запросов. Prompt caching у провайдера — это функция для оптимизации биллинга и задержек. Стабильный префикс вашего промпта сохраняется на серверах провайдера, и при повторной отправке вы платите за него по сниженной цене.
Первое — это память, которую вы покупаете как оборудование. Второе — это память, которую удерживает кто-то другой и берет с вас арендную плату.
Практическая разница важнее определений. У вас может закончиться KV cache, и в этом случае модель откажется загружаться или запрос будет отклонен. У вас не может закончиться prompt cache. Вы можете лишь не попасть в него, и тогда вы просто заплатите полную стоимость.
Что содержит KV cache и зачем он нужен
Трансформер, генерирующий токен номер 500, должен учитывать все 499 предыдущих токенов. Для каждого из этих токенов каждому слою требуются вектор ключа (key vector) и вектор значения (value vector). Повторное вычисление всех этих векторов для каждого нового токена привело бы к тому, что время генерации росло бы пропорционально квадрату длины последовательности, поэтому среда выполнения сохраняет их. Это хранилище называется KV cache (кэш ключей и значений).
Это состояние, специфичное для каждого запроса, так как оно строится на основе точной последовательности токенов конкретного запроса. Два пользователя, отправляющие разные промпты, не могут использовать его совместно, если только среда выполнения не поддерживает prefix caching — отдельную функцию, описанную далее.
Обслуживание запросов происходит в две фазы. Фаза prefill считывает весь ваш промпт и заполняет кэш; её скорость ограничена вычислительной мощностью. Фаза decode генерирует по одному токену за раз и добавляет их в кэш; её скорость ограничена пропускной способностью памяти. Именно из-за этого разделения обработка промпта и генерация токенов показывают разную скорость, когда вы измеряете количество токенов в секунду на своей системе.
Сколько памяти использует KV-кэш?
Не ищите таблицу от производителя. Размер вычисляется арифметически для любой модели:
bytes per token = 2 * layers * kv_heads * head_dim * bytes per elementЦифра 2 учитывает ключ и значение. Все остальные числа берутся из config.json модели, опубликованного на её странице в Hugging Face.
Возьмем Llama 3.1 8B. В её конфигурации указаны num_hidden_layers 32 и num_key_value_heads 8. hidden_size, равный 4096, распределенный по 32 головам внимания, дает размер головы 128. В формате f16 каждый элемент занимает 2 байта:
2 * 32 * 8 * 128 * 2 = 131072 bytes = 128 KiB per tokenУмножьте это на требуемый контекст, а затем на количество одновременных запросов.
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
}
]При контексте 8k кэш составляет 1 ГиБ. При 32k он составляет 4 ГиБ, что находится в том же диапазоне, что и сами 4-битные веса. При полном контексте модели 128k он составляет 16 ГиБ для одного запроса и 64 ГиБ, если четыре запроса заполняют его одновременно. Веса не изменились. Изменился только кэш.
Grouped query attention (GQA) значительно влияет на это число. У Llama 3.1 8B есть 8 голов ключей/значений, обслуживающих 32 головы запросов, поэтому четыре головы запросов совместно используют одну сохраненную пару ключ/значение. Модель, у которой num_key_value_heads равно num_attention_heads, использует в четыре раза больше кэша при том же количестве параметров. Проверьте это поле, прежде чем предполагать, что две модели 8B требуют одинаковых ресурсов для обслуживания.
Почему модель, работавшая на 2k, не загружается на 32k
Потому что среда выполнения резервирует KV-кэш при загрузке модели, исходя из настроенной длины контекста, а не из размера отправленного запроса. По умолчанию в Ollama размер окна контекста составляет 4096 токенов. Увеличьте его до 32k, и вы запросите 4 GiB дополнительной памяти еще до того, как поступит первый токен.
OLLAMA_CONTEXT_LENGTH=32768 ollama serveТа же настройка для сессии из интерактивной подсказки:
ollama run llama3.1:8b
/set parameter num_ctx 32768Ошибка выглядит по-разному в зависимости от стека. vLLM проверяет вычисления при запуске и отказывается работать:
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.На VPS только с CPU такой проверки нет, так как выделяется обычная системная RAM. OOM-killer (out-of-memory killer) ядра завершает процесс, оставляя запись в кольцевом буфере ядра:
dmesg -T | grep -i "killed process"Строка с названием вашего процесса обслуживания означает, что сервер пообещал больше памяти, чем было в наличии. Решение — уменьшить контекст, а не увеличивать файл подкачки: если KV-кэш выгружается на диск, он считывается при генерации каждого токена, из-за чего скорость генерации падает до неприемлемых значений. Выбор разумного значения описан в нашем руководстве по num_ctx и длине контекста в Ollama.
Как параллелизм влияет на показатели
Каждый активный запрос использует собственный KV cache. Это тот фактор, который чаще всего упускают при планировании ресурсов. Четырем пользователям, каждый из которых удерживает 32k контекста, требуется 16 GiB памяти, не считая весов модели.
Среды выполнения различаются по строгости этого требования. Ollama и llama.cpp резервируют запрошенный объем контекста при загрузке модели, поэтому память выделяется независимо от того, используется она или нет. vLLM разбивает пул на блоки фиксированного размера и распределяет их по мере роста каждого запроса, поэтому запрос на 500 токенов занимает память только для 500 токенов. В любом случае пул ограничен, и как только он заполняется, новые запросы встают в очередь, а не выполняются. О том, как эта очередь влияет на время отклика, подробно рассказано в сколько одновременных пользователей может обслуживать self-hosted LLM.
Четыре способа уменьшить размер KV-кэша
- Уменьшите длину контекста. Это самый эффективный рычаг, который обычно не требует затрат. Большинство чат-запросов даже близко не приближаются к 32k.
- Квантуйте сам кэш. По умолчанию в Ollama используется
OLLAMA_KV_CACHE_TYPE, что соответствуетf16. Также поддерживаетсяq8_0, который использует около половины памяти, иq4_0, использующий около четверти. Аналоги в llama.cpp — это-ctk q8_0и-ctv q8_0. - Выбирайте модель с меньшим количеством key/value heads или меньшим числом слоев. Ознакомьтесь с
config.json, прежде чем скачивать 40 GB весов. - Обслуживайте меньше запросов одновременно, а остальные ставьте в очередь.
При q4_0 показатель для Llama 3.1 8B снижается со 128 KiB на токен примерно до 32 KiB. Таким образом, 32k контекста занимают около 1 GiB вместо 4 GiB. Эта экономия не дается бесплатно. Ключи и значения хранятся с меньшей точностью, поэтому сравните вывод на своих собственных промптах, прежде чем переходить на этот режим.
Что на самом деле дает кэширование промптов у провайдера
Кэширование промптов у провайдера — это отдельный продукт с другой единицей учета. Вы помечаете стабильный префикс, провайдер сохраняет его, и последующие вызовы, в точности повторяющие этот префикс, тарифицируются по сниженной ставке вместо полной стоимости входных данных.
Опубликованные Anthropic коэффициенты на август 2026 года: запись в кэш с временем жизни 5 минут стоит в 1.25 раза дороже базовой цены входного токена. Запись на 1 час стоит в 2 раза дороже, а чтение из кэша — 0.1 от базовой цены. Если подставить в эти расчеты системный промпт объемом 20,000 токенов, выгода становится очевидной.
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"
}
]Рассматривайте это как арифметическую задачу. Надбавка за запись с 5-минутным кэшем составляет эквивалент 5,000 токенов при первом вызове: 25,000 против 20,000 при отправке без кэширования. Каждый последующий вызов в течение этого окна тарифицируется как 2,000 вместо 20,000, что дает экономию в 18,000 токенов. Таким образом, 5-минутный кэш становится выгодным начиная со второго вызова.
1-часовой кэш — это другая стратегия. При записи он тарифицирует 40,000, что составляет надбавку в 20,000 токенов, поэтому для выхода в плюс требуется два попадания в течение часа. Это вопрос к вашему паттерну трафика, а не к модели. Полный расчет, включая выбор окна, приведен в математике окупаемости кэширования промптов Claude.
Две детали определяют, попадете ли вы в кэш вообще. Во-первых, префикс короче минимально допустимой длины модели не кэшируется без уведомления: на август 2026 года задокументированный минимум составляет 512 токенов для Claude Opus 5 и 1,024 токена для Claude Sonnet 5, при этом более короткие запросы обрабатываются в обычном режиме без возврата ошибки. Во-вторых, время жизни отсчитывается с момента начала запроса, который записывает или считывает запись, и каждое чтение обновляет его без дополнительных затрат. Таким образом, нагруженный эндпоинт поддерживает 5-минутный кэш активным бесконечно. Эндпоинт, вызываемый раз в десять минут, каждый раз оплачивает надбавку за запись и никогда не получает выгоды.
Проверяйте ответ, а не делайте предположений. Объект usage содержит поля cache_creation_input_tokens и cache_read_input_tokens. Если количество чтений равно нулю при каждом вызове, значит, вы оплачиваете записи, не получая отдачи.
Где пересекаются два вида кэширования
Длинный системный промпт — это точка их встречи, и он создает нагрузку на оба вида кэша одновременно.
Локально системный промпт объемом 20,000 токенов занимает около 2.4 GiB KV-кэша на сервере с моделью Llama 3.1 8B в формате f16, причем для каждого параллельного запроса, включающего этот промпт, кэш создается отдельно. Удаленно тот же префикс требует одной записи в кэш, а затем оплачивается как 0.1 от объема входных данных при каждом последующем вызове. Локальные затраты масштабируются вместе с количеством ваших пользователей. Удаленные затраты масштабируются вместе с трафиком и обнуляются во время простоя.
Существует локальная функция, которая выглядит как кэширование промптов у провайдера и с которой её постоянно путают: кэширование префиксов (prefix caching). Документация vLLM описывает автоматическое кэширование префиксов как сохранение «KV-кэша существующих запросов, чтобы новый запрос мог напрямую использовать KV-кэш, если он имеет тот же префикс, что и один из существующих запросов». Сервер llama.cpp по умолчанию хранит кэш промптов для каждого слота, а параметр --cache-reuse N задает минимальный размер фрагмента, который система будет пытаться использовать повторно.
Кэширование префиксов экономит вычислительные ресурсы на этапе prefill. Ваш системный промпт из 20,000 токенов обрабатывается один раз, а не при каждом запросе, что значительно сокращает время до получения первого токена (time to first token). В vLLM общие блоки используются повторно, а не дублируются, поэтому потребление памяти также оптимизируется. Однако это никогда не уменьшает объем кэша, который вы должны удерживать для активных токенов. Удержание весов модели в памяти между запросами — это связанный, но отдельный механизм, описанный в удержании модели Ollama загруженной между запросами.
Что измерять на собственном сервере
Загрузите модель с целевым контекстом, а затем получите реальные показатели вместо того, чтобы полагаться на оценки.
ollama ps
nvidia-smi --query-gpu=memory.used,memory.total --format=csv
free -gollama ps выводит список загруженных моделей с указанием их размера и того, где они выполняются — на GPU или CPU. Если модель, которая должна была полностью поместиться в GPU, сообщает о разделении с CPU, это означает, что KV cache вытеснил часть данных, и скорость генерации соответственно снизится. nvidia-smi показывает точное значение VRAM, а free -g выполняет ту же задачу на VPS только с CPU. Увеличивайте контекст пошагово, перезагружайте модель и следите за изменением цифр. Ваши расчеты и полученные данные должны быть близки. Если это не так, разница обычно обусловлена собственными вычислительными буферами среды выполнения, а не ошибкой в формуле.
Если эти цифры подталкивают вас к выбору оборудования, которое вы не хотели бы арендовать, сравнение с оплатой за токен приведено в разделе GPU VPS против API токенов.
FAQ
Является ли KV cache тем же самым, что и кэширование промптов (prompt caching)?
Нет. KV cache — это память внутри процесса обслуживания запроса, хранящая векторы ключей и значений для каждого токена в текущем контексте. Она находится в вашей RAM или VRAM и освобождается после завершения запроса. Кэширование промптов у провайдера — это функция биллинга, которая сохраняет стабильный префикс промпта на инфраструктуре провайдера и применяет сниженный тариф при повторной отправке. Исчерпание KV cache приводит к невозможности загрузки модели. Отсутствие кэша промптов лишь увеличивает ваш счет и время до получения первого токена.
Почему моя модель загружается с контекстом 2k, но выдает ошибку на 32k?
Потому что среда выполнения выделяет весь объем KV cache во время загрузки, исходя из настроенной длины контекста, а не из размера отправляемого промпта. Для Llama 3.1 8B в формате f16 кэш составляет 128 KiB на токен, поэтому контекст 2k требует 0.25 GiB, а 32k — 4 GiB. Веса модели помещаются в обоих случаях. Ошибка возникает именно при резервировании памяти. vLLM сообщает об этом как ValueError, указывая максимальное количество токенов, которое он мог бы сохранить, и предлагает увеличить gpu_memory_utilization или уменьшить max_model_len. На сервере только с CPU процесс завершает OOM-killer ядра, что можно подтвердить с помощью dmesg -T | grep -i "killed process".
Как рассчитать размер KV cache для моей модели?
Умножьте 2 на количество слоев, количество голов ключей/значений (key/value heads), размерность головы и количество байт на элемент. Это даст размер в байтах на токен. Затем умножьте на длину контекста и количество одновременных запросов. Количество слоев и голов можно узнать из файла config.json модели. Используйте 2 байта на элемент для f16 или bf16. Кэш q8_0 занимает примерно половину этого объема, а q4_0 — примерно четверть.
Снижает ли кэширование промптов требования к памяти моего сервера?
Кэширование промптов на стороне провайдера никак не влияет на ваше оборудование, так как хранилище находится на стороне провайдера. Локальным аналогом является кэширование префиксов (prefix caching), предлагаемое vLLM и сервером llama.cpp. Оно повторно использует уже вычисленные векторы ключей и значений для общего префикса, что экономит вычислительные ресурсы на этапе prefill и сокращает время до получения первого токена. В vLLM общие блоки переиспользуются, а не дублируются, поэтому потребление памяти также оптимизируется. Ни одна из этих функций не уменьшает объем кэша, необходимый для токенов, находящихся в обработке, поэтому расчеты контекста и параллелизма по-прежнему определяют минимальные требования.
Стоит ли кэшировать промпт, который я отправляю только один раз?
Нет. Запись в кэш стоит дороже, чем обычный ввод: по состоянию на август 2026 года это 1.25 от базового тарифа для 5-минутного окна, поэтому префикс, который вы никогда не отправляете повторно в течение этого времени, является чистым убытком. Кэширование выгодно, когда один и тот же префикс повторяется, например, длинный системный промпт или документ, по которому вы будете задавать несколько вопросов. Проверяйте cache_read_input_tokens в ответе API, чтобы убедиться, что вы получаете попадания в кэш (hits), а не платите за записи.