Робота з Qwen3.8-27B на одному RTX 3090 за допомогою виправленої версії vLLM та DFlash2
Як форк vLLM 0.28.0 із закріпленими версіями, реквантизованими ембеддингами та технологією DFlash2 дозволяє запускати 27-мільярдний гібридний модель на картці об’ємом 24 ГБ, та чому довгий контекст уповільнює його роботу.
Модель із 27 мільярдами параметрів на одній споживчій GPU зазвичай означає складний вибір між довжиною контексту, паралельністю та швидкістю. Комунітетна версія vLLM, налаштована під Qwen3.8-27B, змінює ситуацію на карті RTX 3090 об’ємом 24 ГБ: у завданні з використанням агентів вона досягає приблизно 177 токенів на секунду під час декодування на звичайній карті (без серії Ti), обмеженій потужністю у 300 Вт. У цьому посібнику описано процес встановлення без використання контейнерів, пояснено, які оптимізації забезпечують таку швидкість, та показано точно, на якому етапі цей підхід вже не є ефективним, щоб ви могли вирішити, чи підходить він вашому завданню.
Два профілі запуску, описані нижче, протиставляють довжину контексту паралельністю та швидкістю. Ці показники отримані для завдання з використанням агентів на одному пристрої, тож сприймайте їх як орієнтовні значення, а не гарантію.
| Config | Context | Parallel requests | My measured decode speed |
|------------|-----------|-------------------|--------------------------|
| CTX=fast | 65,536 | 8 | ~177 tok/s |
| CTX=long | 131,072 | 4 | ~122 tok/s |
Оскільки сервер надає API, сумісне з OpenAI, його підключення до інструменту управління агентами, такого як DeepSeek Harness, полягає у вказівці клієнтом базової URL у форматі http://<host>:18020/v1 та наданні ключа-перевізника. Потім підагенти можуть одночасно надсилати запити до локальної моделі, яка відповідає зі швидкістю, яка раніше вимагала обладнання дата-центру.
Що насправді є проектом qwen38-27b-rtx3090
Проект знаходиться у репозиторії syv-ai/qwen38-27b-rtx3090. Це не нова модель та не новий двигун інференсу. Це форк, прив’язаний до версії vLLM 0.28.0, поєднаний із набором патчів, скриптів для реквантизації та профілів запуску, єдиною метою яких є адаптація цієї конкретної гібридної моделі до картки об’ємом 24 ГБ та її швидкий запуск.
Надається образ Docker, і команда docker compose --profile single up -d є повною процедурою встановлення, якщо ви задоволені використанням контейнерів. Репозиторій також дозволяє виконати ті самі кроки вручну у віртуальному середовищі Python, що й було зроблено тут. Це забезпечує видимість журналів, уникає створення ще одного великого образу на диску та полегшує спостереження за змінами під час кожного кроку.
Чому показник одного tok/s мало значить
Пропускна здатність цього стеку сильно залежить від завдання. Генерація коду відбувається швидко, написання довгих текстів — повільніше, а відтворення тексту, який вже є у запиті, — значно швидше (у розділі нижче «Пошук та складання» пояснюється чому). У документації проекту також наголошується на цьому: показник пропускної здатності для цього стеку нічого не говорить, якщо ви не знаєте, який саме запит його створив. Значення 177 ток/с було виміряно за умов роботи з агентами; ваші результати будуть залежати від формулювань ваших запитів значно більше, ніж від випадковостей.
Встановлення нативно, без Docker
Перш ніж починати, переконайтеся, що у вашому комп’ютері є:
- Linux або WSL2 з відеокартою RTX 3090 будь-якої версії (важлива кількість VRAM у 24 ГБ)
- Актуальний драйвер NVIDIA, щоб команда
nvidia-smiпрацювала без помилок - Python 3.12 або новіша версія разом із заголовками для розробки
Практичний спосіб виконання системних передумов — доручити агенту для програмування з доступом до шеллу (Claude Code, Codex, OpenCode чи подібному) керувати процесом встановлення. Направте його до репозиторію та попросіть запустити необхідні компоненти на вашому 3090. Якщо щось піде не так, агент прочитає повідомлення про помилку та вирішить її на місці — чи то встановивши python3.12-dev або libssl через apt, оновивши драйвер чи змінивши набір інструментів CUDA. Це перетворює пошук відсутніх пакетів, який раніше займав цілий день пошуків у форумах, на звичайний крок. Перевіряйте, що виконує агент, як це робите з будь-яким інструментом з доступом до шеллу.
Крок 1: клонування репозиторію
Спочатку завантажте репозиторій та перейдіть у нього.
git clone https://github.com/syv-ai/qwen38-27b-rtx3090
cd qwen38-27b-rtx3090
Крок 2: створіть віртуальне середовище та встановіть фіксовану версію vLLM
Цей форк орієнтований саме на vLLM 0.28.0, адже саме в цій версії спекулятивне декодування DFlash2 стало частиною основного vLLM (об’єднано у PR #52816). Наведені нижче команди створюють нове віртуальне середовище та встановлюють саме цю версію разом із FlashInfer — інструментом для завантаження з Hugging Face, ninja для будування ядра та pandas.
python3.12 -m venv venv
venv/bin/pip install -U pip
venv/bin/pip install vllm==0.28.0 huggingface_hub hf_transfer ninja \
flashinfer-python flashinfer-cubin==0.6.13 pandas
Тут є два моменти, які легко можна переплутати:
- Не дозволяйте pip переміщувати
flashinfer-pythonна іншу версію. Запускач встановлює параметрFLASHINFER_DISABLE_VERSION_CHECK=1, і патчі були перевірені саме для цієї фіксованої пари версій.
curand.h. Якщо у вашій установці CUDA відсутні заголовки curand, компіляція провалюється під час першого запуску, і сервер тихо перемикається на повільніший альтернативний варіант. Результати залишаються правильними, пропускна здатність знижується приблизно на 5%, а в журналах немає жодних позначок про це. На Ubuntu з CUDA 13 проблему можна вирішити, встановивши libcurand-dev-13-0 через apt.Другий пункт є гарним прикладом збою, який не виявить жодна перевірка стану. Якщо ваші показники на кілька відсотків нижчі, спочатку перевірте наявність цих заголовків.
Крок 3: завантажте базовий чекпоїнт
Відправною точкою є чекпоїнт dbirks/Qwen3.8-27B-W4A16-AutoRound об’ємом близько 19,5 ГБ. Увімкнення режиму передачі даних Xet високої продуктивності значно прискорює завантаження.
HF_XET_HIGH_PERFORMANCE=1 venv/bin/hf download \
dbirks/Qwen3.8-27B-W4A16-AutoRound \
--local-dir models/Qwen3.8-27B-W4A16-AutoRound
Крок 4: підготуйте модель
Скрипти в prepare/ змінюють чекпоїнт. Вони переквантизують матриці ембеддингів вхідних та вихідних даних, які у публічних квантованих чекпоїнтах залишаються з повною точністю, перебудовують голову MTP draft навколо більш підходящого словника, а потім завантажують швидку версію разом із файлом DFlash2 drafter об’ємом 1,2 ГБ. Усе виконується на CPU, і кожен скрипт завершується протягом кількох хвилин.
M=models/Qwen3.8-27B-W4A16-AutoRound
venv/bin/python prepare/quant_lm_head.py $M
venv/bin/python prepare/quant_embed.py $M
venv/bin/python prepare/quant_mtp.py $M
venv/bin/python prepare/build_draft_vocab.py $M --ids prepare/draft_vocab_ids.json
venv/bin/python prepare/fetch_fast_variant.py
venv/bin/python prepare/fetch_dflash2.py
Крок 5: застосувати стек патчів
Каталог patches/ містить приблизно п’ятнадцять файлів .patch, які стосуються налаштувань квантування даних, алгоритмів уваги типу split-KV для перевірки проєктів, виправлень у ядрах Marlin int8, механізму пошуку DFlash2 та іншого. Порядок цих файлів визначений у patches/series. Наведена нижче програма видаляє коментарі та порожні рядки з цього файлу та застосовує кожен патч до пакета vLLM усередині віртуального середовища. Вона ігнорує файл dflash2-backport.patch, який існує лише для версій vLLM старших за 0.28.0, де DFlash2 ще не був вбудованим компонентом.
sed -e 's/#.*//' -e 's/^[[:space:]]*//;s/[[:space:]]*$//' -e '/^$/d' patches/series |
while IFS= read -r name; do
[ "$name" = "dflash2-backport.patch" ] && continue
patch -p1 -d venv/lib/python3.12/site-packages/vllm < "patches/$name"
done
Якщо патч не вдається застосувати, це майже завжди означає неузгодженість версій. Виконайте команду venv/bin/pip show vllm та переконайтеся, що вона відображає саме версію 0.28.0.
Крок 6: перевірка встановлення
Перш ніж щось запускати, створіть API-ключ та виконайте скрипт перевірки в офлайн-режимі. Він перевіряє, чи всі оновлення були встановлені та чи має модель очікувану форму.
openssl rand -hex 24 > api_key.txt
bash verify.sh --no-server # checks all patches applied + model shape correct
Чистий результат означає, що ваша інсталяція повністю збігається, байт за байтом, із налаштуваннями, які використовуються розробниками для тестування. Для самостійного використання, де незначні зміни версій постійно створюють плутанину, така відтворюваність є надзвичайно цінною.
Крок 7: запуск сервера
Усі налаштування здійснюються через змінні середовища. Стандартна команда увімкнює функції DFlash2 та кешування префіксів на обраному GPU; у коментарі пояснюється, як перейти на профіль довгого контексту або на профіль 245 кб після виконання kvarn/install.sh.
CUDA_VISIBLE_DEVICES=0 SPEC=dflash2 PREFIX_CACHE=1 bash single-user/start_qwen.sh
# add CTX=long for ~131k context (4 slots), or CTX=huge after kvarn/install.sh for 245k
Завжди явно встановлюйте CUDA_VISIBLE_DEVICES. Інакше vLLM схильний обирати GPU з найбільшою кількістю вільної пам’яті, що на машині з кількома GPU може бути не тією карткою, яку ви хотіли. Ключ-носій читається з файлу api_key.txt або з параметра VLLM_API_KEY; встановіть його перед тим, як відкрити порт для доступу ззовні localhost, оскільки без ключа сервер приймає неперевірені запити. Решта налаштувань береться з файлу .env. Через кілька хвилин після запуску у вас буде кінцева точка, сумісна з OpenAI, яка обробляє модель розміром 27B на одній картці, вартість якої менша, ніж у вживаного велосипеда.
Звідки береться швидкість
Запуск стокового vLLM із квантованими чекпоїнтами з магазину призводить до витрат продуктивності у кількох неочевидних місцях. Документ з оптимізаціями проекту (docs/optimizations.md у репозиторії) описує дев’ять змін. Чотири з них забезпечують більшу частину покращень.
Квантування ембеддингів, яке всі ігнорують
Qwen3.8-27B використовує нез’єднані ембеддинги, тобто окремі таблиці вхідних та вихідних даних по 2,5 ГБ кожна. Публічні „квантовані“ чекпоїнти зберігають обидві таблиці у форматі bf16, переважно тому, що їх квантування є складним. Скрипти підготовки перетворюють їх обидві у формат int8, що дозволяє звільнити 2,6 ГБ VRAM без помітної втрати якості. На картці на 24 ГБ цього достатньо приблизно для контексту ще одного користувача.
Використання гібридної архітектури
Лише 16 з 64 шарів моделі є звичайними шарами уваги, пам’ять яких зростає разом із контекстом. Решта 48 — це шари Gated DeltaNet, що є реалізацією лінійної уваги, причому стан на кожну розмову має фіксований розмір, незалежно від того, чи складає розмова 100 чи 100 000 токенів. Stock vLLM зберігав цей стан у форматі fp32, що займало близько 150 МБ на запит, і не міг обробляти більше 37 одночасних запитів, попри налаштований ліміт у 64. Форк зберігає його у форматі fp16, рівень перплекситету залишається однаковим з точністю до трьох десяткових знаків, і всі налаштовані слоти стають доступними для використання.
DFlash2: створення семи токенів за один прохід
Ця зміна має найбільше значення для затримки обробки однієї запиту. Спекулятивне декодування поєднує невелику модель-проектувальник із великою цільовою моделлю. Модель-проектувальник пропонує кілька майбутніх токенів, а цільова модель перевіряє їх усі за один проходження, зберігаючи правильний префікс та генеруючи текст знову від першої помилки. Перевірка є значно дешевшою за генерацію на GPU з обмеженою пам’яттю, оскільки ваги читаються один раз для кількох токенів, тож кожен крок може дати більше одного токена.
Вбудований механізм MTP у Qwen поєднує чотири спроби прогнозування одну за одною. Модуль fork замінює його на DFlash2 — п’ятишаровий алгоритм, який прогнозує цілий блок з семи токенів за один неавторегресивний прохід, використовуючи приховані стани з п’яти різних глибин цільової моделі. Сам алгоритм був квантований з GPTQ з 3,85 ГБ до 1,19 ГБ, тож він користується однією карткою, не конкуруючи інтенсивно за пропускну здатність пам’яті. У результаті отримується приблизно три токени на крок замість одного. Оскільки стандартне спекулятивне декодування приймає або відхиляє токени-проєкти так, щоб кінцевий результат відповідав лише цільовій моделі, прискорення є безвтратним; точність GSM8K залишається на рівні від 96 до 96,5% у всіх конфігураціях, про які повідомляє репозиторій.
Словник проєктів, створений на основі власних результатів моделі
Формувальник може пропонувати лише ті токени, які існують у його скороченому словнику вихідних даних; все, що поза ним, за визначенням відхиляється. Початковий словник був створений на основі текстів з Інтернету та охоплював 92% токенів, які насправді генерує ця модель. Автори зібрали 5,4 мільйона токенів власних результатів роботи моделі та перебудували словник на основі цих даних, досягнувши покриття 97,5%. Лише ця зміна додала приблизно 10% продуктивності без використання нового обладнання та без змін у самій моделі.
Пошукове формування для тексту, який цитує модель
Ще одна техніка пояснює найекстремальніші показники. Коли результат містить матеріал, який вже був у запиті — наприклад, код джерела, який потрібно відредагувати, або вставлений документ — цей механізм обходить процес створення тексту та пропонує токени безпосередньо з контексту. Відтворення документа обсягом 25 тис. токенів досягає швидкості 381 ток/с на базовому обладнанні. Агенти-кодувальники багато часу присвячують відтворенню коду, який з’явився кілька моментів раніше у запиті, що є ідеальним випадком для цієї техніки, і саме це є однією з причин високих показників у тестах ефективності агентів.
Чому подвоєння обсягу контексту не призводить до вичерпання VRAM
Запускач для одного користувача за замовчуванням використовує контекст обсягом 65 тис. елементів. Перехід на параметр CTX=long приблизно подвоює цей обсяг до 131 тис., і здавалося б, слід очікувати помилки вичерпання пам’яті на карті обсягом 24 ГБ. Однак сервер запускається нормально та працює лише трохи повільніше. Це пояснюється кількома архітектурними рішеннями.
- Ваги мають фіксований розмір. З квантуванням W4A16 об’єм моделі становить приблизно 15 ГБ, незалежно від довжини контексту.
- Буфер KV резервується один раз, у байтах. Перед обробкою будь-якого запиту форк резервує фіксовану кількість пам’яті — приблизно 5,2 ГіБ у цьому випадку — а vLLM фіксує результат, наприклад "Розмір кешу KV GPU: 136,429 токенів." Оскільки буфер виділяється під час запуску, він або поміщається, або сервер відмовляється запускатися. Не існує виділення пам’яті на кожен запит, яке могло б зазнати невдачі посеред виконання завдання агента.
CTX=long зберігає їх у форматі int8, що приблизно вдвічі знижує витрати на один токен. Таким чином, ті самі 5,2 ГБ можуть вмістити 136 429 токенів замість 68 605, подвоюючи максимальний обсяг контексту до 131 тисячі. Рівень перплексності, встановлений вчителем для ідентичного уривка тексту, відрізняється лише на 0,2% між цими двома профілями.Чому ліміт становить 131 072, а не 136 429
Не весь обсяг пам’яті може бути виділений для кешу запитів. Кожен запит також потребує свого стану DeltaNet фіксованого розміру, а DFlash2 додає ще вісім слотів для спекулятивних станів на кожен запит. Оскільки у довгому профілі є чотири вільні місця, запускач встановлює максимальний обсяг контексту на рівні 131 072, що приблизно на 4% менше за можливості пам’яті, залишаючи решту для цих сторінок стану та для вирівнювання блоків. Саме цей запас забезпечує гарантію відсутності проблем з пам’яттю: під час запуску перевіряється найскладніший сценарій — один запит максимальної довжини, при цьому всі інші вільні місця також зайняті.
Що ви жертвуєте замість цього
Довший контекст оплачується швидкістю, а не збоями. Токени зберігаються у форматі int8, і кожен запит користувача резервує сторінки стану з пулу, який тепер розділений між меншою кількістю місць. Кількість паралельних слотів зменшилася з 8 до 4, а швидкість декодування — з приблизно 177 до приблизно 122 токів/с. Карта виконує більше завдань з тими самими даними та оплачується за пропускну здатність, а не за кількість винятків.
У чому є недоліки: глибокий контекст одного запиту
Цей механізм працює найкраще з короткими та середніми контекстами, які зазвичай використовують агенти. Однак запит, що містить близько 100 тис. токенів, поводиться інакше. На пристрої 3090 швидкість декодування одного запиту становила приблизно 107 токенів/секунду з коротким запитом, 78 токенів/секунду при близько 10 тис. токенів та 38 токенів/секунду при близько 43 тис. токенів; ближче до 100 тис. токенів швидкість знижується до приблизно 31 токенів/секунду. Причиною є рівень прийняття пропозицій моделлю, який зменшується з глибиною контексту. У репозиторії спостерігається такий самий ефект: рівень прийняття залишається близько 0,29 незалежно від типу кешу, тож це є властивістю самої моделі та глибини контексту, а не невиправленим багом.
Для порівняння, форк на основі llama.cpp на аналогічному класі карт досягає стабільної швидкості 65–80 ток/с при обсязі контексту 150 к. У ньому немає механізму оцінки, чиї прогнози погіршуються, та немає блоку перевірки, який би зупиняв роботу, тому він ніколи не є особливо швидким, але й ніколи не повільним. У таблиці нижче ці два варіанти представлені поруч; зверніть увагу, що діапазон короткого контексту vLLM поєднує показники для одного запиту та вимірювання для багатоканального агента.
| Context depth | vLLM fork (DFlash2) | llama.cpp ATX fork |
|-----------------|---------------------|--------------------|
| short (<4k) | 107–177 tok/s | 65–80 tok/s |
| ~10k | ~78 tok/s | 65–80 tok/s |
| ~43k | ~38 tok/s | 65–80 tok/s |
| ~100k | ~31 tok/s | 65–80 tok/s |
Практичне правило є прямим наслідком цього. Якщо ваша робота полягає у повільному читанні одного дуже довгого документа, llama.cpp є більш стабільним варіантом; детальніше про цей баланс можна дізнатися у нашому посібнику щодо роботи з моделлю Qwen3.8 MoE на RTX 3090s за допомогою технології вивантаження тензорів llama.cpp. Якщо ж ваша робота включає багато середньої довжини кодувальних розмов, форк vLLM значно кращий.
Чому це підходить для агентських інструментів
Інструменти на кшталт Pi, Hermes чи DeepSeek Harness не підтримують одну розмову. За допомогою субагентів вони одночасно створюють багато паралельних потоків — зазвичай від п’яти до дванадцяти, кожен з середньою довжиною; більшість з них використовують однаковий системний запит та контекст кодової бази. Саме для такого навантаження був налаштований цей форк:
- Загальна пропускна здатність зростає разом із конкуруентністю. Чотири паралельні потоки на одному пристрої 3090 забезпечували сумарну пропускну здатність понад 300 ток/с. Критерії оцінки з репозиторію показують 279–335 ток/с при чотирьох одночасних запитах та до приблизно 400 ток/с у сумі при восьми запитах із використанням MTP.
PREFIX_CACHE=1 64 запити, які використовують спільний системний промпт довжиною 5,8 тис. токенів, виконуються протягом 17 секунд замість 222 у тестах репозиторію, причому середній час відгуку знижується з 95 с до 8 с. Після першого запиту підагенти майже не платять за спільні інструкції. У цій гібридній моделі кеш також зберігає стан DeltaNet, а не лише параметри уваги KV, тому наступна обробка документа довжиною 24 тис. токенів займає близько 1 секунди замість 23.Існує певний ліміт. Після приблизно чотирьох потоків довгого контексту, які працюють постійно, ви обмежені способом розподілу ресурсів пулу, а не потужністю обчислювальних засобів. У документації чітко зазначено, що вісім одночасних запитів на обробку довгого контексту працюють значно гірше, ніж чотири, і це описується як „не компроміс, а втрата“. Найкращий ефект від використання картки досягається при проектуванні системи, яка використовує приблизно чотирьох паралельних працівників середньої глибини. Щоб побачити приклад того, що можна створити на місцевому обладнанні з Qwen3.8-27B на практиці, перегляньте створення локальної копії гри з використанням Qwen3.8-27B та Pi.
Основні висновки
- Швидкість забезпечується програмним забезпеченням: реквантовані ембеддинги, стан DeltaNet у форматі fp16, алгоритм DFlash2 для обробки семи токенів та словниковий запас, адаптований під реальний вихід моделі. Сама GPU залишається незмінною.
verify.sh, перш ніж довіряти будь-яким результатам тестування.Для отримання більш детальної інформації у папці docs/reproductions репозиторію є пояснення щодо крокового відтворення процесу без використання Docker на пристрої 3090.