Как разогнать Qwen 3.8-27B на Tesla M10: от 8 до 50 токенов в секунду
Мы ускорили Qwen 3.8-27B на старой Tesla M10 в шесть раз на вход и в четыре на выход. Обработка промпта выросла с 7,9 до ~50 токенов в секунду, генерация — с 1,8 до 7–8 токенов в секунду, контекст — 192 тысячи токенов.
Nvidia Tesla M10 — серверная карта 2016 года для VDI: четыре чипа GM107 (Maxwell, sm_50) по 8 ГБ GDDR5 на одной плате. На вторичном рынке она стоит копейки, но для языковых моделей её обычно не рассматривают: архитектура старая, память медленная, между чипами нет быстрой связи. Мы проверили, сколько из неё можно выжать на современной 27-миллиардной модели с агентным клиентом поверх.
Итоговая конфигурация: Qwen 3.8-27B в квантизации Q4_K_M (16,5 ГБ), llama.cpp с тензорным разбиением на четыре GPU, KV-кеш q8_0 в видеопамяти, встроенное MTP-предсказание, разогнанный VBIOS и асинхронный обмен между GPU.
Стенд и стартовая точка
Первый рабочий запуск давал 7,9 токена в секунду на вход и 1,77 на выход: ответ в пару абзацев писался минутами.
Компонент | Что стоит |
|---|---|
Сервер | два Xeon на C612, Debian 12, ядро 6.1 |
GPU | Tesla M10: 4 × GM107 (sm_50), 4 × 8 ГБ GDDR5, 53 Вт на чип |
Шина | слот PCIe 3.0 x16 → коммутатор PLX 8747 → по x8 на каждый чип |
Драйвер | NVIDIA 550.54.14, CUDA 12.4 |
Модель | Qwen 3.8-27B, GGUF Q4_K_M (unsloth), 16,5 ГБ, родной контекст 262 144 |
Инференс | llama.cpp, собранный для sm_50 |
Модель не помещается ни в один чип, поэтому её нужно делить на четыре GPU. Первый профиль резал её по слоям (--split-mode layer), держал KV-кеш в оперативной памяти (--no-kv-offload), чтобы влезло окно 256k, и работал с ubatch 64.
У такой схемы три узких места:
При разбиении по слоям в каждый момент работает один чип из четырёх.
Внимание считается на CPU над кешем в RAM.
Напрямую чипы обмениваться не умеют:
nvidia-smi topo -p2pпоказывает «not supported» для всех пар, любая копия идёт через память хоста.
Как улучшали
Самый большой рывок дало тензорное разбиение: четыре чипа начали считать одновременно, и prefill вырос с 15 до 40 токенов в секунду.
Этап | Prefill, ток/с | Генерация, ток/с |
|---|---|---|
Старт: разбиение по слоям, KV в RAM, 256k | 7,9 | 1,77 |
KV-кеш q8_0 на GPU, flash attention, MTP, 128k | 15,1 | 3,14 |
Тензорное разбиение, ubatch 512 | 39,7 | 7,18 |
Асинхронные копии между GPU | 48,2 | 7,90 |
Контекст 192k (текущий профиль) | 49,4 | 6,98–7,73 |
Prefill измерялся на промптах от 1,4 до 8,4 тысячи токенов, генерация — на задачах с кодом при температуре 0. Первые две строки сняты ранней версией теста, поэтому сравнивать их с остальными стоит по порядку величины.
1. KV-кеш на GPU и MTP
Qwen 3.8 — гибридная модель: полное внимание есть только в каждом четвёртом из 65 слоёв, остальные линейные (SSM). Поэтому KV-кеш маленький, и в формате q8_0 он помещается в видеопамять рядом с весами.
Модель также несёт слой MTP (multi-token prediction), который llama.cpp использует как встроенный черновик (--spec-type draft-mtp): никакой отдельной маленькой модели не нужно.
2. Тензорное разбиение
--split-mode tensor делит каждую матрицу на четыре части, и все чипы работают над каждым слоем. Режим в llama.cpp помечен как экспериментальный, но на M10 работает стабильно. Для сравнения: лучшая настройка разбиения по слоям (batch 1024, ubatch 256) давала всего 17,1 ток/с prefill.
3. Разгон через VBIOS
Заводской VBIOS ограничивает напряжение 1,14375 В, из-за этого чипы в нагрузке стояли на 1149–1175 МГц. Мы подняли потолок до 1,2 В (максимум таблицы напряжений) и boost до 1228 МГц. В нагрузке частоты стали 1176–1202 МГц, потребление осталось в пределах 53 Вт.
Выигрыш небольшой, пара процентов, а риск заметный: стоковый nvflash такие образы не принимает из-за подписи, нужна сборка с обходом проверки.
4. Асинхронные копии между GPU
При тензорном разбиении чипы после каждого слоя складывают свои части (all-reduce). Наша сборка была с GGML_CUDA_NO_PEER_COPY=ON, и каждая такая копия блокировала вычисления. Инструментированная сборка показала: из ~92 мс на проход 21 мс уходило на обмен.
После пересборки с асинхронными cudaMemcpyPeerAsync обмен занимает 11 мс. P2P на M10 по-прежнему нет, драйвер гонит данные через память хоста, но теперь параллельно со счётом.
5. Кеш промпта
Для диалога главное не скорость первого prefill, а то, что история не пересчитывается. С cache_prompt: true в запросе первый ответ на промпт в 7,6 тысячи токенов приходит через 165 секунд. Каждый следующий вопрос в том же диалоге — через 8–9 секунд: заново считаются только 25–27 новых токенов.
6. MTP с одним черновым токеном и контекст 192k
--spec-draft-n-max 1 дал +12,6% на коротких ответах (6,40 → 7,20 ток/с), а на ответах в 600 токенов разница в пределах шума. Доля принятых черновиков выросла с ~78% до ~94%.
Контекст 196 608 токенов скорость не снизил: в одной и той же загрузке стенда 128k дал 7,04 ток/с генерации, 192k — 6,98.
Что не сработало и подводные камни
Половина попыток не дала ничего, а самые дорогие проблемы оказались не в модели, а в BIOS и драйверах.
Попытка | Результат |
|---|---|
Контекст 262 144 на GPU | CUDA out of memory на первом же запросе: KV-кеш занимает 8037 из 8192 МиБ, рабочим буферам не хватает места |
| «CUDA0 does not support split buffers» в этой сборке |
Четыре параллельных слота | 5,3 ток/с суммарно, 1,5 на запрос, память почти кончилась |
Разгон до 1306 МГц | нужно ~1,306 В, а максимум таблицы напряжений 1,2 В |
Настоящий P2P между чипами | недоступен: драйвер сообщает «not supported» для всех пар, а на коммутаторе включён ACS |
PCIe x16 до чипов | невозможно: каждый GM107 разведён на плате по x8, x16 есть только до коммутатора |
Два камня стоит знать заранее:
nouveau. Если его не запретить, он захватывает GPU при загрузке. Отвязка на ходу через sysfs один раз сработала, а в другой раз уронила ядро (NULL dereference в
nouveau_bo_move):modprobe nvidiaзавис, и сервер пришлось перезапускать по питанию.Охлаждение. M10 пассивная, ей нужен хороший активный обдув: серверные вентиляторы на высоких оборотах или отдельный вентилятор на плату(Охлаждение для видеокарты NVIDIA TESLA M10, 80мм). При нормальном обдуве чипы в нагрузке держали 40–53 °C, а со слабым быстро доходят до порога замедления 91 °C (аварийное отключение — 96 °C). Разница между самым холодным и самым горячим чипом на одной плате доходит до 40 °C, так что смотрите на максимум, а не на среднее.
Инструкция по настройке
Ниже всё, чтобы повторить текущий профиль на Debian 12 с Tesla M10. Разгон VBIOS сюда не входит: он даёт пару процентов и требует прошивки модифицированного образа.
1. BIOS
Включите Above 4G Decoding: без него четыре GPU не получат 64-битные BAR.
Проверьте режим вентиляторов: карта пассивная, нужен режим для GPU-нагрузки или полные обороты.
После загрузки убедитесь, что у каждого GPU есть Region 1 и Region 3:
sudo lspci -vv -d 10de:13bd | grep -E "Region (1|3)"2. Драйвер и nouveau
Запретите nouveau до установки драйвера и не отвязывайте его на ходу:
printf 'blacklist nouveau\noptions nouveau modeset=0\n' | sudo tee /etc/modprobe.d/blacklist-nouveau.conf
sudo update-initramfs -uУстановите драйвер NVIDIA 550 (мы используем 550.54.14) и перезагрузитесь. Проверка: nvidia-smi -L показывает четыре Tesla M10.
3. Сборка llama.cpp
Нужны CUDA 12.4 (старшая версия, которую поддерживает драйвер 550; sm_50 в ней ещё собирается) и CMake. Главное — GGML_CUDA_NO_PEER_COPY=OFF, иначе обмен между GPU станет блокирующим:
cmake -S llama.cpp -B build -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_CUDA_ARCHITECTURES=50-real -DGGML_CUDA=ON -DGGML_NATIVE=OFF \
-DGGML_CUDA_FA=ON -DGGML_CUDA_GRAPHS=ON -DGGML_CUDA_NO_VMM=ON \
-DGGML_CUDA_NO_PEER_COPY=OFF -DGGML_CUDA_NCCL=OFF
cmake --build build -j --target llama-server4. Модель
Файл Qwen3.8-27B-UD-Q4_K_M.gguf из репозитория unsloth/Qwen3.8-27B-GGUF на Hugging Face, 16,5 ГБ. Сверьте SHA-256 с опубликованным.
5. Запуск
CUDA_DEVICE_ORDER=PCI_BUS_ID ./build/bin/llama-server \
--model Qwen3.8-27B-UD-Q4_K_M.gguf --ctx-size 196608 \
--gpu-layers 99 --split-mode tensor --tensor-split 1,1,1,1 \
--batch-size 512 --ubatch-size 512 --flash-attn on \
--cache-type-k q8_0 --cache-type-v q8_0 \
--spec-type draft-mtp --spec-draft-n-max 1 \
--threads 10 --threads-batch 20 --jinja --no-mmproj \
--host 127.0.0.1 --port 8080 --parallel 1 --alias qwen3.8-27bСервер поднимается за 5–50 секунд и занимает ~7 ГБ на каждом GPU. Если нужен запас памяти, поставьте --ctx-size 131072: освободится ~600 МиБ на GPU. Значение --threads подберите под свои процессоры.
6. Проверка
curl -s http://127.0.0.1:8080/health
nvidia-smi --query-gpu=index,temperature.gpu,clocks.sm,power.draw,memory.used --format=csv -l 5Во время длинного prefill следите за самым горячим чипом: выше 85 °C — сигнал проверить охлаждение.
7. Подключение клиента
Подходит любой клиент OpenAI API на той же машине с адресом http://127.0.0.1:8080/v1 и моделью qwen3.8-27b. Для агента достаточно такого провайдера:
[providers.local-qwen]
type = "openai-compatible"
base_url = "http://127.0.0.1:8080/v1"
default_model = "qwen3.8-27b"
requires_api_key = false
extra_body = { chat_template_kwargs = { enable_thinking = false } }
[[providers.local-qwen.models]]
context_window = 196608Два совета для агентов:
Поставьте
stream_idle_timeout_secsв несколько минут: первый prefill большого контекста идёт долго.Урежьте набор инструментов: их описания идут в каждый запрос, и при 50 ток/с лишние 10 тысяч токенов — это три минуты ожидания.
Итоги
Дешёвая M10 тянет 27B-модель с контекстом 192k на скорости ~50 ток/с на вход и 7–8 ток/с на выход. Этого хватает для чата и неспешного агента с кешем истории, но не для интерактивной работы с целым репозиторием: промпт в 100 тысяч токенов считается полчаса.
Больше всего дали не тонкие настройки, а два решения: держать всё в видеопамяти и заставить четыре чипа считать одновременно. Дальше упираемся в обмен через память хоста (11 мс из ~84 на проход) и в сами чипы Maxwell.
Комментарии
Загрузка комментариев…