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

Как проверить реальную емкость KV cache в локальных LLM и поймать ложные обещания движка

Показываем, как провести stress test KV cache в локальной LLM: откалибровать hit и miss, проверить удержание стабильных контекстов и прочитать retained tokens,

Коротко

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

  1. 01

    Как проверить KV cache в локальных LLM: короткий ответ

  2. 02

    Почему заявленная емкость не совпадает с фактическим удержанием KV cache

  3. 03

    Как устроен stress test KV cache: от калибровки до давления

  4. 04

    Как читать retained tokens, retained % capacity и oldest evicted

Как проверить KV cache в локальных LLM: короткий ответ

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

Практический stress test сначала калибрует, что в конкретной конфигурации считается hit и miss. Затем он добавляет различимые стабильные контексты в фиксированной последовательности, создает давление новыми контекстами и повторно проверяет старые записи. В отчете полезно смотреть на retained tokens, retained % capacity и oldest evicted. Такой тест подходит для сравнения vLLM, llama.cpp, SGLang и других inference-движков, если протокол и условия совпадают.

Проверка меняет состояние текущего кэша. Последовательное заполнение вытесняет уже накопленные записи, поэтому запускать тест рядом с рабочими запросами нельзя. Для чистого результата нужен отдельный остановленный сервер или изолированная тестовая конфигурация.

Что в этом тесте считается реальной емкостью

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

Единицей измерения может быть число токенов, число контекстных блоков или их эквивалент, который использует отчет инструмента. Это нужно фиксировать заранее. Например, при одинаковом размере каждого блока результат можно описать как число сохраненных блоков и пересчитать его в токены. При разном размере контекстов сравнение по одному количеству блоков уже будет искажать картину.

Результат относится к связке из нескольких условий: конкретная модель, формат весов или квантизация, железо, версия движка, параметры запуска, длина тестовых контекстов и состав фоновой нагрузки. Переносить цифру на другой компьютер или другой сервер без повторной проверки нельзя.

Почему одного значения из конфигурации недостаточно

Настроечный лимит отвечает на вопрос, какой размер контекста движок разрешает запросить. Stress test отвечает на другой вопрос: сколько уже обработанных данных останется доступно, когда новые контексты начнут конкурировать за память.

Эти значения связаны, но не равны. В локальном инференсе память распределяется между весами модели, KV cache, входным контекстом, промежуточными буферами и процессами операционной системы. На Apple Silicon все эти компоненты используют общую unified memory. При увеличении контекста свободный запас сокращается, и одинаковый файл модели может вести себя по-разному на разных Mac.

Поэтому обещание движка или верхняя граница параметра не заменяют измерение. Корректная формулировка выглядит так: конкретная конфигурация удержала определенное число проверенных токенов при заданном протоколе. Формулировка вроде универсальной емкости движка требует серии независимых прогонов.

Почему заявленная емкость не совпадает с фактическим удержанием KV cache

Емкость KV cache ограничена не только памятью под модель

Веса занимают заранее выделенный объем, но на этом потребление памяти не заканчивается. Серверу нужны структуры для входного контекста, рабочие буферы вычислений и память под KV cache. Операционная система и фоновые процессы тоже используют доступный запас. Когда свободного пространства становится мало, движок может менять размещение данных, вытеснять записи или отказывать в продолжении выбранного режима.

На практике результат зависит от того, сколько памяти осталось после загрузки модели и старта сервера. Физический объем VRAM сам по себе не показывает, сколько пространства достанется KV cache. Для GPU-сценария нужно учитывать занятость видеопамяти другими процессами. Для Mac с Apple Silicon нужно смотреть на общий объем unified memory и текущую нагрузку системы.

Универсальный процент потерь между расчетной и фактической емкостью здесь неприменим. Он меняется вместе с моделью, настройками, длиной контекста и версией движка. Именно поэтому stress test полезен как измерение конкретного запуска, а не как замена всем расчетам.

Почему Apple Silicon особенно наглядно показывает проблему

В системе на Apple Silicon модель и рабочие данные используют одну память. В нее одновременно попадают веса, KV cache, входные токены, промежуточные буферы и процессы macOS. Отдельной VRAM, куда можно без последствий вынести часть нагрузки, у такой схемы нет.

При росте контекста свободный запас unified memory уменьшается. Из-за этого одна и та же модель может удерживать разный объем KV cache на двух Mac с разным объемом памяти или с разным количеством запущенных приложений. Сравнение результата без записи свободной памяти перед тестом теряет часть смысла.

Практический пример для отчета: нужно указать модель, объем unified memory, свободный запас перед запуском, длину каждого тестового контекста и версию inference-движка. Одна строка с названием модели не описывает условия достаточно полно. Поведение на Mac M5 Air и на рабочей станции с отдельной видеопамятью нельзя считать эквивалентным только по имени модели.

Похожий вопрос возникает при анализе локального запуска моделей на Mac, где отдельно сравниваются prefill, decode, квантизация и поведение на длинном контексте: разбор запуска Qwen3.8-Next на Mac M5 Air.

Hit и miss нельзя считать универсальными без калибровки

Hit означает, что проверяемый контекст был найден и повторно использован по признаку, который доступен в конкретной системе. Miss означает, что этот признак не подтвердился. Само название метрики еще не говорит, какой внутренний механизм ее породил.

Калибровка нужна, чтобы связать наблюдаемый сигнал с двумя состояниями. Инструмент проверяет поведение на заведомо повторяемом контексте и на контексте, который ранее не добавлялся. После этого задается правило интерпретации результата. В зависимости от тестового интерфейса сигналом может служить доступная телеметрия или другой наблюдаемый признак, но при отсутствии отдельной проверки нельзя приписывать конкретному движку определенную внутреннюю реализацию.

Эта оговорка особенно важна при сравнении vLLM, llama.cpp и SGLang. Одинаковое слово hit в отчетах может опираться на разные способы наблюдения. В сравнительной таблице нужно записывать не только итог, но и способ калибровки.

Как устроен stress test KV cache: от калибровки до давления

Калибровка: сначала определить hit и miss

Первый этап задает правила эксперимента. В тестовую систему передают воспроизводимый контекст, повторяют проверку и убеждаются, что ожидаемое повторное обращение распознается как hit. Затем используют новый контекст, который не должен считаться сохраненным, и проверяют состояние miss.

Калибровка не измеряет емкость. Она проверяет саму измерительную шкалу. Если инструмент неверно трактует сигнал, последующие значения retained tokens будут выглядеть точными, но описывать другой процесс.

В записи эксперимента полезно указать:

  • какой контекст повторялся при проверке;
  • какой признак считался попаданием;
  • какой признак считался промахом;
  • сколько повторов использовалось для контроля стабильности;
  • были ли между проверками другие запросы.

Последовательное заполнение стабильными контекстами

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

Контексты нужно делать различимыми. Удобный вариант, описываемый в отчете, состоит в нумерации блоков и отдельной проверке каждого номера. Тогда после давления видно, какой именно элемент сохранился, а какой выбыл. Смешивать все блоки в один неразличимый текст нельзя: такой результат не покажет порядок вытеснения.

Последовательность добавления тоже фиксируется. Если сначала добавить блоки A, B и C, а затем проверить только C, тест не покажет состояние A и B. Полный протокол проверяет каждый ранее добавленный блок по одному правилу.

Проверка удержания под давлением

Давление создается добавлением новых данных или контекстов после заполнения исходной последовательности. Новые записи занимают доступное пространство и должны заставить систему проявить фактическую границу удержания. После этого инструмент снова проверяет старые контексты.

Если блок прошел проверку, он попадает в retained tokens. Если проверка не прошла, блок считается вытесненным для этого прогона. Итог нужно описывать как наблюдение: при такой модели, таком железе, таких настройках и таком порядке новые контексты привели к потере конкретных старых блоков.

Один прогон не раскрывает все режимы работы. Изменение размера блока давления, порядка запросов или фоновой нагрузки способно изменить результат. Повторяемость проверяется повторным запуском с теми же входами и отдельной фиксацией расхождений.

Почему последовательность важнее одного повторного запроса

Один повторный запрос показывает, что выбранный контекст может дать hit в текущем состоянии. Он не отвечает, сколько других контекстов система удерживает одновременно и какой блок будет вытеснен первым при росте нагрузки.

Последовательный тест создает порядок событий: блок добавили, затем добавили еще несколько, затем проверили исходные записи. Такой порядок позволяет наблюдать не единичное попадание, а изменение состава кэша. Для оценки емкости это принципиальная разница.

Задача stress test состоит в измерении удержания KV cache. Скорость генерации и качество ответа модели относятся к другим характеристикам. Их можно проверять отдельными сценариями после восстановления обычного состояния сервера.

Как читать retained tokens, retained % capacity и oldest evicted

Retained tokens: сколько данных осталось доступно

Retained tokens показывает абсолютный объем ранее добавленных данных, которые продолжили проходить проверку после давления. В простом протоколе с одинаковыми блоками это число можно получить умножением количества удержанных блоков на длину блока в токенах.

Пример: если тест добавил шесть стабильных блоков по 4000 токенов, а после давления проверка подтвердила четыре, отчет может описывать удержание как 16 000 токенов. Это пример расчета по заданному протоколу, а не результат измерения конкретного движка.

Показатель не равен максимальному контексту модели. Максимальный контекст описывает допустимый вход или режим запроса, а retained tokens описывает сохранность ранее вычисленных данных после выбранного давления. Эти понятия нельзя смешивать в одной сравнительной строке.

Retained % capacity: относительная доля удержания

Retained % capacity переводит сохраненный объем в относительную долю тестовой емкости. Если отчет использует стандартный расчет, показатель получается как отношение retained tokens к объему, который был подан на проверку, умноженное на 100.

Проценты сопоставимы только при одинаковом протоколе. Нужно совпадение размера и количества контекстов, порядка добавления, давления, модели, аппаратной конфигурации и правила hit или miss. Процент 80 в одном тесте может описывать другой объем, чем процент 80 в тесте с иным размером блоков.

Относительная метрика удобна для чтения одной серии запусков. Она плохо подходит для самостоятельного рейтинга движков. Рядом с процентом всегда нужны абсолютный retained tokens и условия эксперимента.

Oldest evicted: какой контекст выбыл первым

Oldest evicted указывает на самый ранний контекст в последовательности, который больше не прошел проверку удержания. Метрика помогает увидеть порядок потерь. Если первым выбывает блок с ранним порядковым номером, давление уже затронуло начальную часть набора.

Этот показатель не описывает всю политику управления кэшем. Один идентификатор не говорит, сколько записей сохранилось, как менялся состав кэша между проверками и что произойдет при другой последовательности. Его нужно читать рядом с retained tokens.

Как читать три метрики вместе

Сначала проверяют абсолютный объем. Retained tokens отвечает, сколько данных сохранилось. Затем смотрят на долю, чтобы сравнить результаты внутри одинаковой серии. После этого анализируют oldest evicted и связывают потерю с порядком добавления.

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

МетрикаНа какой вопрос отвечаетОграничение
retained tokensСколько токенов осталось доступно после давленияЗависит от конкретного запуска и размера тестовых блоков
retained % capacityКакая доля проверенного объема сохраниласьСравнима при совпадении протокола и исходной емкости теста
oldest evictedКакой ранний контекст выбыл первымНе описывает всю политику кэширования

Как запускать тест и не разрушить рабочую нагрузку

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

Перед запуском: зафиксировать исходные условия

Соберите короткую карточку эксперимента. В нее входят:

  • название и версия модели;
  • формат весов или квантизация, если они используются;
  • аппаратная платформа и доступный объем VRAM или unified memory;
  • свободная память перед стартом;
  • название и версия inference-движка;
  • параметры, связанные с длиной контекста и KV cache;
  • размер, структура и порядок тестовых блоков;
  • критерий, по которому инструмент различает hit и miss.

Для GPU важно убрать конкурирующие процессы, которые занимают видеопамять. Для Apple Silicon нужно закрыть лишние приложения и записать свободный запас unified memory. Набор тестовых входов следует сохранить без изменений, иначе повторный прогон уже не будет идентичным.

Во время теста: не обслуживать параллельные запросы

Рабочий сервер на время проверки нужно остановить, отключить от маршрутизации или заменить отдельным экземпляром с той же конфигурацией. Новый пользовательский запрос меняет порядок обращений и может добавить собственные записи в кэш. Инструмент в этот момент измеряет смесь двух нагрузок.

Запуск рядом с активной агентной сессией особенно рискован. Сессия рассчитывает на сохранность своих префиксов, а stress test целенаправленно заполняет тот же ресурс. После такого соседства скорость повторной обработки и состав кэша уже нельзя объяснить одним исходным сценарием.

После теста: сохранить отчет и вернуть обычный режим

Сохраните метрики вместе с карточкой условий. Минимальный отчет содержит retained tokens, retained % capacity, oldest evicted, порядок блоков и результат повторного прогона. При расхождении укажите его явно, а не заменяйте двумя значениями усредненной цифрой без объяснения.

После проверки сервер следует вернуть в обычный режим с учетом измененного состояния KV cache. В зависимости от стека рабочей сессии потребуется новая инициализация или отдельный способ очистки. Универсальный способ сброса для всех движков заявлять нельзя, поэтому действие нужно сверить с используемым сервером и вашим процессом запуска.

Как сравнить vLLM, llama.cpp и SGLang в одинаковых условиях

Что нужно сделать одинаковым

Сравнение начинайте с одного набора входных данных. Для каждого движка зафиксируйте:

  • одну и ту же модель;
  • максимально близкий формат весов;
  • одинаковую аппаратную конфигурацию и объем доступной памяти;
  • одинаковую длину и структуру тестовых контекстов;
  • одинаковый порядок добавления блоков;
  • одинаковый объем давления новыми контекстами;
  • одинаковое правило интерпретации hit и miss;
  • версию и параметры каждого движка.

Если одинаковый формат весов недоступен, это нужно вынести в отдельную колонку отчета. Сравнение vLLM с llama.cpp или SGLang при разных моделях, квантизации и запасе памяти показывает поведение конфигураций, но не изолированную разницу движков.

При анализе локального инференса полезно отдельно учитывать загрузку весов, работу с длинным контекстом и тип KV cache. Практический пример такой методики есть в разборе поведения Qwen3.8-Flash-Next в llama.cpp.

Почему одинаковый протокол не отменяет различий реализации

Inference-движки по-разному распределяют память, обрабатывают последовательности и предоставляют наблюдаемые сигналы. Единый протокол уменьшает число переменных, но не превращает внутренние процессы в полностью одинаковые.

Поэтому калибровка нужна отдельно для каждой системы. Один движок может предоставить прямой признак повторного использования, другой потребует косвенной проверки через доступный интерфейс, а третий может не дать сопоставимого сигнала без дополнительной обвязки. В отчете нужно разделять подтвержденный наблюдаемый результат и предположение о внутренней причине.

Стресс-проверка отвечает на вопрос о выбранном сценарии. Она не показывает все режимы планировщика, все варианты конкуренции запросов и все настройки памяти. Для рабочих выводов следует добавлять отдельные тесты на пропускную способность, задержку, стабильность и качество генерации.

Как оформлять сравнительный результат

На каждый движок заведите отдельную строку или отдельный блок отчета. Укажите конфигурацию, условия нагрузки, способ калибровки, retained tokens, retained % capacity, oldest evicted и число повторов. Отдельно запишите, был ли сервер изолирован от других запросов.

ПолеЧто фиксировать
ДвижокНазвание, версия и параметры запуска
МодельВерсия, формат весов и квантизация
ПамятьТип памяти, доступный объем и свободный запас перед тестом
ПротоколРазмер блоков, их порядок, число блоков и давление
КалибровкаНаблюдаемые признаки hit и miss
РезультатRetained tokens, retained % capacity и oldest evicted
ПовторяемостьРезультаты повторных прогонов и замеченные расхождения

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

Для отдельного сравнения на одной видеокарте полезно разделять влияние формата KV, MTP, NVFP4 и поведения самого движка. Методика с явным разделением подтвержденных данных и предположений приведена в разборе запуска Qwen 3.8 27B на RTX 5090.

Какие факторы меняют реальную емкость KV cache

VRAM, unified memory и свободный запас системы

Фактическое удержание зависит от свободной памяти после размещения весов и запуска инференса. В доступный объем конкурируют KV cache, входной контекст, рабочие буферы и процессы ОС. При наличии других приложений или сервисов граница вытеснения может наступить раньше.

VRAM и unified memory нужно описывать раздельно. Видеокарта имеет собственный пул памяти, хотя на поведение могут влиять другие потребители. Apple Silicon использует общую память системы, где модель и рабочие данные конкурируют с macOS. В обоих случаях показатель из спецификации устройства не заменяет замер свободного запаса перед тестом.

При сравнении конфигураций записывайте состояние памяти в момент старта и после загрузки модели. Разница между этими двумя значениями показывает, сколько ресурса уже заняли веса и служебные структуры, но не предсказывает результат без самого stress test.

Модель и параметры запуска

Размер и архитектура модели влияют на объем памяти под веса и рабочие структуры. Формат весов или квантизация меняют размещение модели. Длина контекста, параметры KV cache и выбранные настройки сервера меняют объем данных, который требуется удерживать.

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

При длинном контексте особенно легко принять теоретическую вместимость за рабочую. Отдельный материал о расширении контекстного окна на одной RTX 4090 показывает, почему вместе с заявленным объемом нужно анализировать способ хранения KV и компромиссы конкретной конфигурации: разбор расширения контекстного окна до 350K токенов.

Почему нужно повторять измерение после изменения конфигурации

Повторный тест нужен после смены модели, формата весов, аппаратной платформы, версии движка или существенного параметра запуска. Изменение фоновой нагрузки тоже достаточно, чтобы заново проверить результат на машине с ограниченным запасом памяти.

Храните результаты в одной форме. Для каждого прогона сохраняйте дату, конфигурацию, протокол и три основные метрики. Такая история помогает отличить реальное изменение поведения от разницы в размере блоков или ошибочного правила калибровки.

Если повторные запуски дают разные значения при одинаковых входах, сначала ищите неконтролируемый фактор: параллельные запросы, фоновые процессы, изменение свободной памяти или отличающийся порядок операций. Стабильность результата нужно подтвердить до публикации сравнения.

Какие выводы тест позволяет делать, а какие нет

Что можно утверждать по результату

По корректно записанному прогону можно сообщить, сколько контекстов или токенов сохранилось после заданного давления. Можно указать относительную долю удержания, самый ранний вытесненный блок, порядок добавления и условия, при которых появилась потеря.

Для технического отчета подходит формулировка: при конкретной модели, версии движка, аппаратной конфигурации и заданном протоколе инструмент подтвердил удержание указанного объема. Такой вывод проверяем и привязан к наблюдаемым данным.

Что нельзя утверждать без дополнительных тестов

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

Stress test не заменяет тестирование пропускной способности, задержки, качества ответов и устойчивости под реальной многопользовательской нагрузкой. Он измеряет удержание проверенных контекстов в одном сценарии. Связь между этим результатом и скоростью генерации нужно проверять отдельно.

Нельзя объяснять каждый промах конкретной политикой вытеснения, если инструмент наблюдает только внешний сигнал. Для такого вывода потребуется независимая проверка поведения движка и повторяемый эксперимент.

Практический итог для выбора inference-движка

Stress test KV cache полезен, когда нужно проверить собственный сценарий локальной LLM и отделить фактическое удержание от рекламируемого лимита. Он помогает сравнить vLLM, llama.cpp, SGLang и другие серверы при одинаковом протоколе, но результат всегда привязан к конкретной конфигурации.

Для выбора движка используйте совокупность данных: retained tokens, retained % capacity, oldest evicted, требования к VRAM или unified memory, поведение под рабочей нагрузкой, скорость и качество генерации. Ни один показатель не заменяет остальные.

Рабочий порядок простой: откалибровать hit и miss, изолировать сервер, последовательно заполнить кэш, создать давление, проверить старые контексты, сохранить отчет и повторить измерение после изменений. Такой протокол позволяет поймать ложное обещание емкости без вывода, который эксперимент не подтверждает.

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