Чаму две картакі RTX 3090 даюць 55 tok/s замест 220 з вікорыстаннем SGLang і DFlash2.
Практычны ўзгляд на запуск 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. 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, якая падтрымлівае CUDA 13, а версія triton автоматычна выставлена на 3.7.1 — гэта сама версія, якая патрэбна GPU мадэляў Ampere, каб ухіліцца ад вядомага бага ў кэрнелі.
Крок 3: модэлі. За рэкамендацыямі спальноты трэба завантажыць два файлы:
cyankiwi/Qwen3.8-27B-AWQ-INT4— галоўная модэль. За дапамогою квантызаціі INT4 ёе розмер зменшваецца да апоўну 20 ГБ у 5 частак, што даскладаецца для двух GPU з памяцю 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. У модэлі 27B гэта дае апошню колькасць 100 раунд-трыпаў на кожны створаны токен. Для з’едыначэння даных такой шчылай потрэбны быстрыя зв’язкі между картамі, а гэтыя две карты такога зв’язку не мелі. Два аднальныя браку прымусілі ўзаімна паглыбляцца.
Няма пэйр-ту-пэйра. Карты GeForce больш не падтрымляюць офіцыйна функцыю GPU-direct P2P, таму кожны данневы пакет праходзіць праз RAM сістэмы: з 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 т/с у працэўнасці з прагнутымі 1 200 або больш, а працэс дэкадавання застоюецца на роўні 55.
Відэакарты ўзятыя за швальнасць. Але кабель, які ўз’еднуе іх, ёсць медленным. У рэжыме тэнзорнага паралелізму кабель фактычна стае самым модэлем.
Як хтось можа досягнуць 220
Ніхто не показвае свою матырнай плакат у дэмантэ. Якша падключаныя x16-пары з працюючым P2P даюць прыблізна у дзесяць разоў большую пропускную здатнась між картамі, чым у гэтым рашынку. Указаная цыфра, верагодна, была рэзультатам кароткага запуску програмы з вялікімі кэшамі і ідэальнымі умовамі працы, а не стабільнай швальдзей. Програмна частка гэтага рашынку ў парадку, але яна таямна прыяўляе наявнась апаратуры, якой маюць толькі некалькі домашніх комп’ютэраў.
Ішчы два адналікавання для ўседневнага вжытку
Камера кантэксту ёсць меншою, ніж очыкувалося. Два карткі по 24 ГБ здаюцца достатнімі. Однак потрыбно вірахуваты вагі INT4 (прыблізно 10 ГБ на кожну картку), даныя працэсу формування тэксту, графікі CUDA, а таксама памяць для рэкурэнтного стану праз вікорыстання гібрыдноі увагі у Qwen3.8. У рэзультаты для кешу KV залішалося прыблізно 103 тыс. токенів загалом, або 131 тыс. пры більш рызікованіх налаштуваннях. Одна заявка на 65 тыс. токенів підходыць без проблем. Оголошэна колькасць 48 одночасніх сеансів не ёсць рэалістычною, оскількі кожна актывна розмова таксама вікорыстовуе частыну памяці mamba, і насамрэч можа вестыся лішэ тры–шысць сеансів. На відміну ад гэтага, llama.cpp перэдае майжэ всю VRAM одній мадэлі і без проблем апрацоўвае велікі кантэксты.
Перзагрузка, вызваная падэннем напаўжэння. Калі обе 3090 працуюць адночасна, выпрацоўваючы задачы прадзейсвёрджэння даных і стварэння праекта пад максымальным навантажэнням, спрабоўкі падключэння прыходзяць з рангу «без навантажэння» да апошніх 700 ватаў за каліксекунды. Адпрацоўвачы напаўжэння паслужаюць сабе пасля гэтага слабка. Одна такая рывкавата змянена напаўжэння запускае захіст і выключае сервер у момент генеравання даных. Ён перзагружаецца сам, але урок застаёцца: для такага навантажэння два карточкі выкалікаюць патрэбу ў адпрацоўвачы напаўжэння высокай якосці з рэальным запасам на першыя моманты падвышэння навантажэння, і паказаная на упаковцы колькасць ватаў недастатня.
Чы хаця бы прабаваць гэта ў домашніх умовах?
SGLang плюс DFlash2 — гэта вражаючая інжынерная рашытка, калі ўсе яе прыпускі справядлівы, а гэтыя прыпускі складаюцца з чаклісту апаратных умоў:
- Две GPU ў справжніх слотах x16. Блакеты для гэймінгу для прастых корыстнікаў зазвычай маюць адную GPU, можа ўжо з другой, якая підключаецца праз чыпсет на прысоедзінанні x4. Перад пакупкай обавязкова прачытайце інструкцыю, адколькі самэ гэта параметра вялічынюе розныцю между 220 і 55.
- Функцыяянальная комунікацыя между GPU. Гэта значыць наявнасць функцый Resizable BAR і налаштаванняў ACS у BIOS, а таксама драйвероў, створаных спільнайясцю для GeForce чым NVLink паza межамі прастых блакетоў. На практыцы гэта тэрыторыя серверных матарысоў.
- Адміністрацыйны блакет з высокай якосцю і дапаможнай моцы. Разбіжнасці напаўвольтажа, якія ствараюць две карты моцы 350 Вт, зрэшты прыведуць да перавантажэння слабкага блакета.
- Чатыры карты RTX 3090 для працы з Qwen3.8-27B у рэжыме пূрнага кантэксту. З такім бюджэтам кеш KV перастае быць абмежэннем, на вядомаць у прыпадку скромнага кешу 103K.
Такія програмы не прымножваюць дарогі апарат. Яны працуюць медленней там, дзе ніхто не падумаў заглянуць.
Нямаючы ні програмнага, ні апаратнага забезпечэння, якое ён прыпускае, llama.cpp застаецца розумным выборам. Гэта означае адну карцу, адны бінарны файл, працэсаванне на 75 токеноў за секунду па стандартнаму PCIe, адсутнае вікорыстанне інструментальных комплектаў Rust і няпатрэбныя перэзапускі. Пара GPU можа працаваць незалежна з шырыней канала x4.
Цячка 220 была правдзівай. Але яна не паслужыць для гэтага прыстрою, і, верагодна, не для багато іншых, пакуль не будзе перапрацаваны кабель межаў карточак.