A training framework is the combination of parallel strategy + memory optimization + observability. PyTorch native offers two paths: DDP (data parallelism; every rank holds a full model replica and runs one gradient All-Reduce after backprop — equivalent to ZeRO-0, solving compute scaling only, no memory win) and FSDP (Fully Sharded Data Parallel, the official implementation of ZeRO-3): every weight tensor is sharded by rank, parameters for each layer are All-Gathered before forward and discarded after use, and backward ends with a Reduce-Scatter of gradient shards, so optimizer states naturally live sharded — per-rank memory
16Ψ/Nd at ~1.5× DDP communication (3Φ vs 2Φ); layer-level sharding plus parameter prefetch hide communication inside compute for near-linear scaling. DeepSpeed offers ZeRO-1/2/3 with CPU/NVMe offload, mechanically equivalent to FSDP: pick FSDP for single-node multi-GPU with minimal code, DeepSpeed for cross-node, very large scale, offload, or its mature ecosystem. Profiling for bottlenecks (torch.profiler + Chrome trace): split a step into four parts — ① compute (CUDA kernel time and SM occupancy; too many tiny kernels → operator fusion / fewer syncs); ② communication (NCCL share of time; is the gradient All-Reduce overlapped with backprop — if not, tune bucket size / enable async); ③ IO (are DataLoader workers saturated, is disk/network the limiter — tune worker count and prefetch_factor); ④ idle (GPU idling on sync points or serial kernels). Debug order: check the CUDA time share first, then split forward/backward/optimizer, then drill down to the specific kernel or comm primitive.