Головна / Статті / Як подвоїти швидкість декодування Qwen3.8-Flash-Next з MTP на відеокартах RTX

Як подвоїти швидкість декодування Qwen3.8-Flash-Next з MTP на відеокартах RTX

Як зібрати форк llama.cpp зі спекулятивним декодуванням MTP і підібрати прапорці, щоб приблизно вдвічі пришвидшити генерацію Qwen3.8-Flash-Next на системах із кількома RTX.

1248 слів

Багато хто каже, що Qwen3.8-Flash-Next не дотягує до своїх результатів у бенчмарках. У щоденній роботі поруч із Qwen3.8-27B вона поводиться інакше, ніж каже ця репутація. Як на свій розмір, вона не спалює багато токенів на міркування, робить те, що просять, без суперечок, тримає нитку довгої сесії й правильно пригадує ранішу історію. Порівняно з DeepSeek V4 Flash 0731 це приємніший повсякденний асистент, і легко дійти до того, щоб зробити її моделлю за замовчуванням.

Єдина претензія — швидкість. На трьох RTX 3090 і RTX 3070 Ti декодування тримається на рівні близько 30 токенів за секунду. Це повільна рівна крапля, і швидка друкарка її обжене.

Відомі ліки є, і звуться вони MTP (multi-token prediction). Це варіант спекулятивного декодування від DeepSeek: легка чернеткова голова, прикручена до моделі, пропонує кілька наступних токенів, а повна мережа лише перевіряє їх. Перевірка коштує значно дешевше за генерацію, тож п'ять токенів можуть з'явитися за той час, що зазвичай іде на один.

Заковика в тому, що llama.cpp не змерджив MTP для цієї моделі. Після довгих спроб і помилок одна комбінація ваг, форка й прапорців приблизно подвоїла пропускну здатність із тими самими файлами моделі, тим самим сервером і тими самими чотирма GPU. Рецепт нижче, а після нього — про те, чому випуск цієї функції перетворився на таку плутанину.

Візьміть правильні ваги

Для основної моделі завантажте збірку Q4_K_M на 4,27bpw із GGUF-репозиторію AtomicChat.

Цей квант робили з прицілом на MacBook: він розрахований на об'єднану пам'ять Apple silicon. Але й на картах RTX він працює чудово, до того ж йому потрібно менше VRAM, ніж співставному кванту від Unsloth. Іноді збірка, запакована для однієї аудиторії, виявляється придатною для іншої.

У репозиторії є й менший варіант — IQ4_XS на 3,84bpw. Протестували обидва, і менший помітно гірший. Економія 0,4 біта на вагу не варта того, що ви втрачаєте.

Голова MTP постачається окремим файлом-супутником, який лежить у теці sidecar в Unsloth. Тут використовувалася голова Q8_0. Якщо VRAM мало, підійде й голова Q4_K_M.

Зберіть форк

Робочий код лежить у гілці qwen4exp/mtp форка llama.cpp від danielhanchen. Власна документація Unsloth відсилає туди ж, що свідчить про те, де ця функція насправді живе зараз.

Це звичайна збірка через CMake: встановіть пакети, склонуйте гілку, сконфігуруйте.

apt-get update
apt-get install pciutils build-essential cmake curl libcurl4-openssl-dev -y
git clone --branch qwen4exp/mtp https://github.com/danielhanchen/llama.cpp
cmake llama.cpp -B llama.cpp/build \
    -DBUILD_SHARED_LIBS=OFF -DGGML_CUDA=ON
cmake --build llama.cpp/build --config Release -j --clean-first \
    --target llama-cli llama-mtmd-cli llama-server llama-gguf-split

Запустіть сервер

Ось команда запуску, яка працює як є. Вона використовує чотири GPU, контекстне вікно на 200K і чернетку MTP, що може пропонувати до 5 токенів за крок.

CUDA_VISIBLE_DEVICES=0,1,2,3 llama-server \
  -m .../Qwen3.8-Flash-Next-AD-4.27bpw-Q4_K_M-M64-00001-of-00033.gguf \
  --alias Qwen3.8-Flash-Next \
  --jinja \
  -ngl 99 \
  -c 200000 \
  --split-mode layer \
  --tensor-split 0.9,0.22,1.0,0.75 \
  -fit off \
  --host 0.0.0.0 --port 8082 \
  --metrics \
  --temp 1.0 --top-p 0.95 --top-k 20 --min-p 0.0 --repeat-penalty 1.0 \
  --reasoning-format none \
  --reasoning-effort low \
  --parallel 1 \
  -md .../mtp-Qwen3.8-Flash-Next-Q8_0.gguf \
  --cache-type-k q8_0 --cache-type-v q8_0 \
  --spec-type draft-mtp --spec-draft-n-max 5

Основну частину пришвидшення дають кілька прапорців:

  • --spec-type draft-mtp вмикає чернеткову голову MTP. Доки апстрим не змерджив цю роботу, цей прапорець знає лише форк.
  • --spec-draft-n-max 5 дає чернетці змогу вгадувати до п'яти токенів наперед. З цією моделлю частка прийнятих токенів була досить високою, щоб п'ять мали сенс.
  • -md ... вказує на файл-супутник із головою MTP, бо до основного кванта вона не входить.
  • --parallel 1 обов'язковий. Є відкритий баг: MTP разом із кількома паралельними слотами дає вмісту однієї розмови просочуватися в іншу. Один слот зберігає вивід чистим.
  • --split-mode layer разом із --tensor-split 0.9,0.22,1.0,0.75 розкладає шари по чотирьох нерівних картах: трьох 3090 на 24 ГБ і одній 3070 Ti на 8 ГБ. Ці пропорції підібрано під конкретний набір, тож під своє залізо їх, найпевніше, доведеться підлаштувати.
  • -fit off змушує llama.cpp завантажувати рівно те, що ви вказали, а не ухвалювати власні рішення щодо пам'яті.

Що ви отримаєте

На тій машині «до» і «після» виглядали так:

Before MTP    After MTP
Decode speed          ~30 t/s       60–80 t/s
Model weights         same          same
Server hardware       same          same
Quality               same          same
Total VRAM used       76GB          ~80 GB

Різницю радше відчуваєш, ніж вимірюєш. За 30 токенів на секунду ви дивитеся, як модель друкує. За 60–80 ви з нею розмовляєте. Ті самі ваги, та сама машина, той самий ранок, а якість виводу не зрушила.

Будь-яка система з понад 80 ГБ VRAM може запустити саме цю конфігурацію й має отримати такий самий приріст.

Чому це досі не змерджено?

Бо опенсорс іноді складається невдало, навіть коли код уже існує.

MTP для Qwen3.8-Flash-Next — не нерозв'язана дослідницька задача. vLLM і SGLang підтримували його з першого дня. Форки llama.cpp його постачають, а Unsloth вбудовує його у свій десктопний застосунок. Демовідео зі 170 токенами на секунду — справжні.

До апстриму llama.cpp його намагалися внести п'ять окремих pull request'ів, і жоден не пройшов. Один висів чернеткою з конфліктами злиття й без жодного рев'ю. Інший закрили після того, як контриб'ютор надіслав чужий код без дозволу автора. Мейнтейнер пояснив, що згадати людину в подяках — не те саме, що отримати її згоду, і відтоді найкраща робоча реалізація лишилася безхазяйною. Третій, найчистіший з усіх, минулого тижня закрив сам автор із повідомленням, що в апстримі цим зараз не займаються. Застряглий PR #27836 і покинутий PR #28610 можна прочитати самостійно.

Довкола цих PR три складні проблеми. Перша — суперечка про формат GGUF: три конкурентні варіанти того, де зберігати голову MTP. Друга — баг коректності з паралельними слотами, який відстежується в issue #28286. Третя — регресії на бекендах: та сама функція, що подвоює швидкість на CUDA, на Vulkan дає чистий програш.

Ніщо з цього не робить мейнтейнерів лиходіями. Їхнє правило — основна гілка ніколи не має ламати чийсь продакшен-сервер, а спекулятивне декодування на моделі з гібридним станом — мабуть, найстрашніший диф, який їм можна надіслати. І все ж функція, яка працює, і функція, яку випущено, — два різні інженерні артефакти, і розрив між ними вимірюється тижнями очікування.

Чи варто пробувати?

Один вечір збирання зробив модель для щоденної роботи вдвічі швидшою, а команди вище — це весь рецепт. Якщо у вас 80+ ГБ VRAM і Qwen3.8-Flash-Next, що видає токени по краплині, зусилля того варті. Власний посібник Unsloth з MTP покриває бік постачальника.

Чи наздожене основна гілка — гадати марно. Передумови з'являються, формат усталюється, тож, імовірно, хтось донесе правильну реалізацію до правильної гілки з благословення її авторів. А доти передній край живе у форках, тож клонуйте відповідно.