Гибридные модели обещают простую вещь. Память под контекст перестаёт расти линейно с его длиной, значит длинный контекст дешевеет, значит на ту же карту влезает больше пользователей. Звучит настолько разумно, что проверять как-то неловко.
Я всё-таки проверил, потому что интересовало как эта архитектура будет вести себя под нагрузкой и вписываться в существующий стек инференса. Снял на вечер машину с двумя RTX 3090 и прогнал по двум моделям одну и ту же агентскую нагрузку. Qwen3-4B: обычная, полная аттеншн во всех 36 слоях. Qwen3.5-4B: гибридная, полная аттеншн осталась в 8 слоях из 32, а остальные 24 заменены на рекуррентные слои Gated DeltaNet (arXiv:2412.06464), которые вместо растущего списка ключей и значений держат стейт фиксированного размера.
Короткий ответ вышел такой: под нагрузкой гибрид чаще проигрывает чем выигрывает. Он выигрывает там, где важна цена памяти на токен: длинный контекст, длинная генерация, много одновременных сессий на одной карте. А на самом частом сценарии, коротком диалоге с общим системным промптом, он проигрывает, и большая часть отставания приходится на кеш, а не на скорость самой модели.
Проигрывает он из-за того, как устроена его память. Рекуррентные состояния и обычные KV-страницы лежат в одном пуле, страницы там обязаны быть одного размера, и чтобы страница аттеншн догнала по размеру страницу состояния, движок поднимает блок кеша с 16 токенов до 528. Совпадение префиксов после этого засчитывается в 33 раза грубее: на каждом запросе заново пересчитывается кусок промпта, который у обычной модели нашёлся бы в кеше. На моей нагрузке это 84% попаданий против 94% у обычной модели и в три раза больше токенов промпта, которые каждый раз считаются с нуля. Подробно все преимущества и недостатки разберем дальше.
1. Небольшой глоссарий
Базовую информацию про KV-кеш, префиксное кеширование и блоки кеша можно прочитать в другой моей статье KV-Cache в LLM: разбираем инференс через 9 ключевых вопросов.
Гранулярность. Размер блока, которым нарезается KV-кеш.
Тенант. Один синтетический клиент со своим системным промптом и диалогом.
Рабочий набор. Суммарный контекст активных тенантов.
Попадания в кеш. Доля токенов промпта, которые нашлись в кеше
TTFT. Время до первого сгенерированного токена.
ITL. Среднее время между генерацией следующего токена.
2. Арендуем стенд для экспериментов.
Прогоны шли на одной карте с одними версиями софта, чтобы разница осталась только в моделях. Железо арендованное и намеренно скромное: мне были интересны числа, которые получаются на потребительской машине, которую обычно снимают для своих микро B2B вертикальных SaaSов.
|
|
Значение |
|---|---|
|
GPU |
2× RTX 3090, 24 ГБ; в большинстве замеров работала одна карта |
|
CPU / RAM |
16 vCPU, 125 ГБ, swap отключён |
|
Шина |
PCIe Gen4 x16, замерено 23.57 ГБ/с |
|
Драйвер / CUDA |
595.58.03 / 13.2 |
|
vLLM / torch |
0.26.0 / 2.11.0+cu130 |
Сами модели:
|
|
Qwen3-4B |
Qwen3.5-4B |
|---|---|---|
|
Слоёв |
36, все с полным KV |
32: 8 с полным KV, 24 рекуррентных (Gated DeltaNet) |
|
Геометрия аттеншн |
GQA 32/8, размерность головы 128 |
GQA 16/4, размерность головы 256 |
|
KV на токен |
144 КБ |
36 КБ |
|
Рекуррентное состояние |
нет |
2.05 МБ на слой, 49.1 МБ на снимок |
|
Веса в bf16 |
7.49 ГБ |
8.68 ГБ (модель мультимодальная, с башней зрения) |
|
Класс модели в vLLM |
|
|
|
Время старта |
~30 с |
127 с, включая 33 с компиляции |
Про синтетические данные для теста: нагрузка у меня агентская, то есть длинный повторяющийся префикс плюс короткий уникальный хвост. Это профиль, на котором префиксный кеш имеет смысл. Если ваша нагрузка — независимые одиночные запросы без общих начал, половина выводов ниже к вам не относится.
3. Full Attention vs Gated DeltaNet: как две архитектуры помнят прошлое
Двадцать четыре слоя из тридцати двух у гибрида не хранят KV. Без механизма все дальнейшие числа выглядят произвольными, поэтому сначала он.
Модель с полной аттеншн помнит контекст списком. На каждый токен, в каждом слое, в каждой KV-голове она держит пару векторов, и список растёт линейно с длиной контекста. Префиксное кеширование поверх такого устройства — это шаринг кусков списка: совпало начало промпта, значит совпали куски списка, второй запрос берёт готовые.
Рекуррентный слой помнит контекст состоянием. Вместо растущего списка он держит один тензор фиксированного размера — состояние. На каждом новом токене слой частично затирает то, что в состоянии уже лежит, и дописывает туда поправку от нового токена. Первое — управляемое забывание, «гейт». Второе — правило дельты, корректирующая запись вместо слепого накопления. Отсюда и название архитектуры, Gated DeltaNet.
Полная аттеншн ведёт блокнот, в который только дописывают. Рекуррентный слой пишет на доске и каждый раз частично её стирает.
Размер состояния не зависит от длины контекста: на Qwen3.5-4B это 2.05 МБ на слой и при 500 токенах, и при 30 тысячах. Прошлое в нём сжато с потерями — восстановить конкретный токен, который был двадцать тысяч шагов назад, из сводки нельзя.
Префиксное кеширование для гибрида поэтому — отдельная инженерная задача. Чтобы второй запрос переиспользовал общий префикс, ему нужна сохранённая копия состояния в точке конца префикса: текущее состояние уже испорчено уникальным хвостом первого запроса. У полной аттеншн такой проблемы нет, там блоки после записи не меняются, и хвост первого запроса лежит в своих блоках, не задевая общие.
В vLLM за снимки состояний отвечает параметр mamba_cache_mode с тремя значениями:
|
Режим |
Что хранится |
Годится для префиксного кеша |
|---|---|---|
|
|
одно состояние на активный запрос |
нет, переиспользовать нечего |
|
|
состояние на границах блоков и на шаге планировщика |
частично |
|
|
полные снимки на каждой границе блока |
да |
На моей модели в vLLM 0.26.0 при включении кеша движок принудительно откатывается в align с предупреждением, потому что класс Qwen3_5ForConditionalGeneration не объявляет интерфейс SupportsMambaPrefixCaching, поэтому всё дальнейшее про цену кеширования относится к align режиму.
Если коротко: гибрид помнит прошлое сжатой сводкой вместо растущего списка, и переиспользовать префикс между запросами он может только через снимки этой сводки.
4. 36 КБ против 144 и блок 528 вместо 16
Состояние фиксированного размера должно быть видно в первом же практическом числе — в байтах KV на токен. Считаю по слоям, у которых полный KV есть: у Qwen3-4B это 36 слоёв из 36, у гибрида 8 из 32.
Вес токена — произведение шести множителей: 2 (ключи и значения) × число слоёв × число KV-голов × размерность головы × 2 байта на элемент. Все они лежат в config.json модели, то есть посчитать это можно до аренды GPU.
|
|
Qwen3-4B |
Qwen3.5-4B |
|---|---|---|
|
Расчёт |
2 × 36 × 8 × 128 × 2 байта |
2 × 8 × 4 × 256 × 2 байта |
|
КБ на токен, расчёт |
144.0 |
32.0 |
|
КБ на токен, из лога старта vLLM |
144.03 |
36.14 |
У полной аттеншн расчёт и замер сходятся до третьего знака. У гибрида замер выше расчёта на 4 КБ, и это первый признак того, что разбирается ниже: рекуррентные состояния лежат в том же пуле, что и KV, и их страницы размазываются по ёмкости кеша.
Что это даёт по ёмкости:
|
Из лога старта |
Qwen3-4B |
Qwen3.5-4B |
|---|---|---|
|
Веса / активации |
7.56 / 0.74 ГиБ |
8.61 / 1.70 ГиБ |
|
Памяти осталось под кеш |
12.83 ГиБ |
10.26 ГиБ |
|
Ёмкость кеша |
93 408 токенов |
297 720 токенов |
|
Одновременных запросов по 16k токенов |
5.70 |
18.17 |
Мой замер. Обе модели, 1× RTX 3090 24 ГБ, vLLM 0.26.0,
--gpu-memory-utilization 0.9. Числа сняты из лога стартаvllm serve.
Гибриду досталось на 2.6 ГиБ меньше памяти под кеш: веса тяжелее, активации больше в 2.3 раза. И при этом ёмкость у него в 3.2 раза выше.
К этой ёмкости есть поправка, без которой дальше ничего не воспроизведётся. Для гибридных моделей vLLM 0.26 выключает префиксное кеширование по умолчанию, и --enable-prefix-caching приходится ставить руками:
# vllm/engine/arg_utils.py
# Hybrid models support prefix caching but keep it opt-in for now
# while the feature matures.
default_prefix_caching = (
model_config.is_prefix_caching_supported and not model_config.is_hybrid
)
Сам флаг обходится в 8% ёмкости, с 297 720 до 274 216 токенов, и это цена экономного режима снимков состояний из раздела 3. Дальше кеш включён у обеих моделей везде, кроме прогонов, где я пишу обратное; во что обходится забытый флаг под нагрузкой, видно в разделе 8.1.
4.1. За что платит эта ёмкость
В том же логе запуска гибрида стоит необычное значение block_size: размер блока кеша 528 токенов вместо привычных 16. Такого числа я до этого не встречал ни у одной модели. Объяснение движок дал сам, двумя строками:
Setting attention block size to 528 tokens to ensure that attention page size is >= mamba page size.
Padding mamba page size by 0.76%.
Причина — в устройстве того самого единого пула. Гибридный менеджер кеша режет видеопамять на одинаковые слоты-страницы, и в этом пуле лежат объекты двух сортов. Страница аттеншн хранит K и V для block_size токенов одного слоя, и её размер зависит от настройки. Страница рекуррентного состояния хранит сводку одного слоя, и её размер задан архитектурой. Полка с ячейками одного размера не может иметь ячейку меньше самого крупного предмета, а состояние сжать нельзя, поэтому подтягивать приходится страницу аттеншн.
Считается это на бумаге, и я сошёлся с логом. Состояние слоя складывается из двух частей: ssm-часть это 32 × 128 × 128 значений в fp32, то есть 2 097 152 байта, и conv-часть — ещё 49 152 байта. Итого 2 146 304 байта на слой. Страница аттеншн на один токен блока — это K и V по 4 KV-головы на 256 значений в bf16, то есть 4 096 байт.
|
Величина |
Байт |
|---|---|
|
страница состояния, на слой |
2 146 304 |
|
страница аттеншн, на один токен блока |
4 096 |
|
минимальный блок, при котором аттеншн догоняет |
2 146 304 / 4 096 = 524 |
|
блок обязан быть кратен 16, ближайший сверху |
528 |
|
страница аттеншн при блоке 528 |
2 162 688 |
|
паддинг страницы состояния до равенства |
16 384 байта, или 0.76% |
Расчётные 0.76% совпали с логом vLLM до второго знака, значит механизм я понял верно. Любопытная деталь: число 2 146 304 делится на 4 096 ровно, то есть блок в 524 токена дал бы страницы одинаковыми байт в байт, и весь паддинг — это плата за кратность 16, которую требуют вычислительные ядра.
Вернуть блок 16 руками нельзя. Движок умеет размер блока только повышать:
# vllm/platforms/interface.py
attn_block_size = kernel_block_alignment_size * cdiv(
mamba_page_size,
kernel_block_alignment_size * attn_page_size_1_token,
)
...
if cache_config.block_size < attn_block_size:
cache_config.block_size = attn_block_size
Флаг --block-size 16 будет перебит молча, и в логе появится та же строка про 528.
4.2. Мелкий блок гибриду не подходит и по второй причине
В полноценном режиме снимков состояний (all) снимок делается на каждой границе блока и весит 49.1 МБ. Значит его цена в пересчёте на токен — это 49.1 МБ, поделённые на размер блока:
|
Размер блока |
KV + снимки, КБ на токен, гибрид |
Для сравнения, Qwen3-4B |
|---|---|---|
|
16 |
32 + 3 144 = 3 176 |
144 |
|
64 |
32 + 786 = 818 |
144 |
|
256 |
32 + 196 = 228 |
144 |
|
512 |
32 + 98 = 130 |
144 |
|
1024 |
32 + 49 = 81 |
144 |
Точка, где гибрид со снимками становится дешевле обычной модели по памяти, лежит около 450 токенов на блок. При блоке 16 он был бы дороже в 22 раза: кеширование префиксов на мелких блоках съело бы всю ёмкость и даже больше.
Если коротко: гибрид платит за токен в 4 раза меньше и получает в 3 раза большую ёмкость на меньшем куске видеопамяти. Взамен блок кеша становится 528 вместо 16, и настройками это не снимается — размер вытекает из требования «страница аттеншн не меньше страницы состояния» и сходится с логом до 0.76% паддинга. Оба факта проверяются одной строкой в логе старта, вместе с тем, включён ли вообще кеш:
vllm serve <model> 2>&1 | grep -Ei 'prefix_caching|attention block size|KV cache size'
5. Минусы большой гранулярности
Блок в 33 раза крупнее обязан за что-то платить, и плату я ожидал увидеть в проценте попаданий. Первый замер показал обратное: у гибрида 77% попаданий против 52% у обычной модели.
В той единственной точке смешаны два изменения: ёмкость выросла в 3 раза, гранулярность ухудшилась в 33. По одной точке нельзя понять, какое из них дало эффект. При 40 тенантах рабочий набор около 132 тысяч токенов, у обычной модели ёмкость 97.7 тысячи, набор не влезает, начинается вытеснение. У гибрида 274 тысячи, всё влезает. То есть в этой точке эффект ёмкости просто перекрыл эффект гранулярности.
Чтобы разделить их, я прогнал свип по числу тенантов при фиксированной нагрузке 6 запросов в секунду. Гипотезу записал до прогона, чтобы потом не подгонять объяснение под факт: при малом рабочем наборе обычная модель должна обогнать гибрид по попаданиям, потому что вытеснения нет ни у одной и сравнивается только гранулярность.
|
Тенантов |
Рабочий набор |
Qwen3-4B, попаданий |
Qwen3.5-4B, попаданий |
Qwen3-4B, TTFT p50 |
Qwen3.5-4B, TTFT p50 |
|---|---|---|---|---|---|
|
5 |
17k |
94.3% |
84.0% |
58 мс |
99 мс |
|
10 |
33k |
94.4% |
84.1% |
59 мс |
103 мс |
|
20 |
66k |
94.3% |
84.3% |
61 мс |
109 мс |
|
40 |
132k |
54.6% (перегруз) |
81.9% |
1 696 мс |
110 мс |
|
80 |
264k |
26.4% |
24.9% |
8 951 мс |
6 315 мс |
Мой замер. vLLM 0.26.0, 1× RTX 3090, префиксный кеш включён у обеих моделей, 6 ходов диалога, запрошено 6 req/s. Попадания считаются по разности счётчиков
vllm:prefix_cache_queries_totalиvllm:prefix_cache_hits_totalдо и после прогона, в токенах. По одному прогону на точку.
Так и вышло. На 5, 10 и 20 тенантах разрыв ровно 10 процентных пунктов в пользу мелкого блока, и это чистая цена гранулярности: вытеснения нет ни у одной модели, сравнивается только она.
5.1. Откуда берутся именно 10 пунктов
Каким этот разрыв должен быть по арифметике, я считал уже после замера, так что это объяснение факта, а не предсказание.
Совпадение засчитывается целыми блоками, значит на границе совпадения теряется остаток. Для случайной длины совпадения средняя потеря — половина блока: 8 токенов при блоке 16 и 264 при блоке 528. Разница между архитектурами — 256 токенов на запрос.
Средний запрос в моей нагрузке просит 2 891 токен промпта — это замер на той же нагрузке, а не оценка. 256 от 2 891 — это 8.9% при замеренном разрыве в 10.3 пункта (94.3% против 84.0%). То есть округление объясняет почти весь разрыв. Расчёт опирается на допущение, что совпадающая часть идёт одним непрерывным куском с начала промпта; в моей нагрузке так и есть, потому что общее начало — это системный префикс плюс история диалога. В разделе 9 размер блока меняется ещё раз, и там та же модель ошибается в 2 раза, так что 8.9 против 10.3 — хорошее первое приближение.
Потеря на границе — константа в токенах, от длины промпта она не зависит. Значит её доля падает с ростом контекста:
|
Средний промпт |
Потеря 264 токена — это |
|---|---|
|
2 900 токенов (моя нагрузка) |
9.1% |
|
8 000 токенов |
3.3% |
|
16 000 токенов |
1.6% |
|
100 000 токенов |
0.3% |
Крупный блок бьёт по коротким сессиям и почти не виден на длинных.
Обе границы обвала я предсказал до прогона, поделив ёмкость из лога старта на длину контекста тенанта: 93.4 тысячи на 3.3 даёт 28 тенантов, 274.2 на 3.3 даёт 83. Замер охватывает обе — обвалы наблюдаются между 20 и 40 и между 40 и 80 тенантами.
Собираю карту применимости.
|
Рабочий набор |
Кто выигрывает |
Почему |
|---|---|---|
|
до ~90k токенов |
полная аттеншн |
мелкий блок: 94% попаданий против 84%, TTFT ниже в 2 раза |
|
~90k … 270k |
гибрид |
у полной аттеншн кеш кончился, у гибрида ещё нет |
|
больше ~270k |
никто |
не влезает ни у кого: 26.4% против 24.9% |
Одна оговорка про задержку. Разрыв 58–61 мс против 99–109 не весь про кеш: у рекуррентных слоёв есть своя постоянная стоимость, и на коротких промптах она не окупается. Разбор в разделе 6.
График выше объясняет, почему я гоняю кривую по нагрузке, а не одну точку. При 8 req/s три конфигурации из четырёх уже не справляются, и разрыв между ними измеряется секундами — это разрыв длины очереди, а не задержки системы.
6. Префилл: второе преимущество гибрида
С памятью разобрались, осталось второе обещание гибридных архитектур, ради которого их и делают: длинный контекст должен дешеветь. Меряю напрямую — стоимость обработки промптов разной длины.
Работа аттеншн растёт квадратично с длиной контекста, потому что каждый токен смотрит на все предыдущие. У гибрида таких слоёв 8 из 32, значит квадратичная часть работы должна быть примерно в 4 раза меньше, и стоимость тысячи токенов должна расти с длиной контекста заметно медленнее. Ожидаемый множитель я записал до прогона: у обычной модели на 16 320 токенах ожидалось около 190 мс на тысячу токенов, а кривая гибрида должна быть площе примерно в 4.5 раза.
|
Токенов в промпте |
Qwen3-4B, мс на 1k |
Qwen3.5-4B, мс на 1k |
|---|---|---|
|
512 |
138.1 |
154.2 |
|
1 024 |
129.5 |
133.2 |
|
2 048 |
124.3 |
126.5 |
|
4 096 |
133.1 |
127.6 |
|
8 192 |
153.1 |
131.3 |
|
16 320 |
193.8 (+56%) |
141.1 (+12%) |
Мой замер. vLLM 0.26.0, 1× RTX 3090, уникальные промпты, кеш в замере не участвует, длина режется токенизатором ровно, лимит контекста спрашивается у сервера. Эффективная производительность на префилле — 61–65 TFLOP/с, около 86% паспорта 3090.
Факт: 193.8 мс на тысячу токенов, то есть предсказание попало с точностью 2%. Подгонка прямой к этим точкам даёт линейную часть 114.3 у обычной модели и 124.4 у гибрида — почти одинаково, потому что матричные умножения в остальной части модели стоят примерно одинаково. Вся разница сидит в квадратичном коэффициенте, и отношение этих коэффициентов вышло 4.7 при отношении числа слоёв с полной аттеншн 36 к 8, то есть 4.5. Совпадение с точностью 5% означает, что экономия на префилле объясняется заменой слоёв и ничем больше.
На графике видна и обратная сторона. При 512 токенах гибрид медленнее (154.2 против 138.1), потому что у рекуррентных слоёв есть своя постоянная стоимость, которая на коротких промптах не окупается. Кривые пересекаются около 2.5 тысячи токенов. Отсюда и разница в TTFT из раздела 5: там контексты по 3.3 тысячи, чуть правее точки пересечения, и вклад постоянной стоимости всё ещё заметен.
6.1. Оговорка, которую я нашёл, разбирая артефакты
Кривую префилла я снимал дважды на каждой модели, в конфигурациях с включённым и выключенным префиксным кешем. У обычной модели два прогона совпали до 0.1%: 193.75 и 193.89 мс на тысячу токенов при 16 320. У гибрида они разошлись на 7.6%.
|
Токенов |
гибрид, кеш выключен |
гибрид, кеш включён |
|---|---|---|
|
2 048 |
126.5 |
140.2 |
|
16 320 |
141.1 |
151.8 |
Расхождение систематическое. С включённым кешем движок работает в режиме align и пишет снимки состояний на границах блоков, а это дополнительная работа поверх самого префилла. То есть включение кеша стоит гибриду не только 8% ёмкости, но ещё и 7.6% скорости префилла.
Таблица выше в этом разделе взята из прогона с выключенным кешем. Значит для нагрузок с переиспользованием, где кеш включать надо, преимущество на 16 тысячах токенов не 27%, а 21.7%. Для уникальных промптов, где кеш всё равно выключают, верны исходные 27%.
Если коротко: на 16 тысячах токенов префилл гибрида быстрее на 27% без префиксного кеша и на 22% с ним. Преимущество не зависит от рабочего набора, а на промптах короче 2.5 тысячи токенов меняет знак.
7. Генерация: гибрид отдаёт токен быстрее и заметно менее ровно
Всё предыдущее было про первый токен, а пользователь ждёт весь ответ: TTFT плюс несколько сотен межтокенных задержек, и вторая часть обычно длиннее первой в десятки раз.
Здесь у гибрида должно быть преимущество, и оно арифметическое. Каждый шаг генерации вычитывает из памяти веса модели плюс KV всего контекста. KV у гибрида в 4 раза дешевле, значит читать меньше, значит шаг короче.
|
Тенантов |
ITL медиана, полная / гибрид |
ITL среднее, полная / гибрид |
|---|---|---|
|
5 |
14.9 / 13.9 |
17.7 / 20.9 |
|
10 |
14.9 / 14.1 |
17.6 / 21.9 |
|
20 |
15.0 / 14.1 |
17.9 / 22.3 |
Мой замер. Те же прогоны, что и в разделе 5: обе модели с включённым кешем, 6 запросов в секунду, 6 ходов диалога, контекст сессии около 3.3 тысячи токенов. Взяты только точки, где обе модели справляются с нагрузкой. Межтокенная задержка считается по интервалам между кусками потокового ответа.
Медиана у гибрида ниже на 5–6%, то есть типичный токен он действительно отдаёт быстрее. Среднее при этом выше на 18–25%, а среднее выше медианы означает редкие длинные паузы.
Арифметика на медиану сходится. При контексте 2 891 токен шаг обычной модели читает 7.56 ГиБ весов плюс 407 МБ KV, шаг гибрида — те же веса плюс 102 МБ KV и 49 МБ снимка состояний. Итого 7.94 против 7.70 ГиБ, то есть ожидаемые 3% против замеренных 5–6%. Веса гибрида на 14% тяжелее за счёт башни зрения, но текстовый запрос её не читает, поэтому в трафик шага она не входит. С длиной контекста разрыв растёт вместе с долей KV в этом трафике: при 8 тысячах токенов гибрид читает на 9% меньше, при 30 тысячах на 26%, а с fp8 разрыв скромнее в 2 раза — 4% и 14%. Полосу памяти я взял из паспорта 3090, а не замерил, и разделение весов гибрида на языковую часть и башню зрения не проверял: если башня меньше, чем я думаю, преимущество гибрида на декоде я завышаю.
С паузами хуже. Среднее превышает медиану в 1.55 раза против 1.19 у обычной модели, а на fp8 при 40 тенантах — в 1.98 против 1.16. Моё объяснение: vLLM подмешивает префилл новых запросов в те же проходы, где идёт генерация текущих, а гибрид пересчитывает на префилле в три-четыре раза больше токенов, и каждая такая вставка подвешивает чужую генерацию дольше. В пользу этого говорит то, что перекос растёт там же, где растёт объём пересчёта. Против — поведение хвоста: ITL p99 у гибрида в bf16-прогонах лучше (72 против 91–116 мс), а в fp8 хуже (115 против 73). Механизм объясняет среднее и не объясняет хвост, и проверять его надо отдельным замером с изолированным префиллом.
Если коротко: за крупный блок гибрид платит дважды — в TTFT и в ровности генерации. Сама скорость генерации у него чуть выше, и на коротком контексте это единицы процентов, потому что при 3.3 тысячи токенов KV составляет всего 5% того, что читает шаг.
8. Полное время ответа: решает длина ответа
Есть длина ответа, начиная с которой гибрид отыгрывает своё отставание на первом токене, и её можно посчитать. Отставание — это лишние токены пересчёта, помноженные на цену токена префилла. Выигрыш на токен генерации — разница прочитанных байт, делённая на полосу памяти.
|
Контекст |
Токенов ответа до окупаемости, bf16 |
fp8 |
|---|---|---|
|
2.9k |
119 |
688 |
|
8k |
40 |
192 |
|
30k |
12 |
54 |
При контексте 8 тысяч токенов гибрид отыгрывает своё отставание за первые сорок токенов ответа в bf16, то есть на любом развёрнутом ответе выигрывает он, а на вызове инструмента в тридцать токенов — уже нет. С fp8 у обычной модели порог там же поднимается до 192 токенов. При контексте 3 тысячи и коротких ответах на 50–100 токенов выигрывает обычная модель, а с fp8 её преимущество держится почти до семисот токенов ответа.
Практический смысл: выбор архитектуры определяется произведением длины контекста на длину ответа, а не одной длиной контекста. Агент, который читает 10 тысяч токенов и отвечает вызовом инструмента на 30 токенов, и агент, который читает те же 10 тысяч и пишет отчёт на 800, попадают в разные половины этого графика.
8.1. Пропускная способность считается отдельно
Всё выше про время одного ответа. Пропускная способность сервера ведёт себя иначе, и путать их не стоит.
|
Что меряем |
Полная |
Гибрид |
|---|---|---|
|
Запросов в секунду при 40 тенантах |
5.55 |
7.58 |
|
Выходных токенов в секунду, fp8, оба влезают |
284.5 |
276.8 |
|
Выходных токенов в секунду, 5–20 тенантов |
261–281 |
253–278 |
Выигрыш гибрида по запросам в секунду, 1.4 раза, получен там, где обычная модель вытесняла: рабочий набор 132 тысячи токенов против 97.7 тысячи ёмкости. Это выигрыш ёмкости. Там, где влезают обе, агрегат равный, с перевесом в 1–3% в сторону обычной модели: гибрид декодирует чуть быстрее, но тратит в три-четыре раза больше GPU на префилл каждого запроса, и на агрегате это съедает его преимущество.
Здесь же видно, сколько стоит забытый флаг из раздела 4. Гибрид на настройках по умолчанию, то есть с выключенным префиксным кешем, при тех же 40 тенантах и запрошенных 8 req/s обслуживает 2.74 запроса в секунду — примерно как обычная модель, у которой кеш выключили руками (2.13). Ошибок нет, успешность 1.0, лог чистый. Включение кеша на обычной модели даёт 2.6 раза, смена архитектуры без кеша — 1.29 раза, включение кеша на гибриде — 2.8 раза.
Если коротко: одному пользователю с длинным контекстом гибрид отдаёт ответ быстрее, а сервер на нём обслуживает столько же или чуть меньше запросов.
9. fp8: дешёвая экономия памяти, которая гибриду выходит боком
--kv-cache-dtype fp8 — самый дешёвый способ увеличить ёмкость кеша: K и V хранятся в восьми битах вместо шестнадцати, места нужно в 2 раза меньше. На обычных моделях это работает как чистая победа.
На гибриде из арифметики выравнивания следует другое, и я записал это предсказанием до прогона: восьмибитная страница аттеншн в 2 раза меньше, значит блок, которым она догоняет страницу состояния, должен вырасти с 528 до 1056 токенов. То есть дешёвая экономия памяти обязана в 2 раза ухудшить гранулярность.
|
Модель и тип KV |
Ёмкость |
КБ на токен |
Блок |
Попаданий |
TTFT p50 |
|---|---|---|---|---|---|
|
Qwen3-4B, bf16 |
97 696 |
144.04 |
16 |
0.545 |
1 743 мс |
|
Qwen3-4B, fp8 |
186 816 |
72.01 |
16 |
0.944 |
52 мс |
|
Qwen3.5-4B, bf16 |
274 216 |
39.23 |
528 |
0.822 |
109 мс |
|
Qwen3.5-4B, fp8 |
429 707 |
22.72 |
1056 |
0.778 |
142 мс |
Мой замер. vLLM 0.26.0, 1× RTX 3090,
--kv-cache-dtype fp8, у гибрида префиксный кеш включён вручную. 40 тенантов, 6 ходов, запрошено 6 req/s; обслужено 5.26–5.93, то есть первая строка слегка в перегрузе, остальные в стационаре. Один прогон на конфигурацию. Строки bf16 сняты в отдельном прогоне от таблицы раздела 5, отсюда расхождения в третьем знаке. КБ на токен здесь — фактическая цена токена в занятом пуле, поэтому у гибрида 39.23 против 36.14 из раздела 4: разница в страницах состояний, которые появляются при включённом кеше.
Предсказание попало точно: 1056. Механизм выравнивания отработал и в обратную сторону.
У обычной модели fp8 — то, чем и кажется: цена токена падает ровно в 2 раза, ёмкость растёт в 1.91 раза, рабочий набор в 132 тысячи токенов наконец влезает целиком, попадания идут с 54.5% до 94.4%, TTFT с 1.7 секунды до 52 мс. Выигрыш здесь почти весь от исчезнувшего вытеснения: восьмибитный формат сам по себе задержку не уменьшает, он освобождает место.
У гибрида ёмкость выросла только в 1.57 раза. Цена токена упала в 1.73 раза вместо двух, потому что рекуррентные состояния остаются в fp32 и не сжимаются вовсе. Плюс сам пул под кеш стал меньше, с 10.26 до 9.31 ГиБ; моё предположение — это буферы под коэффициенты масштабирования, но по логу старта этого не видно, так что утверждать не буду.
А попадания упали с 82.2% до 77.8%, и TTFT вырос с 109 до 142 мс: покупать было нечего. Рабочий набор в 132 тысячи токенов влезал в 274 тысячи и до fp8, ёмкость выросла впустую, а за подорожавшую границу блока пришлось заплатить.
Здесь же выяснилось, где ошибается модель округления из раздела 5. Блок вырос в 2 раза, средняя потеря на границе должна вырасти с 264 токенов до 528, и падение попаданий по этой модели — около 9 пунктов. Замер даёт 4.4, то есть в 2 раза лучше предсказания. Считать по фактическим длинам промптов вместо усреднения по случайной длине ничего не меняет: у моих тенантов промпт растёт как 2 000 плюс 210 на ход, и средняя потеря на этих шести длинах выходит 325 токенов при блоке 528 и 589 при 1056 — та же разница в 264. Причин может быть две, и ни одну я не проверял: либо счётчики vLLM округляют до блоков и знаменатель тоже, либо при блоке 1056 в контекст на 3.3 тысячи токенов влезает всего три блока, и распределение остатков перестаёт быть похожим на равномерное. Порядок величины округление задаёт, коэффициент — нет; точное значение надо мерить на своей нагрузке.
Если коротко: на полной аттеншн fp8 даёт ровно то, за что его берут, — тот же токен занимает в 2 раза меньше места. На гибриде это обмен ёмкости на гранулярность, и он выгоден только тогда, когда рабочий набор в ёмкость не влезает. Качество ответов при fp8 я не мерил вовсе: восьмибитный KV огрубляет сами значения, а не только их раскладку по памяти, и это отдельная работа.
10. Коротко про Offload
Выгрузка кеша в оперативку. Карта применимости упирается в 270 тысяч токенов рабочего набора, за которыми не влезает ни одна из двух моделей. Лечится это выгрузкой вытесненных блоков в оперативную память хоста: её на машине 125 ГБ против 24 на карте, а сделка считается легко — привезти готовый KV одного токена из оперативки стоит 28 микросекунд, пересчитать его заново 131.
На гибриде я ждал, что выгрузка не заведётся: механизм работает со спецификациями страниц аттеншн, состояния Gated DeltaNet — объекты другой природы, и в исходниках есть прямое ограничение на их конвертацию. Предсказание провалилось. Штатный CPUOffloadingSpec с пулом 60 ГиБ поднялся без единой правки, и при 80 тенантах суммарные попадания пошли с 24.9% до 83.9%, а TTFT p50 — с 6.3 секунды до 128 миллисекунд.
Следствие важнее обоих сюжетов: потолок в 274 тысячи токенов расширяется оперативкой у обеих архитектур. Значит ёмкость перестаёт быть аргументом при выборе архитектуры, если в машине есть оперативная память, и остаётся то, что видно на малых наборах — гранулярность против стоимости префилла.
11. Плюсы и минусы одной таблицей
Вот без двух последних строк:
|
Метрика |
Гибридная Qwen3.5-4B vs обычная Qwen3-4B |
|---|---|
|
Токен KV-кеша |
36 КБ vs 144 КБ (в 4 раза меньше) |
|
Ёмкость KV-кеша на той же GPU |
274–297 тыс. токенов vs 93–98 тыс. (в 3.2 раза больше) |
|
Одновременные сессии по 16k |
18.2 vs 5.7 |
|
Префилл (16k) |
Быстрее на 27% без префиксного кеша и на 22% с ним |
|
Префилл (до 2.5k) |
Медленнее из-за постоянной стоимости рекуррентных слоёв |
|
Гранулярность совпадения |
Блок 528 токенов vs 16 токенов (в 33 раза грубее) |
|
Hit rate при малом рабочем наборе |
84% vs 94%; TTFT 99–109 мс vs 58–61 мс |
|
Hit rate при большом рабочем наборе |
82% vs 55% |
|
Скорость генерации (медиана) |
Быстрее на 5–6% при контексте 3.3k, до 26% при 30k |
|
Стабильность генерации |
Хуже: среднее выше медианы в 1.55–1.98 раза vs 1.16–1.19 |
|
Полное время ответа |
Лучше при контексте от 8k и ответах длиннее 40–190 токенов |
|
Выходные токены/с |
Практически одинаково; обычная модель быстрее на 1–3% |
|
Пропускная способность (40 тенантов) |
7.58 vs 5.55 запросов/с |
|
Префиксный кеш |
По умолчанию выключен; включение стоит около 8% ёмкости и 7.6% скорости префилла |
12. Выводы и допущения
Гибридная архитектура даёт два измеренных преимущества. Токен кеша дешевле в 4 раза, отсюда ёмкость в 3.2 раза больше при меньшем куске видеопамяти. Префилл площе: от 2 до 16.3 тысячи токенов стоимость тысячи токенов растёт на 12% против 56%, и отношение квадратичных коэффициентов совпало с отношением числа слоёв с полной аттеншн, то есть экономия логично вписывается в архитектурные различия.
Платится за это гранулярностью совпадения. Блок 528 вместо 16 — это 84% попаданий против 94% и TTFT хуже в 2 раза, пока рабочий набор влезает в ёмкость обычной модели. Величину этой платы можно посчитать за пять минут по config.json и логу старта, до всякого прогона.
Три вывода, которые я для себя сделал из этого камерного ресерча.
Первый: Стоимость крупного блока проявляется в основном на коротких сессиях, а на длинных она постепенно нивелируется. Потеря на границе блока — константа в токенах, 264 в среднем. От промпта в 2 900 токенов это 9%, от 16 тысяч — 1.6%, от ста тысяч — 0.3%. При этом преимущество гибрида на префилле с длиной контекста растёт. Оба эффекта тянут в одну сторону: на длинном контексте недостатки гибрида тают, а преимущество растёт. С одной поправкой, которую стоит держать рядом: в процентах попаданий плата тает, а в миллисекундах нет. Пересчитывается постоянное число токенов, а тысяча токенов префилла на длинном контексте стоит дороже, чем на коротком; на моей нагрузке лишние 264 токена — это около 35 мс, и с ростом контекста абсолютная цифра растёт, а не падает. Мои замеры преимущественно на сессиях по 3.3 тысячи токенов, то есть в самой невыгодной для гибрида точке. Проверять это надо дополнительным прогоном на сессиях по 30–100 тысяч токенов. Если арифметика подтвердится, минус превращается в оговорку «на коротких диалогах».
Второй: крупный блок — условие, при котором кеш гибрида влезает в память. Выравнивание страниц объясняет, откуда взялось 528, но даже если бы это ограничение сняли, полноценный режим снимков при блоке 16 стоил бы в 22 раза дороже обычной модели по памяти — расчёт в разделе 4.2. То есть просьба «сделайте гибриду блок 16» означает просьбу отдать всю ёмкость, ради которой гибрид и берут. Отсюда вопрос, на который у меня ответа нет: существует ли схема, где снимки состояний хранятся реже, чем на каждой границе блока, и совпадение при этом всё равно засчитывается точно? Если да, платить за крупный блок больше не придётся.
Третий: hit rate сам по себе ничего не говорит об архитектуре. В первом замере у гибрида получилось 77% попаданий против 52%, из чего следовал вывод «гибрид лучше кешируется» — прямо противоположный правильному. Там смешались ёмкость в 3 раза и гранулярность в 33. Если в сравнении попаданий между архитектурами рядом нет фактического размера блока и ёмкости из лога старта, оно не показывает ничего.
Автор: YUNGC0DE


