Перейти к содержанию
Публикация AiManual

Квантизация KV-кэша в llama.cpp, ExLlamaV3 и vLLM: замеры KL-дивергенции на Qwen3.8 27B

Опубликованные замеры на Qwen3.8 27B: 8-битный KV-кэш в llama.cpp и ExLlamaV3 почти не сдвигает выходное распределение, в vLLM даёт небольшой рост KL. 4-битный

Коротко

Что будет в материале

  1. 01

    Короткий ответ: какой KV-кэш включать на Qwen3.8 27B

  2. 02

    Зачем вообще квантизовать KV-кэш и сколько VRAM это экономит

  3. 03

    Как измеряли: KL-дивергенция и top-1 agreement против bf16-эталона

  4. 04

    8-битный KV-кэш: результаты по движкам

Короткий ответ: какой KV-кэш включать на Qwen3.8 27B

Включайте 8-битный KV-кэш. В llama.cpp это тип q8_0, в ExLlamaV3 восьмибитный кэш, в vLLM формат fp8_e4m3. В первых двух движках размер кэша уменьшается ровно вдвое, а KL-дивергенция относительно bf16-эталона остаётся практически нулевой, то есть разница теряется в шуме. В vLLM KL немного подрастает, но авторы замеров оценивают это как выгодный компромисс для тех, кому не хватает VRAM.

С 4-битным кэшем картина другая. Штраф по KL измеримый и стабильный: в llama.cpp (q4_0) он особенно бьёт по весам около Q8, где веса уже почти не квантованы и терять точность обидно дважды. В ExLlamaV3 величина штрафа зависит от битности весов: чем агрессивнее сжат сам чекпоинт, тем относительно меньше добавляет 4-битный кэш. Автор замеров допускает, что при весах Q3 и ниже такой кэш может быть оправдан, но дефолтом его не рекомендует.

Длина контекста меняет только один показатель. Влияние 8-битного кэша остаётся стабильным, а штраф 4-битного растёт: на протестированных длинных документах добавленный KL при 16-32k токенов примерно на 15-30% выше, чем на первых 4k. Все цифры ниже взяты из опубликованных замеров на 35 вариантах квантования весов в llama.cpp, ExLlamaV3 и vLLM, эталоном служили bf16-логиты llama.cpp (исходная публикация с методикой и таблицами).

Зачем вообще квантизовать KV-кэш и сколько VRAM это экономит

KV-кэш хранит ключи и значения внимания для всех токенов, которые модель уже прочитала. Он растёт линейно с длиной контекста, и на 64k токенов его объём становится сопоставим с объёмом самих весов. Для плотной 27B-модели это уже гигабайты, которые приходится вычитать из бюджета VRAM до того, как вы выставите batch size или параллельные запросы.

Квантизация кэша уменьшает именно эту статью расходов, не трогая веса. Вот что показали замеры на Qwen3.8 27B:

Тип кэшаГде доступенKiB на токенРазмер на 64k контекстеЭкономия против 16-бит
f16 / bf16llama.cpp, ExLlamaV3, vLLM644.00 GiB-
8-битllama.cpp q8_0, ExLlamaV3 8-bit342.13 GiB1.9 GiB
8-битvLLM fp8_e4m3322.00 GiB2.0 GiB
4-битllama.cpp q4_0, ExLlamaV3 4-bit181.13 GiB2.9 GiB

Читать таблицу нужно правильно. 64 KiB на токен, 4 GiB на 64k и вся экономия посчитаны для конкретной модели Qwen3.8 27B: число слоёв и голов, включая grouped query attention, задают эти величины. У другой архитектуры KiB на токен будет своим, а пропорция между 16, 8 и 4 битами сохранится.

Второй нюанс: экономия масштабируется вместе с контекстом. На 8k токенов 8-битный кэш освободит около 0.24 GiB вместо 1.9 GiB на 64k, и ради этого обычно не стоит ничего менять. Переключаться имеет смысл тогда, когда вы работаете на 32k и дальше.

Для сравнения: на 4-битном кэше вы получаете примерно на 1 GiB больше, чем на 8-битном (2.9 против 1.9-2.0 GiB экономии). Доплата качеством за этот гигабайт редко окупается. Подробнее про выбор между Q8, Q4 и Q2 для кэша и про то, где проходит граница безопасности, мы разбирали в материале о квантовании KV-кеша для Qwen3.6 35B A3B.

Как измеряли: KL-дивергенция и top-1 agreement против bf16-эталона

KL-дивергенция (KLD) показывает, насколько вероятности следующего токена у квантованной модели отклоняются от эталона. Ноль означает, что распределения совпадают полностью; чем больше значение, тем сильнее модель «передумала» о том, какой токен вероятнее. Метрика считается по всем токенам датасета и не требует эталонных ответов, поэтому подходит для сравнения квантов между собой.

Top-1 agreement отвечает на более приземлённый вопрос: как часто обе модели выбирают один и тот же самый вероятный токен. Это грубее, зато ближе к тому, что видит пользователь в чате. Две метрики дополняют друг друга: KLD ловит тонкие сдвиги во всём распределении, top-1 agreement показывает, дошли ли они до фактического выбора.

База сравнения: bf16-эталон llama.cpp. Квантованные варианты ExLlamaV3 измерялись относительно этих же bf16-логитов llama.cpp, а не относительно собственного эталона ExLlamaV3, что стоит держать в голове при чтении графиков. Это ограничение методики, а не ошибка.

Калибровка шума важнее самих абсолютных значений. Неквантованная модель, запущенная в llama.cpp и в vLLM, различается по KL на 0.0004. Это не деградация, а разный порядок вычислений с плавающей точкой в двух корректных реализациях. Всё, что лежит в этом диапазоне, считайте нулём: на таких масштабах вы измеряете арифметику, а не потерю качества.

Датасет собран из двух частей: агентного tool-use, сгенерированного Qwen3.8 27B в полной точности, и текстов разных доменов. Строки доходят до 64k токенов контекста, поэтому замеры покрывают и короткие промпты, и длинные документы, типичные для RAG и агентов.

8-битный KV-кэш: результаты по движкам

llama.cpp с q8_0: размер кэша уменьшается вдвое, разница в KLD практически отсутствует. Для практики это означает, что вы можете освободить около 1.9 GiB на 64k контексте и не заметить изменений в ответах.

ExLlamaV3 с восьмибитным кэшем: результат тот же, штраф остаётся в пределах шума. Здесь важно, что замер вёлся относительно bf16-логитов llama.cpp, то есть небольшая систематическая разница между движками уже включена в цифру и всё равно не вывела восьмибитный кэш из безопасной зоны.

vLLM с fp8_e4m3: небольшая разница в KLD есть. Она не превращает восьмибитный кэш в плохой выбор, автор замеров прямо называет его хорошим компромиссом при нехватке VRAM. Если выбирать, где резать память в vLLM, кэш с 16 до 8 бит безопаснее, чем агрессивная квантизация весов.

Вывод по разделу простой: 8-битный кэш - самый дешёвый по качеству рычаг экономии VRAM из доступных, и включать его стоит до того, как вы начнёте понижать битность весов.

Почему vLLM ведёт себя иначе, чем llama.cpp и ExLlamaV3

Часть разницы между движками связана с квантизацией активаций, а не с KV-кэшем. В тех же замерах каждый чекпоинт vLLM проигрывает более мелкому GGUF или EXL3-кванту, и часть этого проигрыша даёт именно квантизация активаций внутри модели.

Масштаб эффекта видно на повторном запуске через Marlin-ядра vLLM, где веса остаются квантованными, а активации переведены в bf16. Для NVFP4 KL упал с 0.0256 до 0.0117, то есть более чем вдвое. Для FP8 из репозитория Qwen - с 0.0039 до 0.0028, примерно на 30%. Разница между NVFP4 и FP8 объясняется схемой: NVFP4-чекпоинты NVIDIA и Unsloth гоняют MLP-слои как W4A4 с 4-битными активациями и внимание как FP8 W8A8, а FP8 от Qwen держит W8A8 по всей модели.

Практический смысл: не переносите штраф активаций на KV-кэш. Когда вы сравниваете движки по KLD, спрашивайте, что именно квантуется в каждом конкретном замере. Восьмибитный кэш в vLLM и восьмибитный кэш в llama.cpp - разные вещи по реализации, и разница в KL между ними частично объясняется соседними настройками, а не самим кэшем.

4-битный KV-кэш: где начинается заметная деградация

4-битный кэш даёт измеримый штраф по KL, и дефолтом его ставить не стоит. В llama.cpp (q4_0) штраф ведёт себя как фиксированная прибавка: он есть почти всегда и особенно неприятен на точных весах около Q8.

В ExLlamaV3 картина зависит от битности весов. Чем агрессивнее сжат сам чекпоинт, тем относительно меньше добавляет 4-битный кэш. При весах Q3 и ниже автор замеров допускает, что такой кэш может быть оправдан, поскольку ошибка весов уже доминирует и добавка от кэша на её фоне теряется. Это компромисс, а не рекомендация.

Арифметика памяти выглядит так: 4-битный кэш экономит 2.9 GiB на 64k против 1.9-2.0 GiB у 8-битного. Доплата заметным ростом KL за примерно 1 GiB - сомнительная сделка, если у вас есть хоть какой-то запас VRAM. Логику выбора битности весов и кэша под разные задачи мы разбирали в статье о том, какие кванты LLM реально брать для чата, кодинга и агентов.

Отдельно про поддержку: 4-битный кэш есть не во всех движках и не для всех конфигураций, в замерах он тестировался только там, где поддерживается. Отсутствие цифры для движка не означает, что там кэш лучше или хуже.

Почему точные веса (около Q8) страдают от 4-битного кэша сильнее

Интуиция простая. Когда веса почти не квантованы, главным источником ошибки становится кэш, и его вклад виден напрямую: он ничем не маскируется. Когда веса уже сильно сжаты (Q3 и ниже), ошибка весов доминирует, и добавка от кэша относительно меньше.

Оговорка: в источнике есть наблюдаемая зависимость штрафа от битности весов в ExLlamaV3 и фиксированный штраф в llama.cpp. Объяснение через доминирование ошибки согласуется с этими наблюдениями, но это интерпретация, а не измеренный механизм. Для практики хватает самого правила: чем точнее веса, тем больнее вам обойдётся 4-битный кэш.

Как деградация растёт с длиной контекста

С длиной контекста растёт только влияние 4-битного кэша. Восьмибитный кэш ведёт себя стабильно на всём диапазоне, который покрывает датасет, до 64k токенов на строку. Это ключевая причина, по которой 8-битный кэш можно включать «на всякий случай», а 4-битный нельзя.

Цифры: на протестированных длинных документах добавленный KL при 16-32k токенов примерно на 15-30% выше, чем на первых 4k. То есть если на коротком промпте вы готовы были терпеть 4-битный кэш, на длинном контексте его штраф вырастает.

Практический смысл для агентов и RAG. Именно там контекст длинный, а ошибка накапливается: каждый следующий токен генерируется на основе испорченного кэша, и небольшая деградация распределения складывается в дрейф рассуждений и неправильные вызовы инструментов. Датасет в этих замерах как раз включает агентный tool-use, сгенерированный Qwen3.8 27B в полной точности, так что сценарий близок к реальной работе с инструментами.

Ограничение: вывод про +15-30% получен на конкретных длинных документах из датасета. Экстраполировать его на все типы текстов нельзя, но направление эффекта устойчивое: агрессивный кэш и длинный контекст плохо сочетаются.

Практические рекомендации: 8-битный или 4-битный кэш под ваш сценарий

VRAM хватает на веса, но не хватает на длинный контекст. Включайте 8-битный кэш: q8_0 в llama.cpp, 8-bit в ExLlamaV3, fp8_e4m3 в vLLM. Это почти бесплатно по KL и освобождает около 1.9-2.0 GiB на 64k токенов. Ровно этот сценарий разобран в материале о запуске Qwen3.8-27B-NVFP4 через vLLM с контекстом до 1M токенов, где видно, как fp8 KV-кэш съедает бюджет памяти.

Веса Q3 и ниже, VRAM впритык. 4-битный кэш может быть оправдан, но проверяйте top-1 agreement на своих задачах, а не только на общем датасете. Если задача чувствительна к точному выбору токена (код, вызов инструментов с жёсткой схемой), сначала попробуйте сэкономить в другом месте.

Веса Q8 или Q6 плюс длинный контекст. 4-битный кэш не рекомендуется: фиксированный штраф ложится на почти не квантованные веса, а с ростом контекста он ещё и увеличивается. Здесь 8-битный кэш закрывает почти всю потребность в памяти.

Короткие контексты до 4k. Экономия составит доли гигабайта, смысла в квантизации кэша почти нет. Выигрыш появляется на 32k и выше.

Не путайте уровни квантизации. Кэш, веса и активации - три разных рычага. NVFP4 и FP8-чекпоинты в vLLM имеют собственные проблемы: после перевода активаций в bf16 через Marlin-ядра NVFP4 снизил KL с 0.0256 до 0.0117 и всё равно лишь сравнялся с UD-Q5_K_XL, который на 8.7 GiB меньше. Это отдельная история про веса и активации, к настройке кэша она отношения не имеет.

Чек-лист проверки на своих данных: посчитайте KLD относительно bf16 на своём датасете, добавьте top-1 agreement, прогоните отдельно короткие и длинные строки (4k против 16-32k), и только потом фиксируйте тип кэша в конфиге. Для агентных сценариев обязательно включите в проверку реальные вызовы инструментов.

Что осталось за кадром и чего эти замеры не показывают

Замеры сделаны на одной модели, Qwen3.8 27B. Переносить пропорции штрафа на другие архитектуры, особенно на MoE и на модели с иным числом KV-голов, без собственной проверки нельзя. Размеры кэша в таблице тоже привязаны к этой модели.

Эталон - bf16-логиты llama.cpp. Квантованные варианты ExLlamaV3 измерялись относительно них, а не относительно собственного bf16-эталона ExLlamaV3, поэтому в их цифры уже входит междвижковая разница порядка 0.0004 по KL. Фактически это калибровочный шум, но помнить о нём стоит.

KL и top-1 agreement описывают распределение вероятностей, а не качество на конкретной задаче. Низкий KLD не гарантирует, что модель так же хорошо пишет код или проходит многошаговые инструкции; высокий KLD не гарантирует, что задача провалена. Метрика говорит, насколько выход сдвинулся, а не насколько он стал хуже для вашего сценария. Как читать KLD и не путать значения 0.1 и 0.01-0.03, разобрано отдельно.

В источнике нет данных по скорости генерации и реальному потреблению VRAM в рантайме, только размеры кэша. Утверждать, что 8-битный кэш ускоряет или замедляет вывод на конкретном железе, по этим замерам нельзя. Смежная тема, где обсуждаются заявления о скорости prefill и KV-кэше, разобрана в материале о том, что стоит за ускоренным prefill на Qwen.

Итог без оговорок: 8-битный KV-кэш - безопасный дефолт, когда нужно освободить память под длинный контекст. 4-битный - осознанный компромисс для случаев, когда веса уже сжаты до Q3 и ниже, а VRAM кончилась. Проверяйте оба варианта на своих данных до того, как зафиксируете настройку в продакшн-конфиге.

Подписаться на канал