Prefix Caching в vLLM: как ускорить повторяющиеся запросы и экономить KV cache

Что делает Automatic Prefix Caching​


При инференсе LLM каждый входной токен проходит через все слои модели, и на каждом слое вычисляются пары key-value (KV), которые сохраняются в KV cache. Если два запроса начинаются с одинакового префикса — например, содержат один и тот же системный промпт или один и тот же длинный документ, — KV cache для этого префикса идентичен. Automatic Prefix Caching (APC) в vLLM использует это свойство: он кэширует KV cache уже обработанных запросов и при поступлении нового запроса с совпадающим префиксом пропускает повторное вычисление общей части.

Результат — снижение времени prefilling (фазы обработки входных токенов) и освобождение вычислительных ресурсов для других запросов. Фаза декодирования (генерация новых токенов) при этом не ускоряется.

Механика: связь с PagedAttention​


vLLM управляет KV cache через механизм PagedAttention: память под KV cache выделяется не непрерывным блоком, а страницами фиксированного размера, подобно виртуальной памяти в ОС. Это позволяет гибко распределять память между запросами и избегать фрагментации.

APC строится поверх этой архитектуры. Когда vLLM обрабатывает запрос, он хеширует блоки токенов и сохраняет соответствующие страницы KV cache. При поступлении нового запроса движок проверяет, есть ли в кэше страницы с совпадающим хешем префикса. Если есть — они подключаются к новому запросу напрямую, без повторного forward pass по этой части последовательности.

Это означает, что APC не требует отдельного хранилища: он переиспользует те же страницы KV cache, которые уже находятся в GPU-памяти, и просто предотвращает их эвакуацию, пока они могут понадобиться.

Гранулярность хеширования​


Хеширование выполняется на уровне блоков токенов, а не отдельных токенов. Размер блока совпадает с размером страницы PagedAttention. Это означает, что совпадение префикса определяется с точностью до блока: если два запроса совпадают на 130 токенов при размере блока 16, закэшированы будут первые 8 блоков (128 токенов), а оставшиеся 2 токена будут обработаны заново.

Такой подход снижает overhead на хеширование и позволяет эффективно переиспользовать страницы памяти без побайтового сравнения.

Eviction и жизненный цикл кэша​


Страницы KV cache, сохранённые APC, не являются «вечными». Они занимают ту же GPU-память, что и активные запросы, и подчиняются общему механизму eviction. Когда свободная память заканчивается, vLLM эвакуирует наименее перспективные страницы — в том числе закэшированные префиксы, которые давно не использовались.

Это означает, что APC не увеличивает общее потребление памяти сверх лимита, выделенного под KV cache. Он лишь меняет порядок освобождения страниц: вместо немедленной эвакуации после завершения запроса страницы остаются в памяти в надежде на повторное использование.

Как включить​


APC активируется одним параметром при инициализации движка:

Python:
from vllm import LLM

llm = LLM(
    model="meta-llama/Llama-3.1-8B-Instruct",
    enable_prefix_caching=True
)

Для OpenAI-совместимого сервера тот же флаг передаётся через аргументы запуска:

Bash:
vllm serve meta-llama/Llama-3.1-8B-Instruct \
    --enable-prefix-caching

В репозитории vLLM есть готовый пример: examples/features/automatic_prefix_caching/automatic_prefix_caching_offline.py.

Дополнительной настройки по умолчанию не требуется — движок сам управляет eviction и повторным использованием страниц.

Offline и online режимы​


APC работает одинаково в обоих режимах использования vLLM:

  • Offline (batch inference) — при создании объекта LLM и вызове generate() на наборе промптов. APC кэширует префиксы между вызовами в рамках одной сессии.
  • Online (API server) — при запуске vllm serve. APC кэширует префиксы между HTTP-запросами от разных клиентов, пока сервер работает и страницы не эвакуированы.

В online-режиме APC особенно полезен, когда несколько клиентов отправляют запросы с общим системным промптом или одним и тем же контекстным документом.

Сценарии с максимальным выигрышем​


Длинный документ + множество вопросов​


Пользователь загружает один и тот же документ (техническое руководство, годовой отчёт, контракт) и задаёт к нему разные вопросы. Без APC каждый запрос заново обрабатывает весь документ на этапе prefilling. С APC документ обрабатывается один раз, а все последующие запросы сразу переходят к генерации ответа.

Выигрыш тем выше, чем длиннее общий префикс относительно полного запроса. Для документа на 50 000 токенов и вопроса на 100 токенов prefilling сокращается примерно в 500 раз.

Много раундов диалога​


В чат-приложении каждый новый раунд включает всю предыдущую историю. Без APC история пересчитывается заново при каждом обращении. С APC KV cache истории переиспользуется, и prefilling выполняется только для нового сообщения пользователя.

Это особенно заметно в длинных сессиях: на 20-м раунде диалога общая история может составлять тысячи токенов, и APC устраняет их повторную обработку.

Общий системный промпт​


Если все запросы к сервису начинаются с одинакового системного промпта (например, инструкции для агента или описания роли), APC кэширует KV cache этого промпта. Каждый новый запрос экономит время на его обработке.

Выигрыш от системного промпта скромнее, чем от длинного документа, потому что системные промпты обычно содержат сотни токенов, а не десятки тысяч. Но при высоком QPS даже экономия в несколько миллисекунд на запрос суммируется в заметный рост throughput.

RAG с фиксированным контекстом​


В RAG-пайплайнах, где один и тот же набор документов подаётся в контекст для разных вопросов пользователей, APC кэширует KV cache этого контекста. Это работает при условии, что документы вставляются в начало промпта (до вопроса) и их порядок стабилен между запросами.

Когда APC не помогает​


APC снижает только время prefilling. Это означает несколько ограничений:

Длинные ответы. Если модель генерирует развёрнутый ответ на сотни или тысячи токенов, основная часть времени уходит на фазу декодирования. Экономия на prefilling в этом случае незаметна в общей задержке.

Отсутствие общего префикса. Если каждый запрос уникален и не пересекается с предыдущими по началу последовательности, кэш не находит совпадений и не даёт выигрыша. APC не выполняет fuzzy matching — префикс должен совпадать поточно, токен в токен.

Короткие запросы. Если входная последовательность содержит 10–20 токенов, prefilling и так занимает миллисекунды. Экономия от APC в абсолютных числах пренебрежимо мала.

Частая смена префикса. Если префикс меняется с каждым запросом (например, из-за динамического timestamp или уникального session ID в начале), кэш не успевает накопить полезные записи.

Влияние на throughput и память​


APC не увеличивает потребление GPU-памяти сверх того, что уже выделено под KV cache. Страницы, которые без APC были бы эвакуированы после завершения запроса, при включённом APC остаются в памяти дольше — но они занимают те же страницы, которые PagedAttention и так управляет. Если память заканчивается, eviction происходит по стандартному механизму.

На throughput влияние положительное: запросы с совпадающим префиксом проходят prefilling быстрее, освобождая слоты в continuous batching для новых запросов. Документация vLLM указывает, что APC в общем случае не снижает производительность — он либо помогает, либо нейтрален.

Взаимодействие с continuous batching​


vLLM использует continuous batching: новые запросы добавляются в batch по мере поступления, а завершённые — удаляются, не дожидаясь окончания всего batch. APC усиливает этот механизм: когда запрос с закэшированным префиксом поступает в batch, он занимает слот на меньшее время (потому что prefilling короче), и слот быстрее освобождается для следующего запроса.

Взаимодействие с chunked prefill​


Chunked prefill разбивает длинный prefilling на части и чередует его с декодированием других запросов. Это предотвращает ситуацию, когда один длинный prefilling блокирует генерацию токенов для остальных запросов в batch.

APC и chunked prefill дополняют друг друга: APC сокращает объём prefilling, а chunked prefill стабилизирует latency для тех запросов, которые всё же требуют обработки. Вместе они дают более предсказуемую задержку под смешанной нагрузкой.

Практические рекомендации​


  • Структурируйте промпты так, чтобы общий префикс был в начале. Если системный промпт, контекст документа или история диалога идут первыми, APC сможет их закэшировать. Если общий контент находится в середине или конце последовательности, он не будет распознан как префикс.
  • Не меняйте начало последовательности между запросами. Даже один отличающийся токен в начале ломает совпадение префикса для всех последующих блоков.
  • Мониторьте hit rate. Если APC включён, но задержка не снижается, вероятно, запросы не имеют общего префикса. Проверьте логику формирования промптов.
  • Сочетайте с chunked prefill. vLLM поддерживает chunked prefill, который разбивает длинный prefilling на части и чередует его с декодированием других запросов. Вместе с APC это даёт дополнительную стабилизацию latency под нагрузкой.
  • Фиксируйте порядок документов в RAG. Если контекст формируется из нескольких документов, их порядок должен быть детерминированным. Перестановка документов меняет последовательность токенов и ломает совпадение префикса.

Типичные ошибки​


ОшибкаПоследствие
Динамический timestamp или session ID в начале промптаКаждый запрос получает уникальный префикс, кэш не срабатывает
Общий контекст помещён после вопросаAPC ищет совпадение с начала последовательности — контекст не кэшируется
Ожидание ускорения генерацииAPC влияет только на prefilling; decoding остаётся прежним
Включение APC для batch из уникальных одноразовых запросовНет повторяющихся префиксов — нет выигрыша, только минимальный overhead на хеширование
Рандомизация порядка few-shot примеровКаждый запрос получает уникальную последовательность в начале, префикс не совпадает
Использование разных chat template для одного и того же контентаРазный template порождает разные токены на границе, префикс ломается

Диагностика: как убедиться, что APC работает​


Прямой метрики «prefix cache hit rate» в стандартном выводе vLLM нет, но косвенные признаки позволяют оценить эффективность:

  1. Сравните TTFT (Time To First Token) для первого и последующих запросов с одинаковым префиксом. Первый запрос выполняет полный prefilling, последующие — только для новой части. Разница в TTFT должна быть пропорциональна длине закэшированного префикса.
  2. Наблюдайте за GPU utilization. При высоком hit rate загрузка GPU во время prefilling снижается, потому что меньше токенов требуют вычисления.
  3. Проверьте логику формирования промптов. Убедитесь, что общий контент (системный промпт, документ, история) стоит в начале последовательности и не содержит динамических элементов.

Если TTFT не снижается после включения APC, наиболее вероятная причина — отсутствие реального совпадения префиксов между запросами.

Итоговая проверка​


После включения enable_prefix_caching=True убедитесь, что:

  1. Запросы действительно содержат общий префикс в начале последовательности.
  2. Prefilling занимает существенную долю общего времени инференса (иначе выигрыш незаметен).
  3. Нагрузка содержит повторяющиеся паттерны — один документ с разными вопросами, много раундов диалога, единый системный промпт.
  4. В начале промпта нет динамических элементов (timestamp, nonce, session ID).
  5. Порядок контента в префиксе стабилен между запросами.

Если все условия выполнены, APC даёт снижение latency на prefilling и рост throughput без дополнительных затрат памяти и без изменения конфигурации модели.

Источники​


 
Назад
Верх Низ