Адаптивный KV-стриминг в llama.cpp - схема, при которой пул видеопамяти перераспределяется между двумя фазами работы модели: обработкой промпта и декодированием. Если контекст вырастает настолько, что KV-кэш перестаёт помещаться в VRAM, недостающая часть подгружается из оперативной памяти хоста по мере необходимости. Форк опубликован на GitHub: за основу взят форк llama.cpp от Raymond, доработки внёс энтузиаст под ником tsangberg.
Отличие от привычной выгрузки слоёв принципиальное. При частичном offload веса модели живут в RAM, и каждый новый токен требует прогонки слоёв по PCIe, из-за чего скорость генерации падает до однозначных значений. В форке с адаптивным стримингом веса остаются в VRAM, а по шине перемещается только KV-кэш, и лишь тогда, когда он не влезает в доступный пул. Автор заявляет более высокий TG tps по сравнению с обычным выгрузом слоёв в RAM. Независимых замеров, которые это подтверждали бы, на момент публикации нет, поэтому цифры стоит проверять на своём железе.
Вторая часть идеи - горячая замена спекулятивного декодирования. Спекулятивная модель выгружается из VRAM, когда начинается стриминг KV-кэша, и возвращается обратно, когда объём контекста снижается. Автор проверял схему на Qwen 3.8 27B в квантизации UD-IQ4_XS и GPU с 16 ГБ видеопамяти (5060 Ti) и отмечает, что подход может пригодиться и владельцам карт с большим объёмом VRAM.
Что такое адаптивный KV-стриминг в llama.cpp и какую проблему он решает
KV-кэш в llama.cpp резервируется под максимальную длину контекста сразу при запуске. Число слоёв, число голов, размерность и тип квантования KV задают объём, который вычитается из свободной VRAM до того, как модель сгенерирует первый токен. Дальше этот резерв простаивает частично или полностью: при коротком диалоге большая его часть не используется, но и другим задачам не достаётся.
На карте с 16 ГБ арифметика жёсткая: веса крупной модели в квантизации, спекулятивная модель и KV-кэш делят один и тот же бюджет. Как только вы двигаете ползунок контекста вверх, приходится либо резать число слоёв на GPU, либо отказываться от спекулятивного декодирования.
Адаптивный пул меняет схему распределения. Память не делится между задачами один раз и навсегда, а перераспределяется по ходу работы: одна раскладка нужна на фазе обработки промпта, другая на фазе декодирования. Когда суммарный KV-кэш превышает доступный объём VRAM, излишек живёт в RAM хоста и читается по мере обращения к соответствующим токенам.
Разница в профиле нагрузки объясняет, почему схема вообще работает. При offload слоёв по PCIe едет весь массив весов на каждый токен, и это десятки и сотни мегабайт на шаг. При стриминге KV по шине перемещаются только те блоки кэша, которые нужны для текущего шага внимания, и только когда они не поместились в видеопамять.
Чем это отличается от классической выгрузки слоёв в RAM
Параметр --n-gpu-layers в llama.cpp определяет, сколько слоёв останется на GPU, а остальные уедут на CPU. Весовые матрицы выгруженных слоёв лежат в оперативной памяти, и при каждом шаге декодирования они читаются по PCIe. Скорость упирается в пропускную способность шины и памяти хоста, поэтому TG tps проседает в разы.
Адаптивный KV-стриминг переносит узкое место в другое место. Все слои модели остаются в VRAM, а в RAM уходит часть KV-кэша. Картина по PCIe становится периодической: всплески трафика при подкачке блоков кэша вместо ровного потока на каждом токене. Для генерации это выгоднее по двум причинам: объём перекачиваемых данных меньше, а сами обращения реже.
| Критерий | Выгрузка слоёв в RAM | Адаптивный KV-стриминг |
|---|---|---|
| Где живут веса | Часть в RAM, часть в VRAM | В VRAM |
| Что идёт по PCIe | Веса выгруженных слоёв на каждом токене | Блоки KV-кэша, только при нехватке VRAM |
| Когда просадка | Постоянно | На длинном контексте и при переключении режимов |
| Что ограничивает контекст | Свободная VRAM после вычета весов | Свободная VRAM плюс пропускная способность RAM |
Пул VRAM: как память перераспределяется между промптом и декодированием
Обработка промпта и декодирование нагружают память по-разному. На префилле модель прогоняет весь промпт за один проход, и пик потребления приходится на промежуточные вычисления внимания. На декодировании шаг маленький, зато растёт KV-кэш: каждый новый токен добавляет строку в кэш всех слоёв.
Статичная конфигурация вынуждена закладываться на худший случай обеих фаз сразу. Адаптивный пул распределяет видеопамять по факту: больше под вычисления на промпте, больше под хранение на генерации. Точную механику стоит смотреть в исходниках форка, здесь даю концептуальную картину. Как объём VRAM и длина контекста меняют скорость на практике, мы разбирали в материале про Qwen3.8-Flash-Next в llama.cpp: там же показано, почему часть замеров легко истолковать неверно.
Зачем нужна горячая замена спекулятивного декодирования (MTP и DFlash2)
Спекулятивное декодирование ускоряет генерацию за счёт вспомогательной модели: она предлагает несколько токенов вперёд, а основная модель проверяет их за один проход. Проверка дешевле, чем генерация по одному токену, поэтому TG tps растёт. Плата за это - видеопамять под спекулятивную модель и её собственный кэш.
В форке есть горячая замена для двух механизмов спекулятивного декодирования, названных автором: MTP и DFlash2. Логика такая: когда начинается стриминг KV-кэша, спекулятивная модель выгружается из VRAM, освобождая место под кэш; когда объём контекста снижается, она загружается обратно. Как DFlash2 сочетается с другими техниками ускорения и кэширования на Qwen 3.8 27B, разбирали в отдельном разборе стека с двумя RTX 3090.
Почему спекулятивная модель и KV-кэш конкурируют за VRAM
На 16 ГБ одновременно должны уместиться три сущности: веса основной модели в квантизации (в примере автора Qwen 3.8 27B UD-IQ4_XS), KV-кэш растущего контекста и спекулятивная модель. Пока контекст короткий, места хватает всем. Как только он разрастается, сумма трёх слагаемых превышает бюджет, и что-то приходится убирать.
Классический выход - отказаться от спекуляции и потерять ускорение. Форк предлагает другой вариант: не выбирать между спекуляцией и длинным контекстом, а переключаться между режимами по ходу работы.
Когда спекулятивное декодирование включается обратно
По описанию автора, спекулятивная модель возвращается в VRAM при снижении объёма контекста. Логика такая: длинный контекст, значит стримим KV и держим спекуляцию выключенной; контекст сократился (новый диалог, сброс сессии, короткий запрос), значит спекуляция снова доступна.
Конкретные пороги переключения - вопрос кода форка. В README и исходниках стоит искать, по какому критерию считается, что памяти достаточно, и сколько времени занимает обратная загрузка модели.
Тестовый сценарий: Qwen 3.8 27B UD-IQ4_XS на 16 ГБ VRAM
Автор проверял подход на модели Qwen 3.8 27B в квантизации UD-IQ4_XS и GPU с 16 ГБ видеопамяти (5060 Ti). Конкретных значений TG tps, задержек и объёмов контекста в описании нет, поэтому цифры не привожу: заявка автора состоит в более высокой скорости генерации по сравнению с обычным выгрузом слоёв в RAM.
Почему 27B в IQ4_XS - показательный кейс для 16 ГБ
IQ4_XS - агрессивная квантизация, которая снижает объём весов и позволяет крупной модели влезть в ограниченную видеопамять. Даже с ней 27B остаётся тяжёлым грузом для 16 ГБ. Основной дефицит возникает не на весах, а на KV-кэше: чем длиннее контекст, тем больше памяти нужно под ключи и значения всех слоёв.
Связка «27B и 16 ГБ» относится к классу сценариев, где модель либо не влезает целиком, либо влезает с очень скромным контекстом. Любая схема с умным распределением памяти здесь заметна сразу. Похожая ситуация у моделей линейки Qwen3.8: о том, как читать заявления об их архитектуре и локальном запуске, есть отдельный разбор.
Что именно измеряет TG tps и почему это ключевая метрика
TG tps (tokens per second) показывает, сколько новых токенов модель выдаёт после того, как промпт обработан. Эта метрика определяет комфорт в диалоге: при 5 токенах в секунду ответ читается примерно со скоростью человеческой речи, при 1-2 токенах в секунду работа превращается в ожидание. Отдельная метрика - скорость обработки промпта (PP), она важна для длинных вставок и RAG-сценариев, но на восприятие генерации влияет меньше.
Поэтому в форке целятся именно в TG tps, а не в абстрактную производительность. Конкретные значения не привожу: подтверждённых замеров нет, а переносить чужие цифры на другую систему бессмысленно.
Ограничения и подводные камни адаптивного KV-стриминга
Схема решает конкретную проблему и создаёт новые. Ниже то, что стоит проверить до сборки.
- Подкачка KV-кэша из RAM не бесплатна: каждый промах по VRAM означает чтение по PCIe и рост задержки.
- Переключение режимов спекуляции требует времени: выгрузка и обратная загрузка модели - это операции с памятью, и на коротких запросах они могут съесть выигрыш.
- Объём и скорость RAM хоста становятся частью конфигурации: если оперативной памяти мало, стриминг упрётся в неё.
- Это форк, а не мейнстримная ветка llama.cpp: документация может быть минимальной, поведение меняется от коммита к коммиту.
- Независимых бенчмарков нет: все заявления о приросте TG tps идут от автора форка.
PCIe и RAM как новое узкое место
Если раньше всё упиралось в перекачку весов, теперь критичны подкачка KV-кэша и переключение спекулятивной модели. Логично предположить, что на PCIe 3.0 и медленной DDR4 выигрыш окажется скромнее, чем на PCIe 4.0 или 5.0 с быстрой DDR5. Подчеркну: это гипотеза, а не измеренный факт, прямых сравнений на разных платформах в описании форка нет.
Профиль доступа к кэшу тоже зависит от сценария. Кэш читается блоками, и на длинной истории с редкими обращениями к её началу промахов будет больше, чем на коротком диалоге с локальным контекстом.
Когда проще снизить контекст или взять модель меньше
Если задачи - короткие диалоги и небольшие запросы, адаптивный стриминг может не дать заметной разницы: при маленьком контексте KV-кэш и так помещается в VRAM, и механизм просто не включится в работу.
Если нужен очень длинный контекст, иногда дешевле взять модель на 7-14B, которая влезает в 16 ГБ целиком вместе с KV-кэшем. Плата - качество ответов, зато никакой возни с подкачкой. Схемы растягивания контекста на одной карте существуют и за пределами этого форка: например, форк NInfer с квантованием KV-кэша доводит окно до 350K токенов на RTX 4090.
Форк tsangberg рассчитан на другой сценарий: нужна именно крупная модель и длинный контекст на ограниченной VRAM, и вы готовы платить за это настройкой.
Кому и зачем это может пригодиться, кроме владельцев 16 ГБ
Автор отмечает, что подход полезен и владельцам карт с большим объёмом VRAM. Логика простая: адаптивный пул и горячая замена спекулятивной модели помогают расходовать память эффективнее на любой карте, а не только на 16-гигабайтной.
На карте с 24 или 48 ГБ освободившийся резерв можно потратить по-разному: держать более длинный контекст, взять спекулятивную модель побольше или поднять точность KV-кэша. Что из этого даст прирост в вашем случае, зависит от модели и профиля задач. Конкретных цифр для больших карт в описании нет, так что это направление для экспериментов, а не готовый рецепт.
Как подступиться к форку: что смотреть в первую очередь
Репозиторий опубликован на GitHub: это форк llama.cpp от Raymond с доработками tsangberg. Команды сборки и названия флагов здесь не привожу, их нужно брать из README и исходников, а не из вторичного текста.
- Прочитайте README и открытые issues: там видно, какие ограничения автор признаёт и что ломается у других пользователей.
- Сравните код с апстримом llama.cpp, чтобы понимать, какие изменения внесены. Без этого сложно отделить эффект стриминга от прочих правок.
- Начните с той же конфигурации, что у автора: Qwen 3.8 27B UD-IQ4_XS и 16 ГБ VRAM. Так у вас будет база для сравнения с обычным запуском.
- Замерьте TG tps до и после на одинаковых промптах и одинаковой длине контекста. Без собственных замеров оценить выигрыш невозможно.
Держите в голове, что это форк, а не официальная ветка. Стабильность, совместимость и поддержка лежат на его авторе.
Итог: стоит ли следить за адаптивным KV-стримингом
Адаптивный пул VRAM и горячая замена спекулятивного декодирования - осмысленный инженерный ход для сценария «крупная модель, длинный контекст, ограниченная видеопамять». Он не отменяет физику: подкачка KV-кэша по PCIe остаётся платной, а переключение спекулятивной модели занимает время.
Главный вопрос практический: оправдывает ли прирост TG tps возню со сборкой форка. Ответ зависит от вашего железа, модели и длины диалогов. Независимых замеров пока нет, поэтому надёжный способ проверить один - запустить форк у себя и сравнить TG tps с обычным llama.cpp на тех же промптах.
Если 16 ГБ не хватает, а контекст нужен длинный, смотреть в сторону форка стоит. Если задача решается моделью меньшего размера, время, скорее всего, уйдёт впустую.