Distributed training (DDP, ZeRO, FSDP, TP, PP)
Data, tensor, and pipeline parallelism, and the sharding strategies that train billion-parameter models.
Why this matters
Above ~7B parameters you cannot fit a model on a single GPU. Above ~70B you cannot even fit it on a single node. Frontier models train on thousands of GPUs in coordinated parallelism. Knowing the names β DDP, ZeRO, FSDP, TP (Tensor Parallel), PP (Pipeline Parallel), SP (Sequence Parallel), EP (Expert Parallel) β and which to combine, is the entire expertise of a "large-model training engineer." It is among the highest-paid specialisations in AI ($350kβ$700k+ in 2026).
You will not run a 1000-GPU job. But you will discuss it in interviews and will use a 2-8 GPU subset of these techniques.
Learning objectives
- Distinguish data, tensor, pipeline, and expert parallelism.
- Use PyTorch DDP and FSDP with confidence.
- Read a
torchruncommand and explain every flag. - Estimate which parallelism strategy you need for a given model + GPU count.
- Recognise common bottlenecks (NCCL, slow networks, gradient checkpointing).
1. The 5 axes of parallelism
1.1 Data parallelism (DP)
Replicate the model on every GPU; shard the batch. After backward, all-reduce gradients across GPUs.
- Simple; near-linear scaling on 8-64 GPUs.
- Memory: each GPU has the full model, full optimiser state.
- Bottleneck for big models.
PyTorch DDP is the default:
import torch.distributed as dist
from torch.nn.parallel import DistributedDataParallel as DDP
dist.init_process_group("nccl")
model = MyModel().to(rank)
model = DDP(model, device_ids=[rank])1.2 Tensor / Model parallelism (TP)
Split individual matrices across GPUs. The Q projection of size (d, d) becomes (d, d/N) per GPU; the output is gathered.
- Used for matrices too large for a single GPU.
- Heavy intra-layer all-reduce; needs fast NVLink (i.e., within a single node).
- Megatron-LM is the canonical implementation.
1.3 Pipeline parallelism (PP)
Split layers across GPUs. GPU0 has layers 1-20, GPU1 has 21-40, etc. Mini-batches flow through the pipeline.
- "Bubble" overhead at start/end of each step (GPUs idle while pipeline fills).
- Mitigated by micro-batches (1F1B schedule, GPipe, PipeDream).
- Used across nodes (slower interconnect is OK because layer outputs are tiny vs gradient all-reduces).
1.4 Sequence parallelism (SP)
Split along the sequence dimension. Useful when sequences are very long. Needed in tandem with TP for very long-context training.
1.5 Expert parallelism (EP)
For MoE: place different experts on different GPUs. Token routing β all-to-all communication.
Combining axes (3D / 4D parallelism)
A real frontier training run uses 3-4 axes simultaneously:
total GPUs = DP Γ TP Γ PP Γ EP
e.g. 4096 = 128 Γ 8 Γ 4 Γ 1Megatron-LM, DeepSpeed, and torchtitan provide configurations for this.
2. The two main "easy mode" strategies
For 95% of jobs (β€ 1024 GPUs, dense models up to 70B), choose between ZeRO-3 / FSDP and TP + PP. Both work; FSDP is simpler.
ZeRO (DeepSpeed) β Stages 1, 2, 3
ZeRO shards memory across DP ranks instead of replicating:
- Stage 1: shard optimiser states across DP ranks.
- Stage 2: + shard gradients.
- Stage 3: + shard parameters (gather on demand for forward/backward).
Memory savings:
- Stage 1: ~4Γ (Adam state is ~12 bytes/param vs 4 for params).
- Stage 3: ~
NΓ (where N is the number of DP ranks).
FSDP (PyTorch native)
FullyShardedDataParallel is PyTorch's clean reimplementation of ZeRO-3, integrated with torch.distributed.
from torch.distributed.fsdp import FullyShardedDataParallel as FSDP, MixedPrecision
from torch.distributed.fsdp.wrap import transformer_auto_wrap_policy
import functools
mp = MixedPrecision(param_dtype=torch.bfloat16,
reduce_dtype=torch.bfloat16,
buffer_dtype=torch.bfloat16)
wrap_policy = functools.partial(transformer_auto_wrap_policy,
transformer_layer_cls={Block})
model = FSDP(MyTransformer().cuda(),
auto_wrap_policy=wrap_policy,
mixed_precision=mp,
device_id=rank)Key practices:
- Wrap each transformer block as its own FSDP unit (so memory cycling matches the natural granularity).
- Use
MixedPrecisionto keep params/gradients in bf16; full state-dict for checkpointing. - Combine with
activation_checkpointingto trade compute for memory.
torchtune and torchtitan give you FSDP-2 + TP recipes ready to use.
3. Activation checkpointing (a.k.a. gradient checkpointing)
Activations dominate memory in long sequences. Activation checkpointing recomputes them during backward instead of storing them β ~25% extra compute, but unlocks large batch sizes.
from torch.utils.checkpoint import checkpoint
def block_forward(x): return block(x)
x = checkpoint(block_forward, x, use_reentrant=False)Almost every modern recipe enables it.
4. NCCL β the communication backbone
NVIDIA Collective Communications Library handles all_reduce, all_gather, reduce_scatter across GPUs. Critical performance knobs:
- NVLink (intra-node): ~600-900 GB/s on H100/B200.
- InfiniBand (inter-node): 200-400 Gb/s β orders of magnitude slower than NVLink.
- Strategy implication: keep TP within a node, DP/PP across nodes.
Common environment vars:
NCCL_DEBUG=INFO
NCCL_IB_DISABLE=0
NCCL_SOCKET_IFNAME=eth0When training stalls, NCCL is usually the suspect. Check nvidia-smi, network throughput, and whether all ranks reached the same step.
5. torchrun β the launcher
torchrun \
--nnodes=4 \
--nproc_per_node=8 \
--rdzv_backend=c10d \
--rdzv_endpoint=$MASTER_ADDR:29500 \
--rdzv_id=run42 \
train.py --config llama3_8b.yamlWhat each flag does:
nnodes/nproc_per_node: total ranks = nnodes Γ nproc_per_node.rdzv_*: rendezvous backend;c10dis built into PyTorch.rdzv_endpoint: the master node's address.
Inside train.py:
import os, torch.distributed as dist
dist.init_process_group("nccl")
local_rank = int(os.environ["LOCAL_RANK"])
torch.cuda.set_device(local_rank)6. Picking a strategy (quick decision tree)
Model fits on one GPU (<= ~6B params, full bf16)?
ββ DDP. Done.
Model fits on one node (β€ 8 GPUs, ~70B with FSDP)?
ββ FSDP with bf16 + activation checkpointing. Done.
Model spans multiple nodes (200B+)?
ββ FSDP across nodes for DP, +TP within node, possibly +PP across nodes.
Mixture-of-Experts model?
ββ FSDP + Expert Parallel; route experts to different GPUs.The frontier compute mix in 2026 (e.g., DeepSeek-V3, Llama 4):
- DP via FSDP (or ZeRO-3).
- TP=8 within a node.
- PP across racks.
- EP for MoE layers.
- bf16 weights, fp8 matmuls (Hopper / Blackwell), activation checkpointing.
7. Fault tolerance
Real training runs fail β GPU dies, network blip, host kernel panic. You need:
- Periodic checkpointing (e.g., every 1k steps).
- Async checkpointing (write to NVMe / GCS in background).
- Auto-restart from latest checkpoint.
- Skip-on-NaN: throw away the spike step and rewind a few steps.
Tools: torch.distributed.checkpoint (DCP), torchsnapshot, internal frameworks at every lab.
Hands-on lab (4 hours, 2-GPU machine or Colab Pro)
distributed_lab.ipynb + train_ddp.py:
- Take your nano-GPT from Lesson 2.4. Wrap it in DDP. Verify same loss curve as single-GPU. Use
torchrun --nproc_per_node=2. - Replace DDP with FSDP. Wrap each
Block. Print memory per rank β should drop ~2Γ. - Add bf16 mixed precision via FSDP
MixedPrecision. - Add activation checkpointing on each block. Increase
block_size(sequence length) until memory is again the limit; show how much further you got. - Save and reload an FSDP checkpoint correctly (use
state_dict_type=FullStateDictConfig). - Bonus: pretend
torchrun --nnodes=2with two physical machines on a LAN; run the rendezvous and verify.
Common pitfalls
- Module ordering β
model = MyModel(); model.cuda(); model = DDP(model)is correct; the reverse breaks. - Per-rank random seed β set
torch.manual_seed(seed + rank)for the data loader, NOT for the model init (model init must be identical across ranks). - Forgetting
dist.barrier()before checkpointing β ranks save inconsistent states. - Calling
.zero_grad()before all ranks finished backward β DDP will hang. - Mismatched FSDP wrap policy β wrapping the entire model as one unit defeats sharding benefits.
- Print from all ranks β flooded logs. Use
if rank == 0: print(...).
Self-check
- What does ZeRO-3 shard?
- Why is TP usually intra-node and DP inter-node?
- What is the bubble in pipeline parallelism?
- When would you turn on activation checkpointing?
- What is
torchrun --nproc_per_node?
References
- Rajbhandari et al. (2020), "ZeRO: Memory Optimizations Toward Training Trillion Parameter Models."
- Korthikanti et al. (2022), "Reducing Activation Recomputation in Large Transformer Models."
- Smith et al. (2022), "Megatron-Turing NLG 530B."
- Shoeybi et al. (2019), "Megatron-LM."
- PyTorch FSDP getting started.
torchtitanβ Meta's reference distributed-training framework (FSDP-2 + TP + PP).- DeepSpeed docs.
Sign in to save your progress and earn badges.