Две A100X на столе - это не роскошь, а головная боль
Вы купили две A100X. Поставили в систему. Запустили свою тонко настроенную Llama 3.5-405B. И... получили прирост в 15%. Вместо ожидаемых 90+. Знакомо? Виновато не железо, а кривой пайплайн, который душит потенциал ваших монстров на 164 терафлопсах каждый (на FP16, конечно).
Проблема в том, что большинство гайдов по multi-GPU написано для кластеров. Для локальной рабочей станции - своя физика. PCIe полосы, память хоста, латентность между картами и главное - драконовские требования к пайплайну обработки данных. Сегодня разберем, как заставить две A100X петь дуэтом, а не молчать по очереди.
Реальность 2026 года: A100X с архитектурой Hopper Next - это уже не просто ускоритель, а самостоятельный вычислительный остров с 128 ГБ HBM3e памяти и пропускной способностью под 8 ТБ/с. Две таких штуки в одной системе - это мини-суперкомпьютер, который требует соответствующего подхода. Старые методы из эпохи RTX 3090 тут не прокатят.
Диагностика: почему ваш пайплайн тормозит
Прежде чем лезть в настройки, найдите узкое место. Запустите простой тест - он покажет, где спотыкается ваша система.
# Проверяем базовую связность и производительность GPU
nvidia-smi topo -m
# Смотрим пропускную способность PCIe
gpustat -i 1 --color --show-power --show-pcie
# Тест на латентность обмена между GPU (требует установки NCCL Tests)
nccl-tests/build/all_reduce_perf -b 8M -e 128M -f 2 -g 2Если в выводе topo вы видите NODE вместо PIX или PHB между GPU - это уже плохо. Значит, карты сидят на разных NUMA-нодах и общаются через память хоста. Задержки вырастают в разы. В идеале нужен прямой PCIe switch или, как минимум, расположение в соседних слотах x16.
Еще одна частая ошибка - неправильная настройка окружения. PyTorch 2.4+ и TensorFlow 2.17+ научились лучше работать с multi-GPU, но по умолчанию используют стратегии, которые для A100X избыточны или просто неоптимальны.
1 Шаг первый: подготовка фундамента
Не начинайте с Python. Начните с BIOS и операционной системы.
- BIOS/UEFI: Включите Above 4G Decoding. Отключите CSM. Для PCIe установите Gen 4 или Gen 5 (в зависимости от платформы). Если есть настройки Power - выставляйте Maximum Performance.
- ОС: Ubuntu 24.10 или Rocky Linux 9.5. Ядро 6.10+. Никаких лишних графических оболочек - они крадут память и PCIe полосы.
- Драйверы: NVIDIA Driver 570.XX или новее. CUDA 12.6+ (на апрель 2026 - это последняя стабильная ветка). CuDNN 9.2+. Все через официальные репозитории NVIDIA.
# Пример установки стека NVIDIA на Ubuntu 24.10
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2410/x86_64/cuda-keyring_1.1-1_all.deb
sudo dpkg -i cuda-keyring_1.1-1_all.deb
sudo apt update
sudo apt install cuda-toolkit-12-6 cuda-drivers-570 nvidia-fabricmanager-570
# Проверяем
nvidia-smi
python3 -c "import torch; print(torch.cuda.device_count(), torch.cuda.get_device_name(0))"2 Шаг второй: выбор стратегии распределения
Тут все зависит от задачи. Для инференса больших LLM (тех же Qwen3.5-397B или MoE-моделей) нужен tensor parallelism или pipeline parallelism. Для batch-обработки изображений или видео - data parallelism.
| Задача | Лучшая стратегия для 2x A100X | Библиотека/Фреймворк |
|---|---|---|
| Инференс LLM 70B+ параметров | Tensor Parallelism (TP) | vLLM 0.4.7+, TGI, llama.cpp |
| Тонкая настройка моделей 7B-30B | Data Parallelism (DDP) + Gradient Checkpointing | PyTorch FSDP, DeepSpeed |
| Обработка видео (рендеринг, анализ) | Task Parallelism (разные кадры на разные GPU) | Custom pipeline на CUDA Streams |
Для LLM-инференса я сейчас предпочитаю vLLM. Он научился грамотно распределять большие модели по нескольким GPU с минимальными накладными расходами. Пример запуска:
# Запуск Qwen3-72B на двух A100X с tensor parallelism
python -m vllm.entrypoints.api_server \
--model Qwen/Qwen3-72B-Instruct \
--tensor-parallel-size 2 \
--gpu-memory-utilization 0.92 \
--max-model-len 8192 \
--enforce-eager # Избегаем graph capture проблем с некоторыми моделямиНе слепо копируйте настройки из интернета. Параметр --gpu-memory-utilization для A100X с HBM3e можно ставить выше 0.9, но только если у вас нет других нагрузок на GPU. Для стабильной работы под долгими нагрузками лучше 0.85-0.88.
3 Шаг третий: оптимизация пайплайна данных
Самое интересное. Даже правильно распределенная модель будет простаивать, если пайплайн загрузки и обработки данных не синхронизирован. Нужно перекрыть вычисления на GPU с передачей данных и препроцессингом на CPU.
Вот как НЕ надо делать (типичная ошибка):
# ПЛОХО: последовательная загрузка и обработка
for batch in dataloader:
data = preprocess(batch) # CPU
data = data.to('cuda:0') # Передача на GPU 0
result = model(data) # Вычисления на GPU
save_results(result) # CPUА вот правильный подход с использованием CUDA Streams и асинхронных операций:
# ХОРОШО: параллельная работа CPU и GPU
import torch
import torch.cuda.stream as stream
# Создаем отдельные стримы для каждой GPU и для копирования
streams = [torch.cuda.Stream(device=i) for i in range(2)]
copy_streams = [torch.cuda.Stream(device=i) for i in range(2)]
# Буферы для перекрытия вычислений
next_batch = [None, None]
current_batch = [None, None]
with torch.cuda.stream(streams[0]), torch.cuda.stream(streams[1]):
for i, batch in enumerate(dataloader):
# Пока GPU обрабатывают текущий батч, CPU готовит следующий
if i > 0:
# Асинхронная передача следующего батча на GPU
with torch.cuda.stream(copy_streams[0]):
next_batch[0] = next_batch[0].to('cuda:0', non_blocking=True)
with torch.cuda.stream(copy_streams[1]):
next_batch[1] = next_batch[1].to('cuda:1', non_blocking=True)
# Вычисления на GPU для текущего батча
if current_batch[0] is not None:
with torch.cuda.stream(streams[0]):
output0 = model_part1(current_batch[0])
with torch.cuda.stream(streams[1]):
output1 = model_part2(current_batch[1])
# Подготовка следующего батча на CPU (параллельно с GPU вычислениями)
next_batch = preprocess_batch(batch) # Возвращает список для каждой GPU
# Синхронизация перед сменой ролей батчей
torch.cuda.synchronize()
current_batch, next_batch = next_batch, current_batchЭтот паттерн сложнее, но дает прирост 30-50% на задачах, где препроцессинг нетривиален (например, обработка изображений высокого разрешения или токенизация длинных текстов для LLM).
Нюансы, о которых молчат в мануалах
1. Память хоста - ваш враг и друг. На двух A100X с 128 ГБ каждая, у вас может быть 256 ГБ GPU-памяти. Но если в системе меньше 512 ГБ оперативной памяти - вы столкнетесь с свопингом. Особенно при загрузке больших датасетов. Решение - использовать memory-mapped файлы или базы данных вроде RocksDB для потоковой загрузки данных.
2. Тепловой режим. A100X в пассивном режиме (при полной нагрузке на обеих картах) могут разогреть корпус до 50+ градусов. Это приводит к троттлингу памяти HBM3e. Мониторьте температуру не только GPU, но и VRAM:
nvidia-smi --query-gpu=timestamp,name,temperature.gpu,temperature.memory,power.draw --format=csv -l 1Если температура памяти превышает 95°C - производительность начнет падать. Нужно улучшать обдув карт или снижать частоту памяти через nvidia-smi.
3. Проблема с Python GIL. При использовании multi-GPU с data parallelism, каждая GPU обычно работает в отдельном процессе. Но если вы используете multi-threading для загрузки данных - GIL может стать бутылочным горлышком. Переходите на multiprocessing или используйте асинхронные загрузчики типа WebDataset.
Мониторинг и финальная настройка
Настройка без мониторинга - это стрельба вслепую. Установите хотя бы простую дашборд:
# Установка и запуск простого мониторинга Prometheus + Node Exporter + NVIDIA DCGM Exporter
# 1. DCGM Exporter для метрик GPU
docker run -d --rm --name dcgm-exporter \
--runtime=nvidia \
-p 9400:9400 \
nvcr.io/nvidia/k8s/dcgm-exporter:3.5.0-rc.3-ubuntu22.04
# 2. Prometheus конфиг для сбора метрик с хоста и GPU
cat > prometheus.yml << EOF
global:
scrape_interval: 5s
scrape_configs:
- job_name: 'node'
static_configs:
- targets: ['localhost:9100']
- job_name: 'dcgm'
static_configs:
- targets: ['localhost:9400']
EOF
# Запускаем Prometheus
docker run -d --rm --name prometheus \
-p 9090:9090 \
-v $(pwd)/prometheus.yml:/etc/prometheus/prometheus.yml \
prom/prometheus:v3.0.0Смотрите на ключевые метрики: utilization GPU, memory usage, PCIe throughput, и самое главное - kernel execution latency. Если ядра выполняются с большой задержкой - значит, есть contention за ресурсы или проблемы с планированием.
Последний совет: не гонитесь за 100% утилизацией обеих GPU одновременно. В реальных workload'ах идеально сбалансированная нагрузка бывает редко. 85-90% на каждой карте с эффективным пайплайном лучше, чем 100% с постоянными простоями из-за синхронизации.
Что в итоге? Цифры
После полной настройки пайплайна для инференса модели Mixtral 8x22B на двух A100X я получил:
- Tokens per second: с 45 до 78 (прирост 73%)
- Memory usage: 112 ГБ из 128 ГБ на каждой карте (используется эффективно)
- GPU utilization: стабильные 88-92% на обеих картах
- Температура памяти: не выше 92°C при 24°C в помещении
Главный вывод: две A100X - это не просто два раза одна A100X. Это качественно другая система, требующая пересмотра всего workflow. От аппаратного уровня до последней строчки кода в пайплайне данных. Если делать все правильно - вы получаете почти линейный scaling. Если нет - дорогие карты будут работать как одна, да еще и с перегревом. Удачи в настройке!