Аннотация. В сентябре 2026 года на Hugging Face есть три модели, которые хочется поставить на пару DGX Spark под Claude Code: DeepSeek-V4.1-Flash на 552B, GLM-5.3-Flash на 320B и Qwen3.8-Flash-Next на 125B. Первая лучше всех в бенчмарках, но не помещается в 243 ГиБ даже в 4 битах. Вторая занимает 198 ГБ и грузится 15 минут. Третья влезает в один спарк — если не считать 2 ГБ, которых ей не хватает. Ниже — почему на этом кластере выбор делает арифметика памяти, а не таблица бенчмарков, почему 6 миллиардов активных параметров не дают 90 ток/с, и почему для Claude Code решающим оказывается не tok/s, а один флаг vLLM, который в самом быстром конфиге выключен.
Полтора месяца назад я развернул deepseek-ai/DeepSeek-V4-Flash-0731 тензорным параллелизмом на 2 узла. Замеренная скорость оказалась в районе 57 ток/с, и на какое-то время это был лучший вариант из всех возможных. Потом за 3 недели вышли 3 новые модели, каждая из которых в таблицах выглядит лучше того, что у меня стоит, и вопрос «что ставить» встал заново. Метод с прошлого раза остался тот же: считать до скачивания, а не после.
Сначала — про сам стенд, потому что вся арифметика дальше отсчитывается от его чисел.
Стенд: два спарка с объединённой памятью
Внутри каждого спарка — суперчип NVIDIA GB10 Grace Blackwell: 20 ядер Arm v9.2-A (десять Cortex-X925 плюс десять Cortex-A725), блэкуэлловский GPU с compute capability 12.1 и теплопакет около 140 Вт.
Главное свойство этого железа — не терафлопсы, а объединённая память. 128 ГБ LPDDR5x-9400 на спарк — это одновременно системная память и видеопамять, отдельной VRAM нет вообще. Поэтому здесь не работает привычный конфиг вида «88 ГБ держим в видеопамяти, остальное выгружаем в хост»: выгружать некуда, это тот же самый пул, и такая «выгрузка» просто отнимает память у самой же модели. Пользователю из 128 ГБ достаётся 121,6 ГиБ, на пару — 243 ГиБ, и дальше в статье всё считается от этого числа.
Узлы соединены напрямую, без коммутатора: ConnectX-7, кабель QSFP56 из порта в порт. Мои замеры на этом соединении, снятые в июле: около 17 Гбит/с одним TCP-потоком, около 107 Гбит/с 8 параллельными и около 19 ГБ/с на NCCL поверх RoCE, когда задействованы оба PCIe-интерфейса карты: ConnectX-7 подключена к системе двумя отдельными интерфейсами PCIe Gen5 x4 и видна как два адаптера, а через один из них полоса упирается в 13,9 ГБ/с. Обычный гигабитный Ethernet между ними тоже есть — 943 Мбит/с по iperf3 — и нужен он только для управления. Веса больших моделей лежат на самих узлах.
Модель подключена к Claude Code через free-claude-code — шлюз, который принимает запросы в формате Anthropic API и переводит их в локальный vLLM. Claude Code обращается к шлюзу, и вместо всех моделей Claude — Opus, Sonnet, Haiku, Fable — подключена одна локальная. Пока я сравнивал кандидатов, выяснилось, что этот шлюз стоял выключенным несколько дней, а я не заметил: Claude Code всё это время работал через облако.
Числа по текущему стеку, на которые я дальше опираюсь: веса DeepSeek-V4-Flash занимают 155 ГиБ и поделены поровну между двумя узлами, API-сервер и планировщик vLLM работают на первом узле; модель даёт 57,54 ± 5,61 ток/с в один поток (медиана по прогонам — разброс между запусками на этом кластере около 20%, поэтому число и подаётся с сигмой, а не как точное), префилл 1600–2000 ток/с, KV-пул около 699 тысяч токенов при окне 262 144 и холодную загрузку около 5 минут.
Калькулятор важнее бенчмарка
10 сентября вышла DeepSeek-V4.1-Flash. По информации разработчика она обгоняет мой текущий стек по всем строкам, и первым рефлексом было качать. Вторым — открыть config.json.
У модели 552 миллиарда параметров в трансформере и ещё 196 миллиардов в Engram — таблице n-грамм, которая читается на каждом токене. В 4 битах один только трансформер весит около 276 ГБ. У пары DGX Spark вместе 2 × 121,6 ГиБ = 243 ГиБ, и это без KV-кэша. Модель не помещается ни в каком квантовании, при котором её ещё имеет смысл называть моделью: ей нужно 3 или 4 спарка, а у меня два (если у вас кластер из 4 спарков — такие конфигурации мне в сети попадались).
На этом месте обычно возникает мысль «а NVFP4?». Она мне тоже пришла, и она неверная: эксперты у DeepSeek изначально хранятся в MXFP4, 4 бита на вес. NVFP4 — это другая схема масштабирования при тех же 4 битах, а не уменьшение вдвое.
Итого лучшая модель месяца выбывает за 5 минут и без единого скачанного байта. Продолжаем с другими кандидатами.
Почему на двух Spark решает память, а не скорость соединения
Когда задействовано больше одного узла, то возникает опасение, что узким местом станет межузловое соединение, по которому на каждом токене идёт коллективная операция all-reduce. Но если разобраться, то при тензорном параллелизме MoE-модели на каждом токене через соединение ходит вектор скрытого состояния, это единицы мегабайт, а интерфейс пропускает 18–19 ГБ/с. Соединение нагружено на доли процента.
По факту всё упирается в память.
Во-первых, объём. Тензорный параллелизм — это способ запустить одну модель на нескольких GPU, при котором каждая матрица весов режется на части, и каждый узел хранит и считает только свою часть; результаты частичных вычислений затем объединяются между узлами той самой операцией all-reduce. Поэтому всё, что в весах, должно поместиться в 243 ГиБ, и делится оно пополам между узлами. Для 198 ГБ (≈184 ГиБ) GLM это 92 ГиБ весов на узел из 121,6 доступных. При gpu-memory-utilization 0.85 под KV-кэш и активации остаётся порядка 11 ГиБ на узел — вот почему в конфиге размер KV-кэша зафиксирован флагом --kv-cache-memory 6 GiB, а не оставлен автоматическому выбору.
Во-вторых, пропускная способность памяти. Токен при декоде — это чтение всех активных весов из памяти. У GB10 паспортная полоса 273 ГБ/с (цифра вендора, я её не мерил). У GLM активных 18B, в 4 битах это около 9 ГБ на токен; поделено между двумя узлами — 4,5 ГБ на каждом. Делим: потолок около 60 ток/с в один поток без спекуляции. Это грубая оценка, но именно она, а не бенчмарки, говорит, что быстрее 60 ток/с на этой модели не будет, и любые «100 ток/с» из чьих-то карточек надо читать как результат спекулятивного декодирования, а не самой модели.
Той же арифметикой Qwen3.8-Flash-Next с 6B активных параметров должна давать скорость в 3 раза выше. Однако это не так, и об этом вторая половина статьи.
GLM-5.3-Flash, 198 ГБ и 15 минут загрузки
GLM-5.3-Flash вышла 26 августа: 320B параметров, 18B активных, MIT, поддержка изображений, миллион контекста нативно. У z.ai есть таблица, где она в Claude Code показывает 29,0 на Code Bench против 29,5 у Opus 4.8. Цифра из блога разработчика, но одна деталь в ней важнее самой цифры: бенчмарк запускали внутри Claude Code 2.1.207, то есть именно в том сценарии, ради которого мне нужен кластер.
На HF под неё есть две NVFP4-версии, и первая же ловушка — выбрать не ту. ModelOpt-версия от NVIDIA портит токены вызова инструментов: вместо аргумента приходит повреждённая строка, и Claude Code молча теряет tool_call (vLLM #54150). Рабочая версия — RedHatAI/GLM-5.3-Flash-NVFP4 в формате compressed-tensors W4A4, 10 шардов по 20 ГБ плюс 7,6 ГБ MTP-головы, итого около 198 ГБ.
Конфиг на пару DGX Spark у tonyd2wild собран целиком: свой образ vLLM под sm121, черновая модель DFlash2 на 2,3 ГБ для спекулятивного декодирования с k = 7, патч индексатора разреженного внимания, который на SM121 иначе завершается ошибкой в top-k, --block-size 2304, KV-кэш в fp8, --enforce-eager. По его README в один поток на задачах с кодом — 46,9 ток/с, при 5 параллельных запросах — 56 в сумме, KV-пул 581–678 тысяч токенов, холодная загрузка около 15 минут. Против моего расчётного потолка в 60 это 78% — со спекуляцией, что правдоподобно.
Пара важных строчек из README:
- Swap должен быть включён, со
swappiness=0, иначе загрузка 198 ГБ через оперативную память не выдерживает пикового потребления. У меня swap выключен на обоих узлах, и это не случайность. С выключенным swap попытка выйти за 128 ГБ сразу кончается явной ошибкой CUDA OOM. С включённым — машина уходит в подкачку на часы: формально она жива, но работать не может. - Перед стартом —
echo 3 > drop_caches, иначе page cache от предыдущей модели занимает память, которая нужна загрузчику. Ритуал, который выглядит как суеверие, пока не увидишь OOM на 190-м гигабайте.
Qwen3.8-Flash-Next, 133 ГБ и спарк, в который он влезает не целиком
Qwen3.8-Flash-Next — превью четвёртого поколения Qwen: 125B параметров, 6B активных, плюс 51B в таблице n-грамм PLE и 4B в MTP-голове; 262K контекста нативно, миллион через YaRN, поддержка изображений. Квантованная версия nvidia/Qwen3.8-Flash-Next-NVFP4 весит около 133 ГБ, и в карточке NVIDIA написано, что по качеству NVFP4 здесь неотличим от FP8.
Первое, что бросается в глаза: 133 ГБ — это почти ровно один спарк. Почти: 121,6 ГиБ — это 130,5 ГБ, и модели не хватает 2,5 ГБ. Конфиг TP1 на одной Spark всё равно существует — за счёт того, что таблица PLE подгружается по частям и не хранится в памяти целиком, — и даёт 43,9 ток/с против 53,7 на двух. Это единственный из трёх кандидатов, который в принципе может работать на одном узле: без межузлового соединения, без двухузлового запуска, без all-reduce на каждом токене. Оба спарка у меня всё равно уйдут под модель, так что ставить я буду TP2 — но само наличие односерверного варианта уже интересно.
Второе — цифры TP2. Тот же автор на двух спарках получает 53,7 ток/с медианой в один поток, 97,9 в сумме на 6 потоках, и KV-пул 1,97 миллиона токенов. Последнее число — в 3 раза больше, чем у GLM, и это прямое следствие 6B активных параметров: сама модель лёгкая, память достаётся кэшу.
Третье — строчка в конфиге, которая для агентного сценария важнее всех чисел выше: --no-enable-prefix-caching. Prefix caching у Qwen3.8-Flash-Next в vLLM выключен принудительно, потому что с ним модель отдаёт неверные ответы (vLLM #54173).
6 миллиардов активных параметров не дают 90 ток/с
Вернёмся к оценке потолка скорости через пропускную способность памяти — той, что дала около 60 ток/с для GLM. У GLM 18B активных параметров, у Qwen — 6B. Если потолок GLM — около 60 ток/с, то у Qwen он должен быть около 180. Конфиг даёт 53,7. Это 30% от потолка против 78% у GLM, и разница слишком велика, чтобы списать её на разброс.
Объяснений у меня два, и оба выведены, не измерены.
Основные причины:
- Таблица PLE. 51 миллиард параметров, которые не считаются «активными», но читаются на каждом токене. Сколько именно байт — зависит от n-граммы и от того, как выполняется gather; в README про это ничего нет.
- Накладные расходы на запуск CUDA-ядер. При 6B активных параметров сами матричные вычисления настолько коротки, что доля запуска ядер, all-reduce и планировщика перестаёт быть пренебрежимо малой. В конфиге tonyd2wild для Qwen видно, что автор боролся именно с этим: захват CUDA-графов, который убирает стоимость запуска ядер, включён только для декода, а спекулятивное декодирование через MTP-голову настроено так, чтобы за один проход модели предсказывать 3 токена вместо 1 — то есть платить накладные расходы 1 раз на 3 токена.
Практический вывод: «активные параметры» — плохой предиктор скорости, когда их становится меньше 10 миллиардов. Между 18B и 6B активных параметров разницы в скорости почти нет: 47 против 54 ток/с. Разница есть в другом — в размере KV-пула и в том, что одна из двух моделей помещается в один спарк.
Где расчёт расходится с замером — и почему я публикую его как есть
Оценка потолка через полосу памяти — это модель с двумя допущениями:
- 273 ГБ/с — паспортная цифра, а реальная полоса под vLLM ниже, и насколько — я не мерил.
- Я считаю, что за токен читаются только активные веса; на деле читаются ещё KV-кэш, у GLM — индексатор разреженного внимания, у Qwen — PLE.
Обе поправки тянут потолок вниз, так что 78% для GLM — это, скорее всего, ближе к 90% от реального потолка, а 30% для Qwen — к 40%.
Я оставляю расчёт в таком виде, потому что он сделал свою работу: отсёк DeepSeek-V4.1 и объяснил, почему у двух моделей с трёхкратной разницей в активных параметрах одинаковая скорость. Дотягивать его до «измерения» я не буду — измерение будет в следующем посте, когда одна из двух моделей будет запущена на этой паре.
Сводная таблица того, что известно на сегодня. Курсивом — мои, обычным — чужие числа.
| DeepSeek-V4-Flash (текущая) | GLM-5.3-Flash NVFP4 | Qwen3.8-Flash-Next NVFP4 | |
|---|---|---|---|
| Параметров / активных | 284B / 13B | 320B / 18B | 125B (+51B PLE) / 6B |
| Веса на диске | 155 ГиБ | ≈198 ГБ | ≈133 ГБ |
| Помещается | два спарка | два спарка | два или один |
| В один поток, ток/с | 57,5 ± 5,6 | 46,9 (со спекуляцией) | 53,7 (TP2), 43,9 (TP1) |
| KV-пул, токенов | ≈699K при 262K | 581–678K | ≈1,97 M |
| Prefix caching | включён | включён | выключен (#54173) |
| Загрузка | ~5 мин | ≈15 мин | в README не указано |
| Ловушки | — | swap включён, drop_caches, не ModelOpt | PLE, YaRN только в text_config |
Следствие: для Claude Code выбор делает не tok/s, а prefix cache
Claude Code — это не чат. Каждый запрос агента отправляет на сервер один и тот же системный промпт с описанием ~150 инструментов, всю историю диалога и в конце — новую реплику. Для сервера это означает, что 95% промпта совпадают с предыдущим запросом, и весь смысл prefix caching в том, чтобы не считать их заново.
У меня есть свои числа на этот счёт, снятые в августе с логов шлюза на текущем стеке. Запросы, у которых в prefix cache попадало больше 80% промпта, получали первый токен за 4,3 с. Запросы с попаданием ниже 20% при том же размере промпта ждали 73,2 с. Разница в 17 раз при одинаковой модели, одном железе и одинаковой длине — и она целиком объясняется тем, считался ли префикс заново.
Теперь приложите к этому --no-enable-prefix-caching в конфиге Qwen. Модель при 1600–2000 ток/с префилла (моя цифра для DeepSeek; у Qwen с 6B активных параметров префилл должен быть быстрее, но порядок тот же) на каждом ходу агента с контекстом в 100 тысяч токенов будет считать эти 100 тысяч заново. Это минута ожидания перед каждой репликой, и никакие 53,7 ток/с декода её не компенсируют: агент генерирует ~200 токенов на ход, а читает — 100 тысяч.
Поэтому порядок кандидатов у меня получился таким, каким он не получился бы по таблице бенчмарков: первым идёт GLM-5.3-Flash, потому что он ничем не отличается от текущего стека по способу работы и уже сравнивался с Opus внутри Claude Code. Qwen3.8-Flash-Next — вторым, и у него один входной билет: если prefix caching у него включается без искажения ответов, он выигрывает по пулу в 3 раза и освобождает второй узел. Если нет — он остаётся моделью для чата, а не для агента, какой бы быстрой ни была.
Выводы
Первый фильтр для модели на двух DGX Spark — не бенчмарк, а сумма весов против 243 ГиБ. Модель, которая не проходит этот фильтр, не нужно даже читать: DeepSeek-V4.1-Flash на 552B отсекается за 5 минут.
NVFP4 не уменьшает модель, у которой эксперты уже в MXFP4. Если в карточке исходника написано «FP4», четырёхбитная производная будет того же размера.
Потолок декода считается из полосы памяти и активных параметров, и его стоит посчитать до чтения чужих тысяч tok/s: для 18B активных параметров на GB10 это около 60 в один поток, всё сверху — спекулятивное декодирование.
Ниже 10B активных параметров их число перестаёт предсказывать скорость: 6B у Qwen и 18B у GLM дают 54 и 47 ток/с. Ищите, что ещё читается на токене — PLE, индексаторы, KV.
Для агентного сценария смотрите не на tok/s, а на prefix caching: на моём шлюзе разница между попаданием и промахом — 4,3 против 73,2 с до первого токена. Конфиг, в котором кэш префикса выключен, для Claude Code не подходит, какой бы быстрой ни была модель.
Конфиг, который требует включить swap, меняет политику памяти всей машины, а не одну строчку в конфиге. У меня он выключен ради честного OOM вместо тихой подкачки, и включать его — решение, которое принимается до установки, а не после первого зависания на 190-м гигабайте.
Прежде чем выбирать модель для сервиса, проверьте, что сервис включён.
Источники: RedHatAI/GLM-5.3-Flash-NVFP4, nvidia/Qwen3.8-Flash-Next-NVFP4, конфиги tonyd2wild для GLM-5.3-Flash на двух Spark и Qwen3.8-Flash-Next, vLLM issues #54150 и #54173, блог z.ai о GLM-5.3-Flash.