Распределённое обучение на GPU-кластере: анатомия OOM и предел вертикального масштабирования
Запуск современной модели на 70 миллиардов параметров съедает около 140 гигабайт памяти только под веса в формате fp16. Добавьте к этому градиенты, состояния оптимизатора AdamW, которые для сохранения числовой стабильности часто держат в полной точности float32, и вам потребуется уже под 600 гигабайт. Попытка впихнуть это в одну карточку обречена на провал, даже если это флагманская 80-гигабайтная A100. Наивный подход подсказывает арендовать восемь таких карт и запустить скрипт, но тут многих ждет сюрприз: пропускная способность сети внезапно падает, а утилизация GPU болтается на уровне тридцати процентов. В итоге распределённое обучение на GPU-кластере превращается в упражнение по сжиганию денег, когда аренда железа стоит тысячи долларов в сутки, а обучение тянется неделями.
Для понимания масштаба катастрофы давайте посчитаем честную математику памяти. В половинной точности bfloat16 веса 30-миллиардной модели займут около 60 гигабайт. AdamW требует хранить моментумы и вариации для каждого параметра, что сразу накидывает еще 120 гигабайт. Добавляем память под градиенты — еще 60 гигабайт. Плюс активации для backward pass, которые напрямую зависят от размера батча и длины контекста, и легко могут сожрать еще пару сотен гигабайт. Суммарно мы получаем потребность в четырехстах гигабайтах видеопамяти просто для того, чтобы модель могла сделать один шаг градиентного спуска. Наивная попытка использовать градиентную аккумуляцию здесь не спасет: она уменьшает потребление памяти активациями, но фундаментальные требования к весам и состояниям оптимизатора остаются неизменными. Раз модель не лезет в карту, ее надо резать.
Тензорный и конвейерный параллелизм: когда интерконнект решает всё
Исторически первым решением, показавшим индустриальную эффективность, стал тензорный параллелизм. Суть подхода заключается в разрезании самих матриц линейных слоев. Вместо того чтобы вычислять всю операцию на одном чипе, мы делим матрицу весов колоночно на первой половине карт и построчно на второй. Каждая видеокарта делает лишь часть вычислений, но чтобы получить финальный результат слоя, им необходимо синхронизироваться через операцию All-Reduce. Звучит математически безупречно, пока вы не посмотрите на тайминги. Операция All-Reduce должна выполняться после каждого трансформерного блока. Если ваши карты стоят внутри одного физического сервера, они связаны высокоскоростной шиной NVLink, способной прокачивать сотни гигабайт в секунду. В таком сценарии тензорный параллелизм работает великолепно. Но стоит вам растянуть тензорный параллелизм между разными физическими серверами, как вы мгновенно упретесь в сеть. Даже топовый адаптер на 400 гигабит в секунду — это всего лишь около 50 гигабайт в секунду теоретического предела, что на порядок медленнее NVLink. Карты просто встанут, ожидая данные друг от друга.
Чтобы обойти ограничения интерконнекта между узлами, инженеры применяют конвейерный параллелизм (Pipeline Parallelism). Модель режется горизонтально: первые слои трансформера ложатся на первый узел, средние на второй, финальные на третий. Общение между узлами происходит только на границах разрезов. Сетевой трафик минимален, пересылаются лишь тензоры активаций. Однако конвейер порождает чудовищную проблему простоя железа. Пока первый узел обрабатывает батч, остальные ждут. Когда данные доходят до последнего узла, первый уже простаивает. Чтобы заставить этот завод работать непрерывно, батч рубят на десятки микробатчей и запускают хитрое расписание прямых и обратных проходов. На бумаге это минимизирует «пузыри» в пайплайне, но на практике ручная балансировка слоев съедает львиную долю времени.
3D-параллелизм и пришествие FSDP: размазывая состояния по кластеру
Когда амбиции доходят до моделей с сотнями миллиардов параметров, в ход идет 3D-параллелизм. Это жесткая суперпозиция всех трех методов: Tensor, Pipeline и Data Parallelism. Тензорный параллелизм жестко запирают исключительно внутри одного физического сервера, чтобы All-Reduce бегал только по быстрым NVLink. Конвейерный параллелизм натягивают между серверами, чтобы минимизировать нагрузку на сеть. А поверх этого добавляют параллелизм по данным. Настройка 3D-параллелизма — это искусство, где неверно выбранный размер микробатча или неоптимальный сплит пайплайна ломает всю экономику проекта.
На фоне этой головной боли с ручной нарезкой архитектуры появление FSDP (Fully Sharded Data Parallel) стало откровением. Алгоритм реализует концепции, заложенные в семействе ZeRO от Microsoft. Философия предельно цинична: оперативная память GPU — самый дорогой ресурс, а значит, нужно размазать состояния по всем участникам кластера. Модель больше не хранится целиком ни на одной карте. Каждая GPU держит только свой крошечный сегмент весов, градиентов и состояний оптимизатора. В момент вычисления forward pass карточки собирают полные веса текущего слоя через высокоскоростной All-Gather, перемножают матрицы, и тут же вычищают чужие веса из памяти, чтобы освободить место под следующий слой.
Когда мы в Morana Labs поднимали кластер под обучение 70B модели для одного энтерпрайз-заказчика, нам пришлось пережить немало боли именно с тюнингом FSDP из-за нюансов инфраструктуры. Кластер сидел на весьма посредственной сети без идеальной неблокирующей топологии. FSDP меняет проблему нехватки памяти на проблему огромного сетевого трафика. Если не настроить агрессивный prefetching, когда сеть скачивает следующий слой параллельно с обсчетом текущего, ваши дорогие GPU превратятся в калькуляторы, ждущие загрузки файлов по сети.
Сетевая инфраструктура как фундамент: RoCEv2, Incast и дропы пакетов
Сеть — это то самое ребро, о которое ломается большинство теоретически красивых ML-пайплайнов. Многие верят, что достаточно объединить сервера свитчами на 100-400 гигабит и абстракции фреймворка всё сделают сами. Реальность суровее. Для FSDP критична латенси и отсутствие дропов пакетов. Когда сотни карт одновременно делают All-Gather, сеть испытывает микроберсты трафика (incast), которые мгновенно переполняют буферы коммутаторов.
Начинаются потери пакетов. Если используется RDMA over Converged Ethernet (RoCEv2), потеря одного пакета приводит к откату окна передачи и лавинообразному падению пропускной способности. Настройка Priority Flow Control (PFC) и Explicit Congestion Notification (ECN) в сетевом оборудовании становится важнее, чем тюнинг гиперпараметров самой нейросети. Узел может выдавать потрясающие TFLOPS в синтетике, но просадка p99 tail latency на сети всего на несколько миллисекунд замедлит весь синхронный шаг кластера. Железо не прощает абстракций.
Отказоустойчивость к падению нод: асинхронные чекпойнты и горячий резерв
Отдельный жанр производственной драмы — отказоустойчивость. Обучение большой языковой модели или тяжелой мультимодальной архитектуры занимает недели. На масштабах в сотни видеокарт вероятность того, что хотя бы один сервер прикажет долго жить, стремится к единице. Отваливается планка памяти, перегревается VRM-зона питания на материнской плате, случается ECC-ошибка в VRAM, рвётся сетевой линк, падает драйвер. Если распределённая система не готова к падению нод, вы теряете недели прогресса и десятки тысяч долларов за простой арендованных мощностей из-за одного битого сектора.
Спасаться частыми чекпойнтами — идея здравая, но технически сложная. Сбросить терабайт весов и состояний с сотен нод на распределенную файловую систему синхронно означает заморозить кластер на несколько минут. Делать это каждый час слишком накладно. Время — деньги, в прямом смысле почасовой аренды. Поэтому внедряется асинхронное чекпойнтирование. В момент синхронизации распределенный процесс делает in-memory snapshot состояний прямо в оперативной памяти каждого сервера. Графические ускорители немедленно возвращаются к работе над следующим батчем, а фоновый процесс CPU начинает неспешно переливать эти гигабайты по сети в постоянное объектное хранилище вроде S3 или Ceph.
При падении узла критично время реакции оркестратора. В правильном сетапе оркестратор фиксирует отвал heartbeat-сигнала, принудительно гасит зависшие процессы на оставшихся живых нодах, изолирует сбойный сервер и вводит в строй горячий резерв. Горячий резерв — это серверы, которые стоят включенными и просто ждут своего часа. Это часто вызывает истерику у финансистов, которые видят оплату за простаивающее железо. Но арифметика неумолима: сутки простоя всего кластера в ожидании ручного дебага и перенастройки топологии стоят кратно дороже, чем аренда пары резервных машин на месяц. После подмены узла пайплайн автоматически подтягивает последний асинхронный чекпойнт и продолжает обучение с потерей максимум получаса вычислительного времени.
Выбор стратегии параллелизма диктуется профилем железа, а не статьями на хайповых ресурсах. Для работы в пределах пары нод с отличным интерконнектом FSDP выжимает максимум из памяти без сложной инженерии. Если масштаб вырастает до десятков серверов, а сеть не поддерживает RDMA на нужных скоростях, придется разрезать модель пайплайнами, забирая часть ресурсов на борьбу с пузырями ожидания. 3D-параллелизм остается уделом гигантских LLM, где стоимость железа оправдывает месяцы работы инфраструктурщиков над балансировкой графа. Либо вы контролируете каждый байт, идущий через PCIe и сетевые интерфейсы, либо ваш дорогой кластер работает с эффективностью игрового ПК.