Гибридные трансформеры под нагрузкой: где Gated DeltaNet проигрывает full attention. fp8.. fp8. Gated DeltaNet.. fp8. Gated DeltaNet. llm.. fp8. Gated DeltaNet. llm. qwen3.. fp8. Gated DeltaNet. llm. qwen3. ttft.. fp8. Gated DeltaNet. llm. qwen3. ttft. vllm.. fp8. Gated DeltaNet. llm. qwen3. ttft. vllm. гибридные модели.. fp8. Gated DeltaNet. llm. qwen3. ttft. vllm. гибридные модели. искусственный интеллект.. fp8. Gated DeltaNet. llm. qwen3. ttft. vllm. гибридные модели. искусственный интеллект. Машинное обучение.. fp8. Gated DeltaNet. llm. qwen3. ttft. vllm. гибридные модели. искусственный интеллект. Машинное обучение. префиксное кеширование.

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

Я всё-таки проверил, потому что интересовало как эта архитектура будет вести себя под нагрузкой и вписываться в существующий стек инференса. Снял на вечер машину с двумя 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

Qwen3ForCausalLM

Qwen3_5ForConditionalGeneration

Время старта

~30 с

127 с, включая 33 с компиляции

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

3. Full Attention vs Gated DeltaNet: как две архитектуры помнят прошлое

Двадцать четыре слоя из тридцати двух у гибрида не хранят KV. Без механизма все дальнейшие числа выглядят произвольными, поэтому сначала он.

Модель с полной аттеншн помнит контекст списком. На каждый токен, в каждом слое, в каждой KV-голове она держит пару векторов, и список растёт линейно с длиной контекста. Префиксное кеширование поверх такого устройства — это шаринг кусков списка: совпало начало промпта, значит совпали куски списка, второй запрос берёт готовые.

Рекуррентный слой помнит контекст состоянием. Вместо растущего списка он держит один тензор фиксированного размера — состояние. На каждом новом токене слой частично затирает то, что в состоянии уже лежит, и дописывает туда поправку от нового токена. Первое — управляемое забывание, «гейт». Второе — правило дельты, корректирующая запись вместо слепого накопления. Отсюда и название архитектуры, Gated DeltaNet.

Полная аттеншн ведёт блокнот, в который только дописывают. Рекуррентный слой пишет на доске и каждый раз частично её стирает.

Размер состояния не зависит от длины контекста: на Qwen3.5-4B это 2.05 МБ на слой и при 500 токенах, и при 30 тысячах. Прошлое в нём сжато с потерями — восстановить конкретный токен, который был двадцать тысяч шагов назад, из сводки нельзя.

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

В vLLM за снимки состояний отвечает параметр mamba_cache_mode с тремя значениями:

Режим

Что хранится

Годится для префиксного кеша

none

одно состояние на активный запрос

нет, переиспользовать нечего

align

состояние на границах блоков и на шаге планировщика

частично

all

полные снимки на каждой границе блока

да

На моей модели в 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.

Ёмкость кеша трёх конфигураций против рабочего набора нагрузки. Пунктир — рабочий набор при 40 тенантах, 132 тысячи токенов. Столбец ниже пунктира означает, что набор не влезает и начинается вытеснение: у Qwen3-4B при ёмкости 97.7 тысячи это даёт 52% попаданий. Крайний правый столбец — гибрид на настройках по умолчанию: самая большая ёмкость из трёх и ноль попаданий, потому что префиксное кеширование у него выключено

Ёмкость кеша трёх конфигураций против рабочего набора нагрузки. Пунктир — рабочий набор при 40 тенантах, 132 тысячи токенов. Столбец ниже пунктира означает, что набор не влезает и начинается вытеснение: у Qwen3-4B при ёмкости 97.7 тысячи это даёт 52% попаданий. Крайний правый столбец — гибрид на настройках по умолчанию: самая большая ёмкость из трёх и ноль попаданий, потому что префиксное кеширование у него выключено

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%

Крупный блок бьёт по коротким сессиям и почти не виден на длинных.

Попадания в кеш против числа тенантов при 6 req/s. Синяя линия (блок 16 токенов) идёт выше оранжевой (блок 528), пока рабочий набор влезает в 93 тысячи токенов ёмкости, и обрушивается сразу за вертикальным пунктиром, где кеш кончился. Оранжевая держится до своего пунктира на 83 тенантах. Пустые маркеры — точки, где система в перегрузе

Попадания в кеш против числа тенантов при 6 req/s. Синяя линия (блок 16 токенов) идёт выше оранжевой (блок 528), пока рабочий набор влезает в 93 тысячи токенов ёмкости, и обрушивается сразу за вертикальным пунктиром, где кеш кончился. Оранжевая держится до своего пунктира на 83 тенантах. Пустые маркеры — точки, где система в перегрузе

Обе границы обвала я предсказал до прогона, поделив ёмкость из лога старта на длину контекста тенанта: 93.4 тысячи на 3.3 даёт 28 тенантов, 274.2 на 3.3 даёт 83. Замер охватывает обе — обвалы наблюдаются между 20 и 40 и между 40 и 80 тенантами.

TTFT p50 против числа тенантов, логарифмическая шкала. Обе линии держатся на десятках миллисекунд, пока рабочий набор влезает в ёмкость, и уходят вверх на два порядка, как только кеш кончился: у обычной модели между 20 и 40 тенантами, у гибрида между 40 и 80

TTFT p50 против числа тенантов, логарифмическая шкала. Обе линии держатся на десятках миллисекунд, пока рабочий набор влезает в ёмкость, и уходят вверх на два порядка, как только кеш кончился: у обычной модели между 20 и 40 тенантами, у гибрида между 40 и 80

Собираю карту применимости.

Рабочий набор

Кто выигрывает

Почему

до ~90k токенов

полная аттеншн

мелкий блок: 94% попаданий против 84%, TTFT ниже в 2 раза

~90k … 270k

гибрид

у полной аттеншн кеш кончился, у гибрида ещё нет

больше ~270k

никто

не влезает ни у кого: 26.4% против 24.9%

Одна оговорка про задержку. Разрыв 58–61 мс против 99–109 не весь про кеш: у рекуррентных слоёв есть своя постоянная стоимость, и на коротких промптах она не окупается. Разбор в разделе 6.

TTFT p50 против запрошенной нагрузки, логарифмическая шкала. Пустые маркеры — точки, где обслужено меньше запрошенного, то есть очередь растёт и абсолютную задержку сравнивать нельзя. Единственная нагрузка, где все четыре конфигурации в стационаре, — 2 req/s

TTFT p50 против запрошенной нагрузки, логарифмическая шкала. Пустые маркеры — точки, где обслужено меньше запрошенного, то есть очередь растёт и абсолютную задержку сравнивать нельзя. Единственная нагрузка, где все четыре конфигурации в стационаре, — 2 req/s

График выше объясняет, почему я гоняю кривую по нагрузке, а не одну точку. При 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% означает, что экономия на префилле объясняется заменой слоёв и ничем больше.

Стоимость тысячи токенов префилла против длины промпта. Чем ровнее линия, тем ближе работа к линейной по длине контекста. У Qwen3-4B от минимума на 2k к 16k рост 56%, у гибрида 12%. На коротких промптах гибрид медленнее

Стоимость тысячи токенов префилла против длины промпта. Чем ровнее линия, тем ближе работа к линейной по длине контекста. У Qwen3-4B от минимума на 2k к 16k рост 56%, у гибрида 12%. На коротких промптах гибрид медленнее

На графике видна и обратная сторона. При 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

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

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

При контексте 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 раза.

Обслуженные запросы в секунду на четырёх конфигурациях при одинаковой нагрузке, 40 тенантов и запрошенные 8 req/s. Две заштрихованные полосы — конфигурации с нулевым hit rate, где кеш не работает вовсе. Гибрид на настройках по умолчанию (2.74 req/s) стоит рядом с обычной моделью, у которой кеш выключен руками (2.13), и в три раза ниже себя же с включённым кешем (7.58)

Обслуженные запросы в секунду на четырёх конфигурациях при одинаковой нагрузке, 40 тенантов и запрошенные 8 req/s. Две заштрихованные полосы — конфигурации с нулевым hit rate, где кеш не работает вовсе. Гибрид на настройках по умолчанию (2.74 req/s) стоит рядом с обычной моделью, у которой кеш выключен руками (2.13), и в три раза ниже себя же с включённым кешем (7.58)

Если коротко: одному пользователю с длинным контекстом гибрид отдаёт ответ быстрее, а сервер на нём обслуживает столько же или чуть меньше запросов.

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 KV на обеих архитектурах. Слева ёмкость кеша: у полной аттеншн она растёт в 1.91 раза, у гибрида в 1.57. Справа попадания и размер блока: у Qwen3-4B попадания идут с 54.5% до 94.4%, у гибрида падают с 82.2% до 77.8%

fp8 KV на обеих архитектурах. Слева ёмкость кеша: у полной аттеншн она растёт в 1.91 раза, у гибрида в 1.57. Справа попадания и размер блока: у Qwen3-4B попадания идут с 54.5% до 94.4%, у гибрида падают с 82.2% до 77.8%

Если коротко: на полной аттеншн 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

Источник