LLMs mit Multi-GPU- und Multi-Node-Parallelism skalieren

Automatische Übersetzung Dieser Artikel wurde automatisch aus der englischen Originalversion übersetzt.

Workloads mit großen Models gehen aus verschiedenen Gründen über eine einzelne GPU hinaus. Bei einem Training Job reicht der Speicher möglicherweise für den Optimizer State nicht aus. Bei einem anderen belegen Activations für lange Sequenzen den gesamten Speicher. Ein Model, das in den Speicher passt, verfehlt möglicherweise trotzdem das Throughput-Ziel. Jedes Problem erfordert eine andere Partitionierung und ein anderes Communication Pattern.

Dieser praktische Überblick behandelt die wichtigsten Parallelism-Strategien und die dahinterliegenden Einschränkungen – basierend auf dem Ultra-Scale Playbook von Hugging Face. Ziel ist zu zeigen, welchen Nutzen jeder Split bringt, welche Daten dabei kommuniziert werden und wann Kombinationen erforderlich werden.

TL;DR. Repliziertes Data Parallelism erhöht den Training-Throughput, wenn eine Replica in den Speicher passt. Fully Sharded Data Parallelism partitioniert den Model State, fügt jedoch Parameter-All-Gathers und Gradient-Reduce-Scatters hinzu. Tensor, Pipeline, Context und Expert Parallelism teilen die Layer-Mathematik, die Tiefe, die Sequenz beziehungsweise MoE-Layer auf. Kombinieren Sie diese Verfahren erst, nachdem Sie den maßgeblichen Memory- oder Communication Bottleneck identifiziert haben.

Dieser Leitfaden setzt voraus, dass Sie mit Backpropagation, Transformer-Layern und einem standardmäßigen PyTorch Training Loop vertraut sind.

Mit zwei Memory Budgets beginnen

Training und Inference haben nicht denselben Footprint.

training peak ≈ parameters
              + gradients
              + optimizer state
              + saved activations
              + temporary buffers
              + communication buffers
              + allocator headroom

inference peak ≈ resident weights
               + key/value (KV) cache
               + runtime workspace
               + communication buffers
               + allocator headroom

Ein Model mit 70 Milliarden Parametern benötigt allein für BF16 Weights mindestens 140 GB (dezimal). Diese Zahl sagt wenig über Training aus, bei dem Gradients, Optimizer State, Master Weights und Activations dominieren können. Auch das Serving wird dadurch nicht dimensioniert: Hier sind Cache Policy, Sequenzlänge, Batch Concurrency und Quantization entscheidend.

Profilen Sie die exakte Architektur, Precision, Sequenzlänge, Micro-Batch-Größe, den Optimizer, die Checkpointing Policy und den Runtime. Erfassen Sie den maximal belegten und reservierten Speicher, Tokens pro Sekunde, die Zeit in Kernels sowie die Zeit, die in Collectives sichtbar ist.

Distributed Scaling beginnt bei einem gemessenen BottleneckDistributed Scaling beginnt bei einem gemessenen Bottleneck

Jede Parallelism-Dimension ist ein Trade-off

Fragen Sie für jede Strategie, welche Tensor-Dimension gesplittet wird, welcher State repliziert bleibt und welches Collective in den Critical Path gelangt.

StrategieSplitsPrimäre EntlastungEingeführte Kommunikation
Repliziertes Data ParallelismBatchTraining-ThroughputGradient-All-Reduce
Fully Sharded Data ParallelismParameter, Gradients und Optimizer State über eine Data-Parallel-(DP-)GruppeModel-State-MemoryParameter-All-Gather, Gradient-Reduce-Scatter
Tensor ParallelismMatrix- oder Attention-Dimensionen innerhalb von LayernLayer-Weights und ActivationsCollectives innerhalb von Transformer-Blöcken
Pipeline ParallelismLayer-GruppenModel-Tiefe und State pro StagePoint-to-Point-Activations plus Scheduling Bubbles
Context ParallelismSequenzdimensionActivation Memory bei langen SequenzenKey/Value- oder Attention-Austausch über die Sequenzgruppe
Expert ParallelismMoE-Experten und geroutete TokensExpert Capacity pro RankToken Dispatch und Combine, typischerweise All-to-All

Die Memory-Entlastung ist kein fester Multiplikator. Sie hängt vom Sharding-Grad, vom weiterhin replizierten State, vom temporär ungeshardeten State, von der Activation Policy, vom Padding, von Imbalance und von Buffers ab.

Repliziertes Data Parallelism: Throughput ohne zusätzliche Capacity

Repliziertes Data-Parallel-TrainingRepliziertes Data-Parallel-Training

Repliziertes Data Parallelism, meist Distributed Data Parallel genannt, hält auf jedem Rank eine vollständige Training Replica. Die DistributedDataParallel-Dokumentation von PyTorch beschreibt dieses Replica-and-Gradient-Synchronization-Modell. Jeder Rank verarbeitet einen anderen Micro-Batch, und die Gradients werden vor dem Optimizer Step synchronisiert.

Verwenden Sie es, wenn der vollständige Training State mit ausreichendem Headroom in den Speicher passt und der globale Batch vergrößert oder Gradient Accumulation angepasst werden kann. Die wichtigsten Vorteile sind eine einfache Semantik und eine ausgereifte Implementierung, die die Backward-Berechnung mit der bucket-basierten Gradient Reduction überlappt.

Das Hinzufügen weiterer Ranks kann schaden, wenn der lokale Batch zu klein wird. Es kann ebenfalls schaden, wenn das Netzwerk das All-Reduce nicht verbergen kann oder die Input-Zufuhr ins Stocken gerät. Der gewünschte Optimization Batch skaliert möglicherweise nicht mit.

Fully Sharded Data Parallelism: State Memory gegen Collectives

Ausführung von Fully Sharded Data ParallelismAusführung von Fully Sharded Data Parallelism

Fully Sharded Data Parallelism speichert Parameter-, Gradient- und Optimizer-Shards über eine Gruppe verteilt. Das ZeRO-Paper beschreibt dieses State-Sharding-Muster, und die FSDP2 API von PyTorch implementiert es. Die Parameter eines Layers werden für die Berechnung per All-Gather zusammengeführt und können anschließend erneut geshardet werden. Gradients werden per Reduce-Scatter an die jeweiligen Owner zurückverteilt.

Die aktuelle PyTorch-Dokumentation unterscheidet die fully_shard API in Fully Sharded Data Parallelism Version 2 (FSDP2) vom älteren FullyShardedDataParallel Wrapper. FSDP2 gruppiert die Kommunikation nach den Modulen, auf die fully_shard angewendet wird, und empfiehlt eine Anwendung von unten nach oben, damit Layer-Gruppen Kommunikation und Berechnung überlappen können.

from torch.distributed.fsdp import fully_shard
from torch.optim import AdamW

# Apply bottom-up: each block becomes a communication group.
for block in model.transformer.blocks:
    fully_shard(block)

# Shard remaining root parameters such as embeddings and output projection.
fully_shard(model)

# Construct the optimizer after parameters have become sharded distributed tensors (DTensors).
optimizer = AdamW(model.parameters(), lr=learning_rate)

Dies ist ein struktureller Sketch, kein vollständiger Launcher. Device Meshes, Mixed Precision, Checkpointing, Initialisierung, Optimizer State und Distributed Checkpoints müssen zum Training Stack passen.

Sharding ist attraktiv, wenn der Model State der maßgebliche Constraint ist und die Layer-Berechnung ausreichend Collective Traffic verbergen kann. Für kleine Models, langsame Links, winzige Layer oder Layouts, deren Shard-Gruppe die falsche Topology-Grenze überschreitet, kann es ein schlechter Trade-off sein.

Tensor Parallelism: Layer-Mathematik partitionieren

Partitionierung eines Layers mit Tensor ParallelismPartitionierung eines Layers mit Tensor Parallelism

Tensor Parallelism partitioniert die lineare Algebra innerhalb eines Layers. Beispiele sind Column-Parallel- und Row-Parallel-Projections. Der Parallelism-Leitfaden von NVIDIA dokumentiert diesen Layer-Level-Split. Teilergebnisse erfordern Collectives innerhalb von Transformer-Blöcken, daher sind Latency und Bandwidth während des Forward- und Backward-Passes wiederholt entscheidend.

Verwenden Sie es, wenn ein Layer oder seine Activations nicht in den Speicher passen oder wenn die Matmuls groß genug sind, damit partitionierte Kernels einen Rank übertreffen. Ordnen Sie die Tensor-Parallel-Gruppe der schnellsten verfügbaren Communication Domain zu und messen Sie anschließend. Ein hoher Tensor-Parallel-Grad kann jede lokale Matrix so weit verkleinern, dass die Kernel-Effizienz sinkt und gleichzeitig der Collective Overhead steigt.

Sequence Parallelism wird häufig mit Tensor Parallelism kombiniert, um die Replikation bestimmter Activation-Berechnungen zu vermeiden. Es unterscheidet sich von Context Parallelism über die vollständige Input-Sequenz des Models.

Pipeline Parallelism: Tiefe partitionieren und Zeit schedulen

Pipeline-Parallel-Stages und Micro-BatchesPipeline-Parallel-Stages und Micro-Batches

Pipeline Parallelism platziert unterschiedliche Layer-Gruppen auf verschiedenen Stages und überträgt Activations zwischen ihnen. Micro-Batches halten die Stages gleichzeitig beschäftigt. Das GPipe-Paper verwendet diesen Schedule für riesige neuronale Netzwerke.

Es entlastet den Model State pro Stage und kann im Vergleich zu Layer-weiten Tensor-Collectives das Kommunikationsvolumen über eine langsamere Grenze reduzieren. Zu den Kosten gehören Bubbles, Activation Transfers, Imbalance zwischen Stages, komplexeres Scheduling sowie schwierigere Recovery und Checkpointing.

Für einen einfachen ausbalancierten GPipe-Style-Schedule mit p Stages und m Micro-Batches beträgt der idealisierte Forward-Bubble-Anteil ungefähr:

(p - 1) / (m + p - 1)

Reale Schedules können One-Forward/One-Backward-, Interleaving- oder Zero-Bubble-Varianten verwenden, und ungleiche Layer-Kosten können die Formel dominieren. Legen Sie Stage-Grenzen anhand gemessener Zeit und des Memory-Verbrauchs fest, nicht anhand gleicher Layer-Anzahlen.

Context Parallelism: Activations langer Sequenzen partitionieren

Attention-Austausch mit Context ParallelismAttention-Austausch mit Context Parallelism

Context Parallelism verteilt die Sequenzdimension. Die Dokumentation von NVIDIA zu Context Parallelism beschreibt den Sequenz-Split und den für Attention erforderlichen Key/Value-Austausch. Jeder Rank besitzt einen Sequenz-Shard, während Attention die Informationen austauscht, die zur Erhaltung der Full-Context-Semantik erforderlich sind. Implementierungen können Point-to-Point-Rings, All-Gather, All-to-All oder hierarchische Kombinationen verwenden.

Es reduziert den Activation Memory bei Long-Context-Training, repliziert jedoch die Weights über die Context-Gruppe und führt Attention-Kommunikation ein. Der Nutzen hängt vom Attention-Typ, vom Causal Masking, von der Sequenzlänge, von Recomputation und davon ab, wie Context-Gruppen mit Tensor- und Data-Parallel-Gruppen kombiniert werden.

Wählen Sie es nicht anhand eines universellen Schwellenwerts von 8K, 32K oder 100K. Profilen Sie Activation Memory und Attention-Kommunikation für die tatsächliche Architektur.

Expert Parallelism: nur für eine MoE-Architektur

Expert-Parallel-Token-RoutingExpert-Parallel-Token-Routing

Expert Parallelism verteilt die Experten in MoE-Layern. Der Parallelism-Leitfaden von NVIDIA dokumentiert diese Expertenplatzierung und ihre Kombination mit anderen Parallelism-Dimensionen. Der Router sendet Token-Repräsentationen an ausgewählte Experten und kombiniert deren Ergebnisse. Für jedes Token berechnen nur die ausgewählten Experten, aber die gesamten Expert Weights müssen weiterhin gespeichert und beim Serving platziert werden.

Expert Parallelism ist kein Optimierungsschalter für ein Dense Model. Es ist Bestandteil einer MoE-Architektur. Zu den relevanten Aspekten gehören Load Balancing, Capacity Limits, Token All-to-All, gedroppte oder gepaddete Tokens, Auxiliary Losses und Failure Skew. Erfassen Sie Tokens pro Expert, Routing Entropy, Capacity Overflow, Communication Time und die Qualität pro Route.

Ein Layout aus der Topology ableiten

Training-Systeme für große Dense Models ohne Expert-Parallel-Gruppe verwenden häufig das Produkt der Gruppen-Größen von Data Parallel (DP), Tensor Parallel (TP), Pipeline Parallel (PP) und Context Parallel (CP):

world size = DP × TP × PP × CP

Wenn Expert Parallelism (EP) eine unabhängige Gruppe ist, berechnet der Parallelism-Leitfaden von NVIDIA den Gesamtwert wie folgt:

total GPUs = TP × PP × CP × EP × DP

Verwenden Sie das vom Framework unterstützte Mesh, statt nicht unterstützte Konfigurationen miteinander zu multiplizieren.

Erstellen Sie das Layout in dieser Reihenfolge:

  1. Zeichnen Sie die Communication Domains ein: GPU-to-GPU-Links, Switches, Non-Uniform Memory Access (NUMA)-Grenzen, Node Fabric, Oversubscription und Storage Path.
  2. Platzieren Sie häufige Latency-sensitive Collectives, typischerweise TP, in der schnellsten geeigneten Domain.
  3. Wählen Sie FSDP- oder replizierte DP-Gruppen anhand der verbleibenden Capacity und Bandwidth.
  4. Fügen Sie PP hinzu, wenn Depth Placement oder Traffic über Domänen hinweg davon profitieren, und balancieren Sie gemessene Stage-Zeit und Memory.
  5. Fügen Sie CP nur für den Sequenz-Constraint hinzu und Expert Parallelism (EP) nur für die Expert-Topology des Models.
  6. Bestätigen Sie die Teilbarkeit von Heads, Hidden Dimensions, Layern, Experten, Batch und Sequenz für das geplante Mesh.
  7. Benchmarken Sie mehrere gültige Meshes. Topology-aware Heuristics wählen Kandidaten, nicht die Gewinner.

Zwei Cluster mit derselben GPU-Anzahl können unterschiedliche Layouts bevorzugen, weil sich Link Bandwidth, Switch-Hierarchie, CPU-Anbindung und Network Contention unterscheiden.

Training und Serving erfordern getrennte Entscheidungen

Inference führt normalerweise weder Gradients noch Optimizer State mit, daher lassen sich FSDP-Training-Layouts nicht automatisch übertragen.

Fragen Sie beim Serving:

  • Passt eine Replica inklusive Weights, KV cache, Workspace und gewünschter Concurrency in den Speicher?
  • Ist der Throughput mit mehreren unabhängigen Replicas höher oder durch das Sharding einer Replica?
  • Reduziert TP den Weight- und Cache-Druck pro Rank ausreichend, um die Kommunikation pro Layer zu rechtfertigen?
  • Wird PP für das Model und den Request Scheduler effizient unterstützt?
  • Wie belasten Prefill und Decode Compute, Memory Bandwidth und Interconnect auf unterschiedliche Weise?
  • Was geschieht mit der Tail Latency, wenn Requests unterschiedliche Prompt- und Output-Längen haben?

Benchmarken Sie den vollständigen Server mit Scheduler, Quantization, Context-Verteilung, Batching Policy und Traffic Shape. Training-Tokens pro Sekunde können weder die Serving Time to First Token noch die Inter-Token Latency vorhersagen.

Ein Scaling-Layout ehrlich messen

Erfassen Sie für jeden Kandidaten:

  • Model, Code, Runtime, Kernels und Topology-Identität
  • globalen und lokalen Batch, Sequenzverteilung und Token-Anzahl
  • maximalen Memory-Verbrauch nach Kategorie, sofern verfügbar
  • nützliche Tokens pro Sekunde sowie die Model-FLOP-Auslastung, sofern konsistent berechnet
  • sichtbare Zeit in All-Reduce-, All-Gather-, Reduce-Scatter-, All-to-All- und Point-to-Point-Operationen
  • Input Stalls, Checkpoint-Zeit, Restart-Verhalten und Verteilung der Stragglers
  • Training Loss oder Parität der Serving-Outputs gegenüber der Baseline

Vergleichen Sie Weak und Strong Scaling bewusst. Strong Scaling hält die Gesamtarbeit konstant, während die Anzahl der Ranks steigt. Weak Scaling hält die Arbeit pro Rank konstant, sodass die Gesamtarbeit mit der Anzahl der Ranks wächst. Ein Prozentsatz mit der Bezeichnung „Scaling Efficiency“ ist ohne diesen Nenner und eine Baseline bedeutungslos.

Fazit

Parallelism ist eine Zuordnung von einem gemessenen Bottleneck zu einer Tensor-Dimension und einem Communication Pattern. Replication, Sharding, Layer-Partitionierung, Staging, Sequenz-Partitionierung und Expert Routing entlasten jeweils einen anderen Constraint und erzeugen jeweils einen anderen Failure Mode.

Erfassen Sie den Workload, zeichnen Sie die Topology, erzeugen Sie gültige Meshes und profilieren Sie sie. Das beste Layout ist dasjenige, das mit Headroom passt und für den tatsächlich ausgeführten Job die sichtbare Kommunikation minimiert.

Referenzen