Почему при использовании SGLang и DFlash2 две видеокарты RTX 3090 достигают скорости 55 ток/с вместо 220.
Практический обзор запуска Qwen3.8-27B на двух видеокартах 3090 с использованием SGLang, а также того, как интерфейсы PCIe x4, отсутствие функции P2P, ограниченная кэш-память KV и резкие скачки потребления энергии снизили заявленную скорость.
Среди поклонников самостоятельной развертки распространилось короткое видео. На нем показано, как Qwen3.8-27B генерирует код с помощью пары карт RTX 3090, причем скорость генерации составляет 220 токенов в секунду. Для работы используются SGLang вместе с DFlash2 — спекулятивным инструментом для подготовки данных. Эта небольшая модель одновременно предлагает семь будущих токенов, а большая модель принимает их или отклоняет за один проход, поэтому правильные предположения не стоят ничего.
Привлекательность этого решения выходит за рамки простой демонстрации. При такой скорости агент может использовать локально развернутую модель, запуская несколько агентов для кодирования одновременно на одном устройстве, без необходимости в API-ключах, без ограничений по тарификации и без передачи данных за пределы устройства. Домашний сервер с двумя дополнительными GPU класса 3090 кажется идеальным вариантом: удвоенная пропускная способность памяти обеспечивает скорость в 220 токенов в секунду.
На деле всё не так. Причины этого более показательны, чем само видео.
Установка SGLang
Те, кто привык к llama.cpp, где один бинарный файл и один файл модели позволяют запустить чат за десять минут, посчитают SGLang скромным по сравнению. Вот последовательность действий на сервере Ubuntu.
Шаг 1: среда Python. U SGLang имеет сложную иерархию зависимостей, поэтому после клонирования репозитория создается собственная виртуальная среда на Python 3.12.
git clone https://github.com/sgl-project/sglang.git
cd sglang
python3.12 -m venv .venv
source .venv/bin/activate
Шаг 2: проблемная установка. Первая попытка полноценной установки заняла примерно 39 минут, в течение которых формировалась кэш-библиотека с большинством пакетов из PyPI, после чего установка была прервана с ошибкой.
RuntimeError: cargo is required to discover the Rust extension modules
Компилятор на Rust для сервера инференса GPU. В новых версиях SGLang предусмотрены необязательные расширения на Rust, и инструменты сборки отклоняют попытку сборки без Cargo, хотя сам движок их никогда не использует. Установка одной переменной окружения позволяет обойти эту проблему.
SGLANG_BUILD_RUST_EXTS=none pip install -e "./python[all]"
Вторая попытка заняла ещё примерно 30 минут, после чего задача была выполнена без проблем. Результатом стало sglang 0.5.20, torch, собранный для CUDA 13, и triton, автоматически настроенный на версию 3.7.1, что как раз необходимо графикам Ampere для избежания известного бага ядра.
Шаг 3: модели. Следуя рекомендациям сообщества, необходимо скачать две модели:
cyankiwi/Qwen3.8-27B-AWQ-INT4— основная модель. Квантизация INT4 сокращает её размер до примерно 20 ГБ в 5 частей, что достаточно для двух графиков объёмом 24 ГБ.z-lab/Qwen3.8-27B-DFlash2— предварительная версия модели, размером около 3,9 ГБ.
Шаг 4: запуск. Для этого достаточно одной команды, причём параметр TP=2 означает параллелизм тензоров между обеими картами 3090.
CUDA_VISIBLE_DEVICES=1,2 python -m sglang.launch_server \
--model-path /mnt/models/Qwen3.8-27B-AWQ-INT4 \
--tp 2 \
--speculative-algorithm DFLASH \
--speculative-draft-model-path /mnt/models/Qwen3.8-27B-DFlash2 \
--speculative-num-draft-tokens 8 \
--mamba-radix-cache-strategy extra_buffer \
--disable-prefill-cuda-graph \
--cuda-graph-max-bs-decode 16 \
--mem-fraction-static 0.85 \
--context-length 65536 \
--reasoning-parser qwen3 \
--host 0.0.0.0 --port 30000
Следите за двумя ловушками. Флаг с именем --cuda-graph-max-bs мешает запуску, поскольку в новых версиях его переименовали для разделения вариантов декодирования и предзаполнения. Кроме того, отсутствие параметра --reasoning-parser qwen3 приводит к тому, что API возвращает сырые результаты мышления модели с вложенными тегами think прямо в поле обычного содержимого. Добавление этого парсера разделяет вывод на структурированные поля reasoning_content и content, что соответствует формату, используемому другими серверами, совместимыми с OpenAI, и это будет кстати тем, кто пишет клиентское программное обеспечение.
Шаг 5: ожидание завершения процесса. Время запуска при первом старте составило примерно шесть минут, пока не появилось сообщение о готовности. Ничего не сломалось: SGLang своевременно компилирует ядра GPU через Triton и записывает графы CUDA на каждой карте. Последующие запуски используют кэш и занимают примерно 35 секунд. Полоса прогресса, кажущаяся застывшей во время первого запуска, на самом деле отображает процесс компиляции.
Результаты измерений
Сервер работал. Он отвечал на запросы, транслировал reasoning_content и принимал подключения агентов. Вот цифры, полученные непосредственно на самом устройстве:
| Metric | Min | Max | Average |
|-------------------------|----------|-----------|-----------|
| Decode speed | 47 t/s | ~100 t/s | ~55 t/s |
| Prompt prefill | 342 t/s | 466 t/s | ~428 t/s |
| Accept length (DFlash) | 3.2 | 6.8 | ~4 |
А вот изображение ёмкости:
| Capability | Advertised limit | Reality |
|-------------------------|---------------------------|-----------------------|
| Max context per request | 65,536 (I set it) | fits, one at a time |
| Total KV pool | ~131K tokens max | 103K at default flags |
| Parallel slots | 48 concurrent requests | 3–6 with real prompts |
В среднем это примерно 55 токенов в секунду, а не 220. Проблема в том, что одна карта RTX 3090 Ti с использованием llama.cpp достигает скорости около 75 токенов в секунду при обработке той же семейства моделей. Две карты с тензорным параллелизмом и сложным инструментом формирования пакетов уступают одной карте, использующей более старый подход.
Уровень принятия данных объясняет часть этих колебаний. Когда алгоритм DFlash правильно предсказывает результаты, принимается 6,8 из 8 токенов, и скорость временно достигает примерно 100 токенов в секунду. Если предсказания ошибочны, скорость падает до 47 токенов в секунду. Даже лучшие пакеты данных значительно уступали показателю 220, поэтому проблема не заключалась в самом инструменте формирования пакетов. Скорость снижалось из-за чего-то ещё.
Улика в журнале логов
Подсказка всё это время находилась в журнале запуска:
Setup Custom allreduce failed with CUDART error: peer access is not
supported between these two devices.
При параллелизме тензоров каждая матрица весов деляется между двумя GPU, а частичные результаты необходимо объединять после каждого блока внимания и каждого блока MLP. В модели объемом 27 млрд параметров это приводит примерно к 100 итерациям передачи данных на каждый сгенерированный токен. Для такой скорости объединения требуется быстрая связь между картами, но у этих двух карт ее не было. Два отдельных сбоя усугубляли друг друга.
Отсутствие функции peer-to-peer. Карты GeForce больше не официально поддерживают технологию GPU-direct P2P, поэтому каждая передача данных проходит через оперативную память системы: с GPU 1 в память, затем на GPU 2 и обратно. Это удваивает количество копий данных и утраивает задержку.
Узкая связь — настоящая причина проблем. Проверка состояния физической связи объясняет ситуацию:
/sys/bus/pci/devices/05:00.0/current_link_width → 4 (max 16)
/sys/bus/pci/devices/09:00.0/current_link_width → 4 (max 16)
Обе ГПУ использовали протокол PCIe x4, а не x16. Четыре канала связи на каждую карту, проходящие через мост хоста, обеспечивают скорость передачи данных всего 1–2 ГБ/с между картами, однако объем данных, передаваемых по этой связи, превышает этот показатель примерно в 100 раз за каждую операцию. ГПУ простаивали в ожидании копий данных. Это объясняет медленную скорость предварительной загрузки — 428 т/с вместо ожидаемых 1200 и более, а также замедление процесса декодирования до уровня около 55.
ГПУ работают быстро. Однако кабель, соединяющий их, медлен. При тензорном параллелизме этот кабель фактически становится частью самой модели.
Как кому-то удается достичь 220
Никто не показывает свою материнскую плату в демонстрации. Хорошо подключенная пара слотов x16 с функционирующим режимом P2P обеспечивает пропускную способность между картами примерно в десять раз выше, чем у данной конфигурации. Указанное число, скорее всего, относится к кратковременной работе с предсказуемым кодом, при этом кэши были разогреты, а условия тестирования идеальны, а не к постоянной скорости работы. Программная часть решения работает надежно, но она косвенно предполагает наличие оборудования, которое имеется лишь у немногих домашних компьютеров.
Еще два вывода для повседневного использования
Комната для обработки контекста меньше, чем ожидалось. Два картриджа по 24 ГБ кажутся достаточными. Однако необходимо вычесть веса INT4 (примерно по 10 ГБ на каждый картридж), данные модели, графы CUDA, а также память для рекуррентного состояния из-за использования гибридной системы внимания в Qwen3.8. В итоге для кэша KV оставалось примерно 103 тысячи токенов в целом, или 131 тысяча при более агрессивных настройках. Одна заявка на обработку 65 тысяч токенов вполне укладывается. Заявленные 48 одновременных сессий реализовать невозможно, поскольку каждый активный диалог также занимает часть памяти модели mamba, и на практике удается поддерживать лишь от трех до шести сессий. В отличие от этого, llama.cpp передаёт почти всю VRAM одной модели и без проблем справляется с большими объёмами контекста.
Перезагрузка из-за проблем с питанием. Когда обе карты 3090 работают одновременно, выполняя задачи предзаполнения и формирования данных при полной нагрузке на тензорное параллелирование, потребление энергии резко возрастает с уровня покоя до примерно 700 ватт в течение нескольких миллисекунд. Блоки питания плохо справляются с такими нагрузками. Один из таких всплесков привел к активации защиты и отключению сервера на середине работы. Он перезагрузился самостоятельно, но урок остается прежним: для такой нагрузки необходим качественный блок питания с достаточным запасом мощности на переходные процессы, и указанная на упаковке мощность недостаточна.
Стоит ли пробовать это дома?
SGLang плюс DFlash2 — это впечатляющее техническое решение, если выполняются их предпосылки, а эти предпосылки сводятся к списку требований к оборудованию:
- Обе ГПУ в настоящих слотах x16. Блоки для игр для потребителей обычно имеют одну ГПУ, возможно, с второй, подключенной через чипсет по интерфейсу x4. Прочитайте руководство перед покупкой, поскольку именно это определяет разницу между производительностью 220 и 55.
- Функциональная связь между ГПУ. Это означает наличие функций Resizable BAR и настроек ACS в BIOS, а также форков драйверов от сообщества для GeForce или технологии NVLink, используемых вне сферы потребительских устройств. На практике это прерогатива серверных материнских плат.
- Качественный блок питания с достаточным запасом мощности. Пиковые нагрузки от двух карт мощностью 350 Вт в конечном итоге приведут к сбоям у слабого блока питания.
- Четыре карты RTX 3090 для работы с моделью Qwen3.8-27B в полном объеме. Благодаря такому бюджету кэш KV перестает быть ограничением, в отличие от ситуации с ограниченным объемом кэша в 103 К.
Подобное программное обеспечение не прощает дешевого оборудования. Оно работает медленнее в тех аспектах, на которые никто не обращает внимания.
Несмотря на отсутствие как программного, так и аппаратного обеспечения, которое он предполагает, llama.cpp по-прежнему остается разумным выбором. Это означает одну карту, один бинарный файл, скорость обработки около 75 токенов в секунду по стандартному PCIe, отсутствие инструментов Rust и ненужных перезагрузок. Пара GPU может снова работать независимо со скоростью x4.
Цифра 220 была реальной. Она просто не применима к этой системе, а, скорее всего, и к многим другим до тех пор, пока не будет проверена связь между картами.