Скорость 109 токенов/с на RTX 5080 и RTX 3060 с 35B-моделью через PCIe Gen2 x4
«Как архитектура MoE и послойное распределение сводят на нет тезис „одна дискретная карта эффективнее двух“.»
В сообществе SE7ENа этой проблематике посвящена отдельная публикация: «Собираем сервер под свой локальный ИИ: сколько нужно VRAM и почему одна видеокарта лучше двух». Там прямо утверждается: «32-гигабайтная одиночная видеокарта предпочтительнее двух по 16 ГБ», а ключевым узким горлышком выступает пропускная способность шины. Второй разъём на потребительских материнских платах функционирует в урезанном режиме x4 или x8, из-за чего межкартаня коммуникация упирается в аппаратные ограничения. Аналогичные холивары возникают вокруг сравнений вроде «две RTX 5060 Ti 16GB против одной RTX 3090 24GB», а также в ветках про мультиграфен-сборки. Впрочем, в комментариях тут же парируют: 24 ГБ вполне успешно вывозят верифицированные 27B- и 35B-модели с экспертной маршрутизацией (MoE) при контекстном окне в 256K.
Мой сетап полностью отражает опасения той статьи: на системной плате я сэкономил, установив ASUS Prime B450-Plus, где дополнительный слот x16 физически разведён как PCIe 2.0 x4. Это худший сценарий из возможных, а не компромиссное «чуть медленнее, чем x8».
Инференс модели Qwen3.6–35B‑A3B у меня функционирует на связке RTX 5080 и RTX 3060. При этом тридцатитысячная серия висит на шине PCIe Gen2 x4. Это обеспечивает пропускную способность всего в 2 ГБ/с, что почти в восемь раз медленнее возможностей 5080. Скорость генерации текста держится на уровне 109 токенов в секунду для коротких промптов и 77 токенов для контекста в 53 тысячи единиц.
Ниже я подробно разберу физику процессов и объясню, в каких кейсах скепсис относительно PCIe всё-таки обоснован.
Аппаратный стенд
-
ЦП: Ryzen 7 5800X (8 ядер / 16 потоков)
-
ОЗУ: 64 ГБ DDR4
-
Материнская плата: ASUS Prime B450-Plus (дополнительный x16 слот функционирует как PCIe 2.0 x4)
-
GPU 0: RTX 3060 12 ГБ (GDDR6, шина 192-bit, 15 Гбит/с → 360 ГБ/с)
-
GPU 1: RTX 5080 16 ГБ (GDDR7, шина 256-bit, 30 Гбит/с → 960 ГБ/с)
-
Интерфейс NVLink: отсутствует
-
ОС: Arch Linux, кастомное ядро 7.1.11 (i use arch btw)
-
Видеодрайвер: 610.57.04, CUDA 13.3
-
Билд llama.cpp: 10 738
Совокупный объем видеопамяти составляет 28 ГБ. Суммировать пропускную способность подсистемы памяти бессмысленно: ускорители задействованы последовательно, а не параллельно. К этому тезису мы еще вернемся.
Особенности конфигурации PCIe заслуживают отдельного упоминания, поскольку это наиболее уязвимый узел системы:
GPU 0 (RTX 3060): Gen2 x4 ≈ 2 ГБ/с (потенциал карты: Gen4 x16)
GPU 1 (RTX 5080): Gen3 x16 ≈ 16 ГБ/с (потенциал карты: Gen5 x16)
Старенькая 3060 задействует второй слот, который, согласно официальной спецификации B450-Plus, реализован как PCIe 2.0 x4 — разработчики заложили лишь четыре линии вместо шестнадцати намеренно. Главный слот функционирует в стандарте PCIe 3.0 x16, поэтому передовая 5080 со своей поддержкой Gen5 также упирается в системные ограничения платформы B450. Оба графических адаптера делят один хост-контроллер PCIe. Никакого моста NVLink и симметричного режима x16/x16 здесь нет и в помине.
Почему интерфейс PCIe в данном случае не является узким местом
Традиционные контраргументы против использования мульти-GPU сетапов базируются на концепции тензорного параллелизма. В этой парадигме каждый ускоритель обсчитывает собственный фрагмент любого слоя сети, требуя синхронизации через операцию all-reduce по завершении каждого шага. Увеличение длины контекста экспоненциально нагружает шину данными, и вот тут низкая пропускная способность вроде Gen2 x4 способна напрочь убить производительность.
В моем сценарии активирован послойный сплит (layer-wise splitting, штатный режим для llama.cpp). Слои нейросети просто распределены между устройствами: первое держит начальные пласты, второе — завершающие. По шине курсируют вовсе не массивные веса, а исключительно промежуточные активации — ровно один раз за генерацию одного токена.
Произведем расчеты. Скрытая размерность выбранной нейросети составляет 2048. Вектор активации в формате fp16 требует 2048 × 2 байта, что эквивалентно 4 КБ. Один пересыл между картами за токен.
При скорости генерации в 109 токенов в секунду итоговый трафик составит скромные 437 КБ/с. Пропускная способность линии Gen2 x4 равна 2 ГБ/с. Таким образом, нагрузка на шину исчисляется сотыми долями процента.
Даже на этапе префилла (предварительной обработки промпта) ситуация кардинально не меняется. Пакет из 2048 токенов занимает около 8 МБ и равномерно распределяется во время инференса. На скорости префилла в 1500 токенов/с мы получаем около 6 МБ/с.
Именно поэтому урезанная шина Gen2 x4 не создает фризов: она исключена из критического пути выполнения. Веса надежно закреплены за локальной памятью карт, а по межсистемному интерфейсу передаются лишь легковесные тензоры активаций.
Почему архитектура MoE меняет правила игры
Будь Qwen3.6–35B‑A3B плотной (dense) нейросетью, картина выглядела бы совершенно иначе. Размер весов в квантованном файле достигает 21,3 ГиБ, и классическая модель вычитывала бы весь этот массив данных для генерации абсолютно каждого токена. На 5080 с ее пропускной способностью 960 ГБ/с это дало бы потолок примерно в 42 токена в секунду, причем в одиночном режиме. С послойным разделением устройства функционируют последовательно, поэтому полосы пропускания не суммируются, а вот задержки — складываются.
Однако рассматриваемая модель построена на базе Mixture of Experts (MoE). Из общих 35 миллиардов параметров на обработку каждого отдельного токена привлекается лишь около 3 миллиардов: 8 маршрутизируемых экспертов из 256 возможных плюс один базовый общий. Оставшиеся 32 миллиарда параметров пассивно покоятся в видеопамяти и не требуют постоянного чтения.
Отсюда проистекает итоговый перформанс. По требованиям к объему VRAM это тяжелая 35-миллиардная модель, но по вычислительным затратам на генерацию одного токена она сопоставима с компактными 3B-моделями. Именно это технологическое несоответствие позволяет комфортно запускать ее на гетерогенном парке потребительских видеокарт.
Архитектурные особенности согласно карточке репозитория на HuggingFace:
-
40 слоев архитектуры, скрытое измерение скрытого пространства — 2048
-
256 экспертных модулей, из которых 8 активируются динамически + 1 глобальный на токен
-
Комбинирование слоев Gated DeltaNet (линейный механизм внимания) и классического Gated Attention
-
Нативная поддержка контекстного окна до 262 144 токенов
-
Дополнительная MTP-голова (Multi-Token Prediction) для спекулятивного декодирования
Несколько слов о механизме внимания. Полноценный механизм внимания задействован лишь в каждом четвёртом слое, в то время как остальные опираются на Gated DeltaNet с фиксированным состоянием памяти. Благодаря этому KV-кэш раздувается крайне медленно, а скорость генерации практически не зависит от текущего объема контекста. Ниже это наглядно демонстрируют бенчмарки.
Практические замеры
Все тесты производились через эндпоинт /completion с флагом cache_prompt: false, что исключает кэширование и заставляет сервер каждый раз заново обрабатывать входной промпт. Предустановки сервера и веса не менялись, перезагрузки не выполнялись, бенчмарки запускались циклично. Никакой искусственной магии — исключительно «живой» прогон серверного окружения.
Базовая скорость генерации
Короткий запрос, генерация 256 токенов, серия из пяти непрерывных запусков:
|
Номер прогона |
Генерация, токен/с |
|---|---|
|
1 |
109,2 |
|
2 |
109,7 |
|
3 |
106,0 |
|
4 |
109,6 |
|
5 |
108,4 |
Диапазон отклонений укладывается в 3,5%, среднее значение — 108,6. Это ничтожно мало на фоне разницы между конфигурациями, так что базовую производительность можно считать эталонной.
Зависимость от доменной специфики контента
Здесь вариативность носит уже не статистический, а содержательный характер:
|
Тип задачи |
Генерация, токен/с |
Валидированные MTP-черновики |
|---|---|---|
|
Программирование (Python) |
114,6 |
85,7% |
|
Структурированный JSON |
113,3 |
85,1% |
|
Код на русском языке |
100,4 |
68,8% |
|
Англоязычная беллетристика |
96,0 |
62,9% |
|
Русскоязычная проза |
95,4 |
62,0% |
Скорость инференса линейно коррелирует с процентом принятых предсказаний спекулятивного декодирования. На строго структурированном коде MTP угадывает более восьми токенов из десяти, тогда как на художественном тексте точность падает до шести.
Эффективность спекулятивного прохода
Аналогичный замер с фиксацией метрик предсказаний (сгенерировано минус подтверждено):
|
Тип задачи |
Токенов за итерацию |
Успешные черновики |
|---|---|---|
|
Код на Python |
2,70 |
85,7% |
|
Структурированный JSON |
2,68 |
85,1% |
|
Русскоязычная проза |
2,22 |
62,0% |
При ограничении --spec-draft-n-max 2 предельный теоретический порог составляет три токена за проход. На формализованных данных модель вплотную приближается к этому максимуму.
Важная ремарка: количество токенов за итерацию не тождественно конечному ускорению в разах. Проверяющая фаза обрабатывает пачки токенов параллельно, требуя больших затрат, чем базовый шаг. Создатели Unsloth заявляют буст в 1,5–2 раза; я не стал изолировать этот фактор, поскольку для чистого A/B тестирования потребовался бы перезапуск демона без активации --spec-type draft-mtp, а конфигурация пресета оставалась неизменной.
Поведение на длинном контексте
Пожалуй, наиболее показательная таблица тестов:
|
Контекстная база, токенов |
Промпт-процессинг, токен/с |
Генерация, токен/с |
|---|---|---|
|
13 865 |
1820 |
90,8 |
|
27 703 |
1708 |
87,9 |
|
42 528 |
1586 |
80,6 |
|
53 401 |
1504 |
76,7 |
Падение скорости генерации при четырехкратном раздувании контекста (с 14K до 53K токенов) составляет всего 15%: с 90,8 до 76,7. Это заметно, но далеко от катастрофического падения. Секрет кроется в линейном внимании: емкость скрытого слоя Gated DeltaNet не разрастается пропорционально контексту, а классический механизм внимания развернут лишь в десяти слоях из сорока. При этом KV-кэш предельно компактен — всего 2 головы по 256 измерений, что дает примерно 10 КБ на один токен для всей группы слоев. Плотная языковая модель с полным вниманием во всех сорока пластах просела бы по перформансу гораздо сильнее.
Скорость обработки промпта снижается несколько динамичнее (с 1820 до 1504, то есть на 17%), упираясь в чисто вычислительные мощности ядер.
Параллельная обработка двух слотов
Параметр parallel = 2 делит максимальное окно в 131 072 токена на два независимых слота по 65 536. Запуск двух параллельных потоков запросов дает следующие цифры:
|
|
Генерация, токен/с |
|---|---|
|
Слот 1 (программирование) |
69,3 |
|
Слот 2 (беллетристика) |
60,5 |
|
Суммарная пропускная способность |
≈ 130 |
Совокупный воркпут вырос со 109 до 130 токенов в секунду, однако каждый отдельный поток просел почти вдвое. Для автономных AI-агентов, отправляющих запросы в фоне, это скорее минус: отдельные задачи ожидают завершения дольше, чем при последовательной обработке.
Конфигурационный файл
Рабочий пресет в директории ~/.config/llama.cpp/models.ini:
[unsloth/Qwen3.6-35B-A3B-MTP-GGUF-64K]
hf = unsloth/Qwen3.6-35B-A3B-MTP-GGUF:UD-Q4_K_XL
ctx-size = 131072
cache-type-k = q8_0
cache-type-v = q8_0
spec-type = draft-mtp
spec-draft-n-max = 2
temperature = 1.0
top-p = 0.95
top-k = 20
min-p = 0.0
presence-penalty = 1.5
repeat-penalty = 1.0
Инференс оркестрируется через утилиту llama-server --models-preset, автоматически разворачивающую модель в изолированном процессе. Итоговый синтаксис команды:
llama-server --host 127.0.0.1 --port <dynamic> \
--alias unsloth/Qwen3.6-35B-A3B-MTP-GGUF-64K \
--hf-repo unsloth/Qwen3.6-35B-A3B-MTP-GGUF:UD-Q4_K_XL \
--ctx-size 131072 --parallel 2 \
--cache-type-k q8_0 --cache-type-v q8_0 \
--n-gpu-layers -1 --flash-attn on --load-mode mmap \
--spec-type draft-mtp --spec-draft-n-max 2 \
--temperature 1.0 --top-p 0.95 --top-k 20 --min-p 0.0 \
--presence-penalty 1.5 --repeat-penalty 1.0
Критически важные детали конфигурации:
Отсутствие флага --split-mode. Задействован дефолтный послойный метод распределения. Именно он нивелирует узкие места убогой шины Gen2 x4.
Квантование KV-кэша до пресета q8_0. У данной архитектуры кэш памяти внимания компактен (всего 2 головы), и на коротких дистанциях профит едва заметен. Тем не менее, это высвобождает около гигабайта драгоценной VRAM, а в условиях дефицита памяти на разнородных картах каждый мегабайт определяет, сколько слоев удастся скинуть на реактивную RTX 5080. При масштабировании контекста экономия памяти становится кратной.
--load-mode mmap вместо mlock. На этом аспекте стоит остановиться подробнее, так как параметр mlock часто работает не так, как ожидается. На моей машине лимит ядра RLIMIT_MEMLOCK жестко зафиксирован на отметке 8 МБ, в то время как веса модели занимают 22,85 ГБ. Бэкенд llama.cpp честно фиксирует в логах ошибку failed to mlock ... Cannot allocate memory, а метрика VmLck в выводе /proc/<pid>/status остается нулевой. Иными словами, модель не закрепляется в оперативной памяти в обход свопа, создавая лишь ложное чувство безопасности. Стратегия mmap в данном контексте честнее: файл удерживается в страничном кэше ядра и эффективно переиспользуется при последующих стартах.
Параметры сэмплирования заимствованы из официальной карточки модели для режима рассуждений (reasoning): temperature=1.0, top_p=0.95, top_k=20, min_p=0.0, presence_penalty=1.5.
parallel = 2 при активированном модуле MTP. Команда Unsloth в описании репозитория прямо заявляла о несовместимости параметров -np > 1 и --mmproj с технологией MTP. Тем не менее, с конфигурацией parallel = 2 (что аналогично -np 2) вся связка работает стабильно: токены генерируются корректно, а все приведенные бенчмарки сняты именно с этой конфигурации. Мультимодальный флаг --mmproj в конфиге отсутствует, так что вторая часть ограничения меня не затрагивает.
Что на самом деле выступает бутылочным горлышком?
Результаты тестов полностью сняли подозрения с интерфейса PCIe. Но что именно ограничивает производительность в таком случае?
Я мониторил показатели nvidia-smi с дискретностью в одну секунду во время затяжного инференса — 2200 токенов за 24 секунды, что дало 23 валидных семпла на каждый ускоритель. В расчет принимались исключительно замеры с ненулевой нагрузкой, дабы исключить периоды простоя и не искажать картину.
|
Графический чип |
Средняя загрузка |
Диапазон |
Энергопотребление |
Диапазон ватт |
Паверлимит |
|---|---|---|---|---|---|
|
RTX 3060 |
62% |
58–66% |
110,7 Вт |
102–113 Вт |
170 Вт |
|
RTX 5080 |
15% |
14–18% |
67,6 Вт |
65–68 Вт |
360 Вт |
Вот и разгадка. Старенькая RTX 3060 загружена на 62% и утилизирует две трети своего лимита энергопотребления. В то же время флагманская RTX 5080 простаивает на 15%, потребляя менее пятой части своего потенциала. Ускорители не сбалансированы по нагрузке: быстрая карта вынуждена ждать медленную напарницу.
Уточнение по методике: усреднение метрик раз в секунду сглаживает пики на масштабе сотен токенов, поэтому по ним нельзя судить о микропроцессах внутри генерации одного токена. Однако для оценки глобальной утилизации во время прогона этой точности более чем достаточно.
Распределение памяти под модель: 10 462 МиБ на RTX 3060 и 14 986 МиБ на RTX 5080. Мощная карта забрала на себя заметно большую долю слоев, но три четверти рабочего времени она все равно ожидает соседа.
Резюме: лимитирующим фактором выступает не шина и не пропускная способность видеопамяти, а последовательная цепь зависимостей. Ускоритель 5080 физически не может начать расчеты своих слоев, пока 3060 не завершит обработку своей порции, и наоборот. Сорок слоев — это сорок передач управления, на каждой из которых устройство простаивает в ожидании партнера. Мы расплачиваемся за асимметричность вычислений высокой латентностью, а не дефицитом пропускной способности каналов.
Архитектура MoE делает выполнение каждого отдельного шага столь легковесным, что даже при столь неоптимальной схеме распределения удается выжимать 109 токенов в секунду.
Чему не было уделено внимание
Три аспекта, о которых честнее заявить прямо, чем умалчивать:
Тензорный параллелизм на этой паре карт. Теоретически он должен устранить простои, поскольку оба чипа обрабатывают слои синхронно. Однако этот метод предъявляет жесткие требования к ширине канала, а у 3060 задействован убогий разъем Gen2 x4. Будет ли профит от такого подхода на столь несбалансированной связке — вопрос открытый.
Инференс без задействования модуля MTP. Для чистоты такого замера потребовался бы повторный запуск сервера, а конфигурацию я принципиально не трогал.
Качество генерации на экстремальном контексте. Все замеры производительности снимались на фиксированном наборе промптов. Сохраняет ли модель одинаково высокое качество ответов на длине в 53K и 1K токенов, я не исследовал — в фокусе была исключительно скорость работы.
Выводы
Тезис «одиночная видеокарта эффективнее связки из двух» безупречно работает для монолитных плотных моделей и тензорного параллелизма. В подобных сценариях требования к полосе пропускания формируют непреодолимое бутылочное горлышко, и диспропорция в характеристиках графических адаптеров больно бьет по перформансу.
Для архитектур MoE в сочетании с послойным распределением это правило теряет актуальность по двум ключевым причинам:
-
Количество активных параметров кратно меньше общего объема. Задействуется лишь 3B из 35B, тогда как полновесная модель аналогичного класса считывала бы на порядок больше терабайт за один токен.
-
По межсистемному интерфейсу передаются лишь активации, а не веса. 4 КБ данных на один переход при пропускной способности шины в 2 ГБ/с — это статистическая погрешность. Урезанная линия PCIe 3060 попросту выведена за пределы критического пути.
Чего действительно стоит опасаться в подобной конфигурации, так это последовательного характера вычислений: карты работают по очереди, из-за чего задержки суммируются, а пропускная способность не складывается. Если финансовые возможности позволяют приобрести одну мощную современную карту взамен пары устаревших, плотная модель на ней запустится комфортнее. Если же у вас в корпусе уже пылятся два разных графических ускорителя, а выбор пал на MoE-архитектуру с компактным KV-кэшем — подобная гибридная связка способна выдавать 109 токенов/с на коротких запросах и 77 токенов/с при длине контекста в 53 тысячи единиц.
Это по-прежнему не промышленный серверный кластер. Но эта система тихо гудит под рабочим столом и не требует ничего, кроме свободных слотов PCIe.
Как воспроизвести сетап у себя
Базовый пресет, использованный мной в качестве отправной точки, выложен в публичный доступ: репозиторий github.com/aigen31/llama‑cpp‑preset‑base распространяется под лицензией MIT. Это минималистичный инсталлятор, разворачивающий три компонента: управляющий лаунчер llama-qwen, INI-конфиг ~/.config/llama.cpp/models.ini и проверку наличия бинарника llama-server в системной переменной PATH.
Скрипт не пытается самостоятельно компилировать из исходников llama.cpp. Если исполняемый файл отсутствует в системе, утилита выведет прямую ссылку на официальный репозиторий и справочную таблицу бэкендов под конкретное железо: CUDA, ROCm, Vulkan, SYCL, Metal или чистое исполнение на CPU. Сборка остается за пользователем, поскольку корректные флаги компиляции напрямую зависят от конфигурации вашей рабочей станции.
Нейросети описываются единственным параметром hf, содержащим полный путь к репозиторию и префикс квантования, например hf = unsloth/Qwen3.6-35B-A3B-MTP-GGUF:UD-Q4_K_XL. При первом запросе сервер автоматически выкачивает GGUF-модели с хранилища Hugging Face. API-ключ генерируется в формате sk- с добавлением 48 случайных символов, установщик не интерактивен и полностью готов к запуску через конвейер curl | bash. Документация README дублируется на русском, английском и китайском языках.
Мой персональный рабочий конфиг незначительно отличается от шаблонного репозитория: я дополнительно прописал квантование KV-кэша и принудительно заменил параметр mlock на mmap после того, как экспериментально выяснил, что системный лимит на аллокацию заблокированной памяти на моей машине жестко ограничен на отметке 8 МБ.
Первый сетевой накопитель Xiaomi сильно подешевел спустя полтора месяца после релиза
До анонса OnePlus 16 раскрыты характеристики: батарея на 9000 мА•ч, флагманские камеры и уникальная подсветка корпуса
Анонс смартфона Honor Win 2 с батареей на 12 000 мА·ч состоится 21 октября
«Яндекс Пэй» восстанавливают после атаки беспилотников: деньги пользователей в безопасности
SETI впервые начала системный поиск инопланетных сигналов со стороны Земли на фоне Солнца и нашла 25 спектрограмм
Blue Origin анонсировала запуск New Glenn на декабрь и подтвердила создание пилотируемого корабля
Акции Samsung бьют рекорды, но иностранные инвесторы сократили долю до 17-летнего минимума
Новая кисть Atlas от Boston Dynamics: робот лишился мизинца ради идеального промышленного хвата