Tesla v100 SXM2  X2 32GB total

от автора

Можно ли запустить современную 27-миллиардную модель и полноценного автономного агента на паре серверных ускорителей 2017 года, установленных в обычный десктоп через переходники? Короткий ответ — да, но с оговорками, которые важно знать заранее.

В этом материале я разбираю практический кейс: развёртывание Qwen3.6-27B на двух Tesla V100-SXM2-16GB под управлением автономного агента Hermes от Nous Research. Карты подключены к потребительской платформе через адаптеры SXM2→PCIe — конфигурация, которую несложно собрать дома, но которая накладывает жёсткие ограничения на доступную видеопамять и межкарточную пропускную способность.

По итогам эксперимента эту связку можно считать нижней границей практической применимости для локального запуска Hermes: всё работает, но ровно на пределе возможностей железа. Ниже — последовательный разбор всех подводных камней, рабочие конфигурации с готовыми командами и честный вывод о том, где проходит та самая стена, которую не обойти настройками.

Железо и цель

Сервер: Proxmox, проброс (PCIe passthrough) двух карт в одну VM.

GPU: 2× Tesla V100-SXM2-16GB через переходники SXM2→PCIe. CPU — Intel 6700k.

Важная деталь: NVLink между картами нет (переходники выводят только PCIe-линии), и 6700k даёт всего 16 линий PCIe 3.0 → карты работают в режиме x8/x8.

Цель: запустить Qwen3.6-27B-AWQ и подключить агента Hermes от Nous Research, которому нужно минимум 65 000 токенов контекста.

Проверить топологию и режим линий можно так (внутри VM, после установки драйвера):

nvidia-smi topo -m          # между GPU0/GPU1 ждём NV*, а получили PHB = NVLink нетnvidia-smi -q | grep -A2 "Link Width"   # Current: 8x = потолок межкарточного обмена

Грабля №1: «карты грузятся на 50%»

Классическая жалоба: при работе двух карт каждая загружена примерно наполовину. Причина — llama.cpp и подобные движки по умолчанию делят модель по слоям (pipeline/layer split): пока считает GPU0, GPU1 ждёт. Среднее — 50%.

Лечится переходом на tensor parallelism, где модель режется «поперёк» и обе карты считают каждый токен одновременно. В vLLM это флаг --tensor-parallel-size 2. Именно он даёт обеим картам реальные ~100% загрузки.

Грабля №2: новый vLLM не поддерживает Volta

Qwen3.6 требует vllm>=0.19.0, а свежий vLLM уже не поддерживает архитектуру Volta (sm_70) — падает при старте. Вдобавок AWQ-ядра (Marlin) требуют sm_80+. Решение — community-форк 1Cat-vLLM, который возвращает SM70-ядра внимания, AWQ под Volta и поддержку Qwen3.5/3.6.

Почему именно 1Cat-vLLM, а не другой движок

Здесь сходятся сразу несколько требований, и закрыть их все может только этот форк:

Стоковый vLLM новых версий выкинул поддержку sm_70 — на V100 не стартует в принципе.

Старый vLLM, который ещё поддерживал Volta, не знает архитектуру Qwen3.5/3.6 (Gated DeltaNet + MTP) — упадёт на «unknown architecture». Получается вилка: новый движок знает модель, но не знает железо; старый знает железо, но не знает модель.

llama.cpp / GGUF заводится на V100, но Qwen3.6 в GGUF на момент экспериментов корректно не собиралась под этот гибрид, и tensor-parallelism там слабее.

SGLang, TGI и прочие официально требуют sm_75+ для современных моделей.

1Cat-vLLM — единственный из доступных, кто одновременно закрывает обе стороны вилки: возвращает sm_70-ядра (TurboMind SM70 WMMA для AWQ) и содержит код под Qwen3.5/3.6 с MTP и mamba/GDN-слоями. Плюс он валидирован авторами именно на multi-GPU V100 (их бенчи — на 4×V100-16GB), то есть это не теоретическая совместимость, а проверенная на нашем же классе железа.

Установка — из готовых колёс (НЕ из исходников, это для разработчиков ядер):

# скачать оба.whl из релизаmkdir ‑p ~/wheels && cd ~/wheelscurl ‑s https://api.github.com/repos/1CatAI/1Cat‑vLLM/releases/latest \| grep browser_download_url | cut ‑d '“' ‑f4 | xargs ‑n1 wget”# поставить в conda‑средеpython ‑m pip install ‑prefer‑binary ‑no‑cache‑dir \‑extra‑index‑url https://download.pytorch.org/whl/cu128 \/flash_attn_v100-*.whl./vllm‑*.whl

Колёса тянут torch под CUDA 12.8 — это совместимо с рантайм-драйвером 580/CUDA 13, доустанавливать toolkit не нужно.

Почему именно AWQ, а не обычный 4-битный GPTQ

Это не вопрос вкуса — формат квантизации диктует само железо. На Volta (sm_70) большинство современных 4-битных схем просто не запускается:

GPTQ-Int4 через Marlin-ядра требует sm_80+ (Ampere и новее). На V100 эти ядра не компилируются и не исполняются — стандартный «быстрый» путь GPTQ для нас закрыт.

FP8-вычисления — это вообще Hopper (sm_90). На Volta из FP8 доступен только формат e5m2 для KV-кэша, и то с оговорками.

GPTQ-Int8 заводится, но 8 бит на вес означают вдвое больший размер: 27B-модель в Int8 весит ~50+ ГБ и не влезает даже близко в 32 ГБ суммарной VRAM.

Остаётся AWQ 4-bit — и именно его «оживляет» форк 1Cat-vLLM: он интегрирует TurboMind SM70 WMMA-ядра и расширяет AWQ-слои vLLM так, чтобы 4-битный AWQ исполнялся на Volta. По сути это единственный 4-битный формат, который реально работает на V100.

Бонусом AWQ-4bit ужимает 27B до ~21 ГБ — только в таком виде модель вообще помещается в 2×16 ГБ. Поэтому весь поиск моделей шёл по простому правилу: Volta → только AWQ-4bit через 1Cat-форк → ищем AWQ-сборки нужных моделей (например, от QuantTrio).

Грабли по мелочи (на которых легко застрять)

Мало RAM у VM. vLLM по умолчанию резервирует 4 ГБ swap на карту. Если у VM всего 8 ГБ RAM — падает с «Too large swap space». Дайте VM 24+ ГБ.

fp8e4nv not supported. Volta умеет только FP8-формат e5m2. Поэтому KV-кэш в FP8 включается так: --kv-cache-dtype fp8_e5m2 (не просто fp8).

No valid cudagraph sizes (кратность 5). Когда включён встроенный MTP (num_speculative_tokens=4), CUDA-графы должны захватываться размерами кратными 5. Ставьте --compilation-config '{"cudagraph_mode":"full_and_piecewise","cudagraph_capture_sizes":[5]}' или отключите MTP.

Tool calling. Hermes шлёт tool_choice: auto, поэтому сервер надо поднимать с --enable-auto-tool-choice --tool-call-parser qwen3_coder --reasoning-parser qwen3.

Архитектурный сюрприз: почему 65k вообще влезают

Qwen3.6-27B — гибрид. Из 64 слоёв только 16 имеют обычное внимание с растущим KV-кэшем, остальные 48 — Gated DeltaNet (линейное внимание), у которого кэш не растёт с длиной контекста. Поэтому память под длинный контекст у неё в разы меньше, чем у обычной dense-модели, и 65k в принципе достижимы даже на 32 ГБ суммарной VRAM.

Что работает на 2×V100-16GB

После всех граблей мы получили рабочую конфигурацию: vLLM поднимается, отвечает по сети, Hermes ходит, tool-calls проходят, контекст 65k. Цена — отключённый prefix caching (об этом ниже).

CUDA_VISIBLE_DEVICES=0,1 \VLLM_DISABLE_PYNCCL=1 \VLLM_1CAT_DISABLE_QWEN35_MTP_DEFAULTS=1 \python -m vllm.entrypoints.openai.api_server \  --model ~/models/Qwen3.6-27B-AWQ \  --served-model-name qwen36 \  --tensor-parallel-size 2 \  --dtype float16 \  --kv-cache-dtype fp8_e5m2 \  --gpu-memory-utilization 0.92 \  --max-model-len 65536 \  --max-num-seqs 1 \  --max-num-batched-tokens 512 \  --trust-remote-code \  --attention-backend TRITON_ATTN \  --disable-custom-all-reduce \  --skip-mm-profiling \  --limit-mm-per-prompt '{"image":0,"video":0}' \  --enable-auto-tool-choice \  --tool-call-parser qwen3_coder \  --reasoning-parser qwen3 \  --compilation-config '{"cudagraph_mode":"full_and_piecewise","cudagraph_capture_sizes":[1]}' \  --host 0.0.0.0 --port 8000

Проверка контекста и теста:

curl -s http://127.0.0.1:8000/v1/models | python3 -m json.tool   # max_model_lencurl http://127.0.0.1:8000/v1/chat/completions -H 'Content-Type: application/json' \  -d '{"model":"qwen36","messages":[{"role":"user","content":"Привет!"}],"max_tokens":100}'

Реальные показатели: декод ~45 ток/с, обе карты под нагрузкой. Минус — каждый ход агент заново обрабатывает весь промпт (медленный prefill), потому что кэш отключён.

Где стена: 65k + prefix caching одновременно не выходит

Логичный шаг — включить prefix caching, чтобы статичный системный промпт + 60 инструментов Hermes не пересчитывались каждый ход. Но на 2 картах это не работает, и вот почему.

Prefix caching на этой гибридной модели функционирует только в паре с --mamba-cache-mode align. А align-режим добавляет память под выровненный кэш Gated DeltaNet. В сумме «веса 21 ГБ + 65k KV + align-буферы + временные буферы GDN-ядра» не помещаются в 32 ГБ. Сервер стартует (проверка KV проходит), но падает на первом же запросе в ядре chunk_gated_delta_rule:

RuntimeError: Triton Error [CUDA]: out of memory

Мы проверили это при gpu-memory-utilization 0.95 и 0.88, с разным контекстом — результат один. На 2×16GB честно невозможно иметь одновременно 65k контекста и работающий prefix cache. Три доступных режима:

Режим

65k?

Prefix cache?

Итог

65k без кэша (fp8, без align/MTP)

да

нет

работает, но медленный prefill

Кэш + align, контекст <65k

нет

да

Hermes не стартует (нужен минимум 64k)

65k + align

стартует

да

падает в рантайме (OOM в GDN)

Развилка и решения

Вариант А — жить с тем, что есть. Qwen3.6-27B на 65k без кэша. Полностью рабочий агент, просто первый токен каждого хода идёт через полный prefill (на x8 PCIe это ощутимо).

Вариант Б — взять модель поменьше. Память под кэш освобождает только меньшее число параметров (MoE-модели с A3B весят как полная модель — для памяти не помогают). Отличный кандидат — Qwen3.5-9B-AWQ (~6 ГБ): влезает с огромным запасом, и 65k + prefix cache + MTP реально заработают. Цена — качество 9B вместо 27B.

CUDA_VISIBLE_DEVICES=0,1 VLLM_DISABLE_PYNCCL=1 \python -m vllm.entrypoints.openai.api_server \  --model ~/models/Qwen3.5-9B-AWQ --served-model-name qwen35-9b \  --tensor-parallel-size 2 --dtype float16 \  --gpu-memory-utilization 0.90 --max-model-len 65536 \  --enable-prefix-caching \  --trust-remote-code --attention-backend TRITON_ATTN --disable-custom-all-reduce \  --skip-mm-profiling --limit-mm-per-prompt '{"image":0,"video":0}' \  --enable-auto-tool-choice --tool-call-parser qwen3_coder --reasoning-parser qwen3 \  --host 0.0.0.0 --port 8000

Вариант В — 3-я карта. С pipeline-параллелизмом (--pipeline-parallel-size 3 --tensor-parallel-size 1) веса 21 ГБ размазываются по ~7 ГБ на карту, освобождая память под всё сразу: 65k + prefix cache + MTP. Это единственный путь получить «27B + быстрый агент». Нюанс: --tensor-parallel-size 3 для этой модели невалиден (4 KV-головы не делятся на 3), поэтому именно pipeline.

Выводы

1. Современные LLM на старых V100 реально запускаются — спасибо community-форку 1Cat-vLLM и гибридной архитектуре Qwen3.6.

2. Tensor parallelism (--tensor-parallel-size 2) лечит «50% загрузки».

3. На 2×16GB без NVLink есть жёсткая стена: 65k контекста и prefix caching одновременно не помещаются. Это ограничение памяти, а не настроек.

4. Практический выбор: 27B медленно (без кэша), 9B быстро (с кэшем), или 3-я карта — и тогда 27B быстро.

Если ваша задача — фоновый агент в мессенджере, медленный prefill терпим. Если нужен интерактив «как ChatGPT» — берите модель поменьше или добавляйте карту. Железо честно диктует правила.


Конфигурация на момент написания: Proxmox + Ubuntu 22.04/24.04 в VM, 2× Tesla V100-SXM2-16GB (sm_70, x8 PCIe, без NVLink), драйвер 580 / CUDA 13, 1Cat-vLLM 1.1.0, Qwen3.6-27B-AWQ, Hermes Agent.

ссылка на оригинал статьи https://habr.com/ru/articles/1043956/