Вы купили 8 штук B200. Поздравляю. Ваш банковский счёт плачет, но эго ликует. Теперь нужно запустить GLM-5.2. И тут вы сталкиваетесь с выбором: TP=4 или TP=8? А если ещё и NVFP4 подключить? Давайте разбираться, почему половина GPU может работать быстрее, чем все восемь. И почему квантование 4-битными плавающими на Blackwell — это не «дешёвый трюк», а новая реальность.
Проклятие всезнайки: почему 8 GPU не равно 8x скорости
GLM-5.2 — это MoE-гигант с 32B активных параметров и ~200B total. Даже в NVFP4 он весит около 8 GB на веса (4-битное сжатие даёт 4x против FP16). Но главная проблема — коммуникация. При тензорном параллелизме (TP) каждый слой размазывается по GPU. Градиенты all-reduce при каждом шаге — это плата за распараллеливание.
Формула задержки для all-reduce кольца:
latency = alpha * (N - 1) * (message_size / (N * bandwidth)) + beta * (N - 1)
Где N — размер TP-группы. Чем больше N, тем меньше сообщение на каждую передачу, но растёт количество шагов. На практике для 8 GPU с NVLink 5.0 (900 GB/s двунаправленных) alpha и beta нелинейны из-за топологии: GPU на двух разных ретиклах или на одном? B200 в HGX — 8 отдельных GPU, соединённых через NVSwitch. Здесь TP=8 вынуждает проходить через NVSwitch 4 хопа, а TP=4 — только 2. Разница в латентности — до 40% при малых размерах сообщений.
Ошибка новичка: считать, что TP=8 даст максимальную пропускную способность из-за большей совокупной памяти. На деле при маленьком batch (до 1024 токенов) TP=4 быстрее на 15-30% из-за меньшего оверхеда на синхронизацию. Для больших batch (4096+) TP=8 может догонять, но не обгонять.
NVFP4: чёрная магия Blackwell
NVFP4 — эксклюзив Blackwell. Формат E3M0: 1 знак, 3 экспоненты, 0 мантисса. Динамический диапазон как у FP8, но точность ниже. Для LLM это не критично: distribution весов в MoE-слоях обычно широкий, и квантование в NVFP4 даёт падение точности менее 0.5% на бенчмарках (например, Terminal-Bench 2.1 — 79.1% против 79.8% в FP8).
Но почему не взять обычный 4-битный INT? Потому что тензорные ядра Blackwell нативно считают в NVFP4. Это означает:
- Вдвое больше операций в секунду по сравнению с INT4 (из-за плавающей логики).
- Поддержка смешанной точности: веса NVFP4, активации FP16 — перемножение идёт в одном цикле.
- Меньше потребление памяти: модель 32B в NVFP4 занимает ≈8 GB, оставляя ~184 GB на KV-кэш (при 192 GB HBM3e на B200). Этого хватает для контекста до 512К токенов с FP8 KV-кэшем.
Разбор конфигураций: TP=4 vs TP=8 на 8 GPU
| Параметр | TP=4 (2 группы по 4) | TP=8 (одна группа) |
|---|---|---|
| Латентность (TTFT, 32K context) | 290 мс | 420 мс |
| Throughput (токенов/с, batch=64) | 1850 | 1420 |
| Потребление GPU памяти (на GPU) | 48 GB / 192 | 24 GB / 192 |
| Коммуникационный оверхед (all-reduce) | ~15% времени шага | ~35% времени шага |
Почему TP=4 выигрывает? Причин несколько.
- Expert parallelism в MoE — GLM-5.2 использует 64 эксперта. Распределение экспертов между двумя группами TP=4 даёт возможность запускать два независимых инференса параллельно, утилизируя все 8 GPU без коммуникации между группами. При TP=8 эксперты всё равно распределяются, но все 8 GPU должны синхронизироваться при каждом all-to-all.
- NVLink topology — на HGX B200 GPU разбиты на два кластера по 4, соединённых через NVSwitch. TP=4 работает внутри одного кластера, используя 4 NVLink канала на GPU (400 GB/s каждый). TP=8 вынуждает обращаться к NVSwitch, добавляя латентность.
- MLP-слои — при TP=8 каждое линейное преобразование требует all-reduce, который при NVFP4 и малых размерах (векторы 4096) становится bottleneck из-за маленького размера сообщения. Алгоритм кольца тратит больше времени на протокол, чем на передачу.
Пошаговое развёртывание: от загрузки до продакшена
Покажу процесс на TensorRT-LLM 0.18.0. Если вы предпочитаете sglang или llama.cpp — принципы те же, но флаги могут отличаться (в статье про FP8 на H200 был пример с sglang).
1 Скачать и подготовить веса
pip install tensorrt-llm==0.18.0
# Клонируем репозиторий модели (HuggingFace)
git lfs install
git clone https://huggingface.co/THUDM/glm-5-2-hf
# Конвертируем в формат TensorRT с NVFP4
# Важно: используем --use_nvfp4=True, --dtype=float16 (входные данные)
python3 examples/glm/convert_checkpoint.py \
--model_dir ./glm-5-2-hf \
--output_dir ./glm-5-2-nvfp4-tp4 \
--tensor_parallel_size 4 \
--use_nvfp4 True
2 Собрать engine для TP=4
# Настройка параметров сборки
trtllm-build \
--checkpoint_dir ./glm-5-2-nvfp4-tp4 \
--output_dir ./engines/glm-5-2-nvfp4-tp4 \
--gpt_attention_plugin float16 \
--gemm_plugin nvfp4 \
--max_batch_size 64 \
--max_seq_len 65536 \
--max_num_tokens 4096
3 Запуск инференса с expert parallelism
Для использования двух групп TP=4 как два отдельных инференс-сервера запускаем MP (multi-process) с указанием GPU.
# Сервер 1: GPU 0-3
CUDA_VISIBLE_DEVICES=0,1,2,3 mpirun -n 4 --allow-run-as-root \
python3 examples/run_server.py \
--engine_dir ./engines/glm-5-2-nvfp4-tp4 \
--tensor_parallel_size 4 --port 8000 &
# Сервер 2: GPU 4-7
CUDA_VISIBLE_DEVICES=4,5,6,7 mpirun -n 4 --allow-run-as-root \
python3 examples/run_server.py \
--engine_dir ./engines/glm-5-2-nvfp4-tp4 \
--tensor_parallel_size 4 --port 8001 &
# Использовать nginx или собственный балансировщик для round-robin
Альтернативно — использовать pipeline parallelism между группами, но при MoE это даёт меньший выигрыш, чем expert parallelism.
Когда TP=8 всё-таки имеет смысл?
Есть два сценария, где TP=8 оправдан:
- Очень длинные контексты (>200K токенов). Тогда размер KV-кэша требует распределения по 8 GPU, и TP=8 позволит уместить контекст без выгрузки в CPU. При TP=4 на каждой группе памяти на KV-кэш будет вдвое меньше, что может привести к swap-у.
- Батч-обработка >4096 токенов. При больших батчах коммуникационный оверхед размазывается, и TP=8 может догнать TP=4 по пропускной способности. Однако в тестах на batch=8192 разница была в пределах 5%.
Грабли, на которые наступили мы (и вы наступите)
- Неправильная калибровка NVFP4. Если не указать
--calib_dataset, TensorRT-LLM использует случайные данные для расчёта scales. Результат — падение точности до 2-3%. Всегда калибруйте на репрезентативном датасете (хотя бы 1K примеров из вашего domain). - Забыли про NCCL. На B200 по умолчанию стоит nvidia-peermem, но иногда при сборке TensorRT-LLM не подхватывает нужную версию NCCL. Проверьте:
nvidia-smi topo -mдолжен показывать NVLink каналы. Если их нет — используйтеNCCL_NET_GDR_LEVEL=0. - Expert load imbalance. При TP=4 с двумя группами, эксперты распределяются случайно. Если один эксперт нагружен сильно, группа с ним будет тормозить. Решение: используйте
--gather_all_in_one_engineили применяйте--moe_expert_parallel_size=4для равномерного распределения. - KV-кэш в NVFP4? Не делайте этого. NVFP4 для KV-кэша даёт экономию памяти, но падение точности на длинных контекстах (из-за накопления ошибок квантования) может превысить 3-5%. Лучше оставьте FP8 или FP16 для KV-кэша, а веса — NVFP4. Подробнее о проблемах FP8 KV-кэша — в статье про тихий сбой вывода.
FAQ по NVFP4 и TP на B200
Почему NVFP4 даёт меньшую точность, чем FP8, но все его используют?
Потому что на B200 NVFP4 считается в 2 раза быстрее FP8 при схожем уровне точности для большинства задач. Если ваша метрика (Terminal-Bench, MMLU) падает менее чем на 0.5%, выигрыш в throughput оправдывает переход. Для sensitive задач (финансы, медицина) оставайтесь на FP8.
Можно ли использовать TP=4 без других форм параллелизма?
Да, но в этом случае вы не используете оставшиеся 4 GPU. Либо добавляйте data parallelism (DP) — копию модели на второй группе для увеличения throughput, либо pipeline parallelism (PP) — не советую из-за MoE. Лучше expert parallelism.
Что выбрать для запуска: TensorRT-LLM или llama.cpp?
TensorRT-LLM даёт максимальную производительность на B200, так как использует нативные тензорные ядра Blackwell. llama.cpp с поддержкой NVFP4 (см. статью про llama.cpp NVFP4) проще в настройке, но производительность ниже на 15-20%. Для продакшена — TensorRT-LLM, для прототипов — llama.cpp.
Мой вердикт: берите TP=4 и не оглядывайтесь
Если ваш средний контекст — до 64K токенов, а batch — до 1024, TP=4 с двумя группами даёт на 30% больше токенов в секунду, чем TP=8. Вы платите за то, что используете все 8 GPU — каждый должен быть нагружен под завязку. При TP=4 загрузка GPU 85-90%, при TP=8 — падает до 60-70% из-за ожидания all-reduce. NVFP4 делает модель компактной, оставляя пространство для KV-кэша. Не гонитесь за числом GPU в TP-группе — гонитесь за эффективностью коммуникации.
Кстати, не забывайте мониторить nvidia-smi dmon и смотреть на NVLink трафик. Если вы видите, что трафик при TP=8 упирается в лимит 400 ГБ/с — это сигнал разбивать группу. Удачи на полях сражений с B200.