English translation is not available yet. Showing Russian content.

Сравнить производительность NCCL ring и tree алгоритмов на разном масштабе GPU

ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Сравнить производительность NCCL ring и tree алгоритмов на разном масштабе GPU

1. Цель задачи

Провести количественное сравнение алгоритмов коллективных коммуникаций NCCL (ring и tree) на бенчмарке allreduce для разного числа GPU (2–64) и размеров сообщений (256 Б – 256 МБ). Определить точку перехода, когда tree-алгоритм начинает превосходить ring по пропускной способности, и объяснить причину (топология, число узлов, латентность). Полученные знания позволят выбирать оптимальный алгоритм при конфигурации распределённого обучения на масштабе >32 GPU.

Ключевой результат Набор графиков и таблица с критическими значениями числа GPU и размера сообщения, при которых tree быстрее ring.

2. Исходные данные

Что нужноОткуда взять
Кластер с GPU (минимум 2 GPU, желательно 4+ на узел, несколько узлов)Собственный, арендованный (AWS p4d/p5, Lambda Cloud) или доступ к суперкомпьютеру
NVIDIA NCCL (установлен, версия ≥ 2.14)apt install nvidia-nccl или из контейнера nvidia/cuda
nccl-tests (набор бенчмарков)Git-репозиторий: https://github.com/NVIDIA/[nccl-tests](/wiki/nccl-tests)
MPI (mpiexec) для запуска на нескольких процессахOpenMPI / MPICH, устанавливается системно
Python с matplotlib, numpy, pandasокружение conda/pip
Документация NCCLhttps://docs.nvidia.com/deeplearning/[nccl](/wiki/NCCL)/user-guide/

Если нет реального кластера с >8 GPU — симулируем:

  1. Запустить nccl-tests на доступном числе GPU (например, 2, 4, 8) на одной или двух нодах.
  2. Для масштабов 16, 32, 64 использовать открытые данные из бенчмарков NVIDIA (например, графики из NCCL user guide) или эмпирические формулы:
  3. Сравнить измеренные и теоретические кривые, сделать вывод о точке пересечения.

3. Технологический стек

КомпонентИнструментыНазначение
Бенчмаркинг allreducenccl-tests (allreduce_perf)Замер пропускной способности и латентности
Оркестрация процессовOpenMPI (mpiexec)Запуск нескольких рангов на разных GPU/узлах
Выбор алгоритма NCCLПеременные окружения NCCL_ALGO=RING / TREEПринудительная установка алгоритма
Сбор и анализ данныхPython (subprocess, re, pandas, matplotlib)Парсинг вывода, построение графиков
ВизуализацияJupyter Notebook или Python-скриптГрафики bandwidth vs #GPU, latency vs size
Опционально: профилированиеNVIDIA Nsight Systems (nsys)Подробный трейс вызовов NCCL

4. Этапы выполнения

Этап 1: Подготовка окружения и сборка nccl-tests (30 минут)

Действия

  1. Установить зависимости (если не установлены):

    sudo apt update && sudo apt install openmpi-bin openmpi-common libopenmpi-dev
    

    Проверить наличие CUDA Toolkit и NCCL:

    nvcc --version
    ldconfig -p | grep nccl
    
  2. Клонировать и собрать nccl-tests

    git clone https://github.com/NVIDIA/nccl-tests.git
    cd nccl-tests
    make
    

    Бинарник build/allreduce_perf будет использоваться.

  3. Проверить топологию GPU (важно для понимания ограничений):

    nvidia-smi topo -m
    

    Записать количество узлов и связи (NVLink, PCIe, межузловая сеть).

  4. Подготовить скрипт для запуска (шаблон):

    # examples/run_bench.sh
    #!/bin/bash
    ALGO=$1          # RING или TREE
    NGPU=$2          # общее число GPU
    NNODES=$3        # число узлов (для межузловых тестов)
    mpirun -np $NGPU -N 8 --host node1:8,node2:8 \
        -x NCCL_ALGO=$ALGO \
        -x NCCL_DEBUG=WARN \
        build/allreduce_perf -b 256 -e 256M -f 2 -g 1 -n 100
    

Ожидаемый результат этапа Рабочий скрипт запуска, подтверждение, что allreduce_perf выполняется на 2 GPU без ошибок.

Этап 2: Проведение бенчмарков (1–1.5 часа)

Действия

  1. Составить список конфигураций

    • Число GPU: 2, 4, 8 (обязательно), затем 16, 32, 64 (если доступно или симулируем).
    • Для каждого числа GPU запустить RING и TREE отдельно.
    • Для каждой комбинации выполнить 3 запуска для устранения случайных флуктуаций.
  2. Команда запуска (пример для 4 GPU на одном узле):

    NCCL_ALGO=RING mpiexec -np 4 ./build/allreduce_perf -b 256 -e 256M -f 2 -n 100 -g 1
    

    Ключевые опции:

    • -b 256 – начальный размер (Б)
    • -e 256M – конечный размер (256 MБ)
    • -f 2 – множитель (каждый шаг ×2)
    • -n 100 – число итераций для прогрева/усреднения
    • -g 1 – одно сообщение на вызов
  3. Собрать выводы в файлы

    mkdir -p results
    for N in 2 4 8; do
        for algo in RING TREE; do
            NCCL_ALGO=$algo mpiexec -np $N ./build/allreduce_perf -b 256 -e 256M -f 2 -n 100 -g 1 \
                | tee results/${algo}_${N}gpu.log
        done
    done
    
  4. Симуляция для 16, 32, 64 GPU (если нет реального доступа):

    • Запустить теоретическую модель в Python (см. Этап 3) или взять данные из официальных бенчмарков NCCL (например, графики bandwidth on DGX-2/DGX A100).
    • Загрузить CSV с гипотетическими данными (см. прилагаемый файл-шаблон).

Ожидаемый результат этапа Директория results/ с лог-файлами для всех доступных конфигураций.

Этап 3: Парсинг результатов и анализ (45 минут)

Действия

  1. Структура лога (фрагмент):

    #                                                      out-of-place                       in-place          
    #       size         count      type   redop    time   algbw   busbw  error   time   algbw   busbw  error
    #        (B)    (count)                               (us)  (GB/s)  (GB/s)           (us)  (GB/s)  (GB/s)
         256             64     float     sum   100.0    8.00    0.03    0.03      0    7.00    0.03    0.03      0
    

    Нужные поля: size, time (us), algbw (GB/s), busbw (GB/s). Используем busbw как метрику пропускной способности шины.

  2. Написать парсер на Python

    import re, pandas as pd
    def parse_log(filepath):
        data = []
        with open(filepath) as f:
            for line in f:
                if line.startswith('#'): continue
                parts = re.split(r'\s+', line.strip())
                if len(parts) >= 8:
                    size = int(parts[0])
                    time_us = float(parts[4])
                    busbw = float(parts[6]) if parts[6] != '-' else 0
                    data.append({'size': size, 'time_us': time_us, 'busbw': busbw})
        return pd.DataFrame(data)
    
  3. Для симуляции на >8 GPU дополнить DataFrame расчётными данными:

    • Использовать формулы (например, из NCCL documentation):
      • Ring: busbw = (2 * (N-1) / N) * peak_bandwidth * link_efficiency
      • Tree: busbw = (2 / log2(N)) * peak_bandwidth * link_efficiency
    • Принять peak_bandwidth = 600 GB/s (NVLink A100), link_efficiency = 0.9.
  4. Агрегировать данные Для каждого размера и алгоритма усреднить по трём запускам, построить таблицу.

Ожидаемый результат этапа Pandas DataFrame со столбцами: algo, ngpu, size, busbw_avg, time_avg.

Этап 4: Визуализация и выявление точки перехода (45 минут)

Действия

  1. Построить график "bandwidth vs #GPU" для фиксированного размера сообщения (например, 1 МБ и 256 МБ):

    import matplotlib.pyplot as plt
    # Пример для 1MB
    sizes_fixed = [1*1024*1024]
    for sz in sizes_fixed:
        df_sub = df_all[df_all['size'] == sz]
        fig, ax = plt.subplots(figsize=(10,6))
        for algo in ['RING','TREE']:
            data = df_sub[df_sub['algo']==algo].sort_values('ngpu')
            ax.plot(data['ngpu'], data['busbw_avg'], marker='o', label=algo)
        ax.set_xlabel('Number of GPUs')
        ax.set_ylabel('Bus bandwidth (GB/s)')
        ax.legend()
        plt.title(f'Allreduce bandwidth vs #GPUs (size={sz} B)')
        plt.savefig(f'bw_vs_gpu_{sz}.png')
    
  2. Для каждого размера сообщения определить точку пересечения (когда tree впервые ≥ ring):

    • Найти минимальное ngpu, при котором tree_busbw > ring_busbw.
    • Свести в таблицу:
      Size (B)N_thresholdПримечание
      25632латентность доминирует
      1M16шина выравнивается
  3. Построить контурную карту "алгоритм против размера и числа GPU" (тепловая карта разницы tree_busbw - ring_busbw).

  4. Сформулировать вывод

    • Для мелких сообщений (< 256 КБ) ring чаще быстрее из-за меньшей латентности.
    • Для крупных сообщений (> 4 МБ) tree выигрывает, начиная с 16–32 GPU, особенно при межузловой связке.
    • Tree даёт преимущество, когда число GPU превышает ширину одного узла (например, 8 на DGX-1) и возникает необходимость в древовидной редукции по кольцу узлов.

Ожидаемый результат этапа 3–4 графика (bandwidth vs #GPU для 2–3 размеров; тепловая карта) и итоговая таблица с пороговыми значениями.

Этап 5: Оформление отчёта (20 минут)

Действия

  1. Подготовить Jupyter Notebook с кодом всех этапов, встроенными графиками и текстовыми выводами.
  2. Сохранить финальную таблицу результатов в CSV: ncc_algorithm_crossover.csv.
  3. Написать краткое заключение (1–2 абзаца): при каком масштабе какой алгоритм рекомендуется, с оговорками.

Ожидаемый результат этапа Завершённый notebook, CSV с данными.

5. Критерии приемки (Definition of Done)

  • Все бенчмарки запущены минимум для 2, 4 и 8 GPU (или симулированы).
  • Для каждого запуска собраны данные по размерам от 256 Б до 256 МБ.
  • Парсер корректно извлекает busbw и time_us из логов.
  • Построены минимум два графика: bandwidth vs #GPU для двух размеров сообщений.
  • Выявлена точка перехода (число GPU) для каждого размера сообщения.
  • Таблица с порогами записана в CSV.
  • Сформулирован вывод с объяснением (влияние топологии, размера сообщения).
  • Код воспроизводим: указаны exact-команды и версии зависимостей в notebook.

6. Ожидаемый результат

Артефакты

  • analysis.ipynb — Jupyter Notebook с кодом, графиками и выводами.
  • results/ — папка с сырыми логами (если реальные запуски).
  • ncc_algorithm_crossover.csv — таблица пороговых значений (size, N_threshold, preferred_algo).
  • (Опционально) PDF-отчёт.

Содержимое notebook

  • Импорт библиотек, функции парсера.
  • Построение графиков для всех доступных данных (с метками RING/TREE).
  • Сравнение с теоретической моделью (если симулировали).
  • Итоговая таблица точки перехода.

7. Возможные сложности и их решение

СложностьРешение
Нет доступа к кластеру с >8 GPUИспользовать измерения на 2–8 GPU + теоретические кривые из документации NCCL; загрузить публичные данные из NVIDIA Developer Blog.
Ошибки при запуске mpiexec (память, драйвер)Убедиться, что все GPU видны (nvidia-smi), проверить CUDA_VISIBLE_DEVICES, использовать -x CUDA_VISIBLE_DEVICES=0,1,....
Разброс результатов между запусками >10%Увеличить число итераций (-n 1000), проверить загрузку системы (нет фоновых процессов).
Несовместимость MPI и NCCLИспользовать mpich вместо OpenMPI, или запускать без MPI через NCCL_ALGO с CUDA_VISIBLE_DEVICES и несколькими процессами на одной ноде.
Значения busbw равны 0 (ошибка в парсинге)Проверить формат лога: иногда выводится - вместо чисел; использовать try/except, искать столбец Gbps.

8. Бюджет времени (оценка)

ЭтапВремя (часы)
Подготовка окружения и сборка nccl-tests0.5
Проведение бенчмарков (2–8 GPU, 2 алгоритма)1.0
Симуляция для >8 GPU (Python модель + сбор данных)1.0
Парсинг, визуализация, анализ1.0
Оформление отчёта0.5
Итого3.5 – 4.0

Примечание: для первого выполнения (включая изучение документации) выделите 4 часа. Если кластер и окружение уже готовы, время сокращается до 2.5 часов.

9. Связанные вопросы из базы знаний

ВопросТема
101Принципы работы коллективных операций (allreduce, allgather)
205Топология GPU (NVLink, PCIe, межсоединения)
307Профилирование NCCL с помощью Nsight Systems
412Влияние размера сообщения на выбор алгоритма (ring vs tree vs other)
528Многопоточная обработка allreduce с MPI+OpenMP
634Пропускная способность InfiniBand и её влияние на tree
711Переменные окружения NCCL: NCCL_ALGO, NCCL_PROTO, NCCL_IB_HCA
825Сравнение производительности allreduce на одном узле (NVLink) и между узлами
901Масштабирование распределённого обучения (DDP, FSDP) и роль коллективных коммуникаций

10. Чек-лист самопроверки

  • Я клонировал и собрал nccl-tests без ошибок.
  • Я запустил тесты минимум для 2, 4, 8 GPU с обоими алгоритмами.
  • Логи сохранены и парсятся корректно (busbw извлекается).
  • Для масштабов >8 GPU я использовал симуляцию или публичные данные.
  • Я построил график bandwidth vs #GPU для хотя бы одного размера сообщения.
  • Я определил точку перехода (количество GPU) для каждого размера.
  • В выводе явно указано: при каком числе GPU и размере сообщения tree быстрее ring.
  • Я сохранил notebook и CSV с результатами.
  • Воспроизводимость обеспечена: указаны версии инструментов и команды запуска.