ZeRO: Zero Redundancy Optimizer
DeepSpeed ZeRO stages 1-3: partitioning optimizer states, gradients, and parameters across data-parallel ranks, communication analysis, ZeRO-Offload to CPU/NVMe, and FSDP as PyTorch's ZeRO-3.
Prerequisites
Table of Contents
- Learning Objectives
- Notation
- Core Intuition
- Memory Breakdown of Standard Data Parallelism
- ZeRO Stage 1: Partition Optimizer States
- ZeRO Stage 2: Partition Gradients
- ZeRO Stage 3: Partition Parameters
- Communication Analysis
- ZeRO-Offload: CPU and NVMe
- FSDP: PyTorch's ZeRO-3
- Common Pitfalls
- Summary
- Exercises
Learning Objectives
- Compute the memory breakdown for mixed-precision training with AdamW.
- Derive memory savings for each ZeRO stage.
- Analyze the communication overhead vs standard data parallelism.
- Explain ZeRO-Offload's GPU-CPU-NVMe memory hierarchy.
- Compare ZeRO-3 and FSDP implementations.
Notation
- — model parameters (count)
- — data parallel degree
- — optimizer state memory multiplier (12 for AdamW mixed-precision)
Core Intuition
Standard data parallelism replicates the entire model (parameters, gradients, optimizer states) on every GPU. For a 70B model: each GPU needs ~280 GB just for optimizer states — impossible. ZeRO partitions these redundant copies across GPUs: each GPU stores only of the states, gathering them on-demand via communication.
ZeRO Optimizer Stages
Memory Breakdown of Standard Data Parallelism
For mixed-precision training with AdamW and parameters:
- Parameters (FP16): bytes
- Gradients (FP16): bytes
- Optimizer states:
- FP32 parameters copy: bytes
- FP32 momentum: bytes
- FP32 variance: bytes
- Total optimizer: bytes
Total per GPU: bytes.
For 7B model: GB per GPU (without activations).
ZeRO Stage 1: Partition Optimizer States
Each GPU stores optimizer states for only of the parameters.
Memory per GPU: .
For : (vs ). 2.9x reduction.
Communication: Same as standard DP (all-reduce gradients). Each GPU updates only its partition, then all-gathers the updated parameters.
ZeRO Stage 2: Partition Gradients
Additionally partition gradients: each GPU keeps gradients only for its optimizer partition.
Memory per GPU: .
For : . 4.3x reduction.
Communication: Replace all-reduce with reduce-scatter (each GPU receives reduced gradients only for its partition). Then all-gather updated parameters.
Total communication: same volume as standard DP ( bytes per step).
ZeRO Stage 3: Partition Parameters
Partition everything: parameters, gradients, and optimizer states.
Memory per GPU: .
For : per GPU.
A 70B model: only GB per GPU. Fits on a single 80GB GPU with room for activations!
Communication: Parameters must be all-gathered for both forward and backward passes. Total: bytes per step (3x more than standard DP).
Communication Analysis
| Stage | Memory/GPU | Communication Volume |
|---|---|---|
| Standard DP | (all-reduce) | |
| ZeRO-1 | ||
| ZeRO-2 | ||
| ZeRO-3 | (3x DP) |
ZeRO-3 tradeoff: less memory but more communication. Worthwhile when memory is the bottleneck (which it usually is for large models).
ZeRO-Offload: CPU and NVMe
ZeRO-Offload: Move partitioned optimizer states to CPU memory (much larger: 512GB–2TB).
ZeRO-Infinity: Extend to NVMe SSDs (essentially unlimited storage):
- Parameters: GPU or CPU.
- Optimizer states: CPU or NVMe.
- Gradients: GPU (compute) → CPU (accumulate).
Data flow: GPU computes forward/backward → gradients offloaded to CPU → CPU performs optimizer step → updated parameters prefetched back to GPU.
Overlap: Computation on micro-batch overlaps with CPU optimizer step for micro-batch .
FSDP: PyTorch's ZeRO-3
Fully Sharded Data Parallelism: PyTorch-native implementation of ZeRO-3.
Key differences from DeepSpeed ZeRO-3:
- Uses PyTorch's distributed primitives (process groups).
- Finer-grained sharding (per-parameter, not per-layer).
- Better integration with PyTorch ecosystem (autograd, checkpointing).
- Supports mixed precision via native PyTorch AMP.
Sharding strategy:
FULL_SHARD: ZeRO-3 (shard everything).SHARD_GRAD_OP: ZeRO-2 (shard gradients + optimizer).NO_SHARD: Standard DDP.
Common Pitfalls
Pitfall 1. Using ZeRO-3 with small batch sizes. The all-gather communication per step is standard DP. If batch/compute per step is small, communication dominates.
Pitfall 2. Not overlapping communication with computation. ZeRO-3 must prefetch the next layer's parameters while computing the current layer. Without overlap: training is 2-3x slower.
Pitfall 3. ZeRO-Offload with slow CPUs. The CPU optimizer step must keep up with GPU forward/backward. Insufficient CPU cores or slow memory bandwidth creates a bottleneck.
Summary
- ZeRO-1: Partition optimizer states → ~3x memory reduction, same communication.
- ZeRO-2: Additionally partition gradients → ~4x reduction, same communication.
- ZeRO-3/FSDP: Partition everything → reduction, 3x communication.
- ZeRO-Offload: Leverage CPU/NVMe for optimizer states when GPU memory is exhausted.
- FSDP = PyTorch-native ZeRO-3 with fine-grained sharding.
Exercises
Exercise 1. For a 13B model with : compute per-GPU memory for standard DP, ZeRO-1, ZeRO-2, and ZeRO-3.
Exercise 2. Derive the communication volume for ZeRO-3 (show why it's per step).
Exercise 3. For a training step taking 100ms compute on 8 GPUs with 400 GB/s inter-GPU bandwidth: compute the communication overhead for ZeRO-3.
Exercise 4. Design an offload strategy for training a 175B model on 8x A100-80GB GPUs (explain what goes where).
Exercise 5. Compare FSDP FULL_SHARD vs SHARD_GRAD_OP for a 7B model on 4 GPUs with 80GB each. Which is appropriate?