Знакомая картина: модель загружена, занято 5 ГБ видеопамяти, всё стабильно. Через сорок минут разговора занято 8 ГБ, а потом движок начинает выгружать слои на процессор и скорость падает. Похоже на утечку памяти, но это не она. Это KV-кэш.
#Зачем он нужен
Порождая очередной токен, модель должна учесть весь предыдущий текст. Считать его заново на каждом шаге было бы расточительно: при ответе в 500 токенов весь контекст пересчитывался бы 500 раз. Поэтому для каждого уже обработанного токена движок один раз вычисляет и сохраняет два вектора, участвующих в механизме внимания — ключ (K) и значение (V). Дальше новый токен просто сверяется с готовым кэшем.
Плата за это — память, растущая с каждым токеном диалога.
#Формула и порядок величин
размер KV-кэша ≈ 2 × слои × kv_головы × размер_головы × токены × байт_на_число
Двойка — это K и V. Всё, кроме числа токенов, задано архитектурой модели и лежит в её config.json. Отсюда три практических вывода:
- Кэш растёт линейно по длине контекста. Удвоили окно — удвоили расход памяти под кэш.
- Он не зависит от того, ваши это токены или ответа модели. Реплики ассистента занимают кэш наравне с вашими.
- Он зависит от модели сильнее, чем кажется. У моделей с общими ключ-значение-головами (GQA) множитель
kv_головыв разы меньше, чем число голов внимания, и кэш соответственно меньше. Две модели одинакового размера могут отличаться по расходу кэша в несколько раз.
Точный расчёт под конкретную видеокарту и длину окна — в разборе контекста и VRAM под KV-кэш.
Кэш можно сжать: хранение K и V в 8 битах вместо 16 примерно вдвое уменьшает его объём. Что при этом теряется в качестве и когда это оправдано — в тексте про Flash Attention и квантизацию KV-кэша.
#Где путают
Заблуждение первое: растущая память — признак утечки. Штатное поведение. Проверяется просто: начните новый чат — занятая память должна вернуться к исходной. Если не вернулась, вот тогда речь об утечке.
Заблуждение второе: KV-кэш — это память ассистента между сессиями. Не память. Кэш живёт в пределах одной последовательности и умирает вместе с ней. Часть провайдеров и движков умеет переиспользовать кэш общего префикса промпта между запросами — это ускоряет обработку, но и только.
Заблуждение третье: кэш занимает мало по сравнению с моделью. На коротких диалогах — да. На длинном контексте у модели без GQA кэш способен занять больше места, чем сами веса. Именно поэтому увеличение num_ctx внезапно ломает конфигурацию, которая только что работала.
Заблуждение четвёртое: кэш можно отключить, чтобы сэкономить память. Формально да, практически нет: без него каждый шаг генерации требует пересчёта всего контекста, и скорость падает на порядки. Экономить нужно длиной контекста и сжатием кэша, а не отключением.
#Частые вопросы
#Как понять, сколько памяти уйдёт под кэш у моей модели?
Возьмите из config.json число слоёв, число ключ-значение-голов и размер головы, подставьте в формулу выше и умножьте на желаемую длину окна. Практический ориентир проверяется опытом: поднимите контекст вдвое и посмотрите на прирост занятой памяти. Общий бюджет памяти под запуск — в разборе того, сколько нужно VRAM для LLM.
#Почему длинный диалог замедляется, а не только занимает память?
По двум причинам. Во-первых, на каждом шаге внимание сверяется с растущим кэшем — это чтение всё большего объёма памяти. Во-вторых, когда кэш перестаёт помещаться в видеопамять, часть работы уходит на процессор, и вот это замедление уже резкое.
#Помогает ли очистка истории чата?
Помогает, и это самый прямой рычаг: новая сессия начинается с пустого кэша. Если тема сменилась, начинать новый чат полезно и по существу, и по памяти.
#Кэш общий для всех пользователей на сервере?
Нет, у каждой последовательности свой. Поэтому при обслуживании нескольких человек одновременно память под кэши суммируется — и планировать её нужно на пиковое число одновременных диалогов, а не на один.
Что именно хранится в этих векторах — в справке про механизм внимания. Остальные термины — в глоссарии терминов ИИ.