Modern deep learning models are increasingly resource-hungry, and training on a single GPU is often either impossible or highly inefficient. In such cases distributed training is used, leveraging multiple GPUs or even nodes in a cluster.
One of the most mature and high-performance implementations of parallel training in PyTorch is the DistributedDataParallel (DDP) mechanism.
Limitations of single-device training
Training on a single GPU is constrained by:
- insufficient memory for storing model parameters and gradients,
- long epoch times,
- inability to scale experiments efficiently when multiple GPUs are available.
Resolving these limitations requires organizing parallel execution with consistent handling of parameters and gradients.
Approaches to distributed training
There are two main approaches:
Data Parallelism: each process gets a full copy of the model and processes a separate subset of the data. After computing gradients on every process, gradients are synchronized and aggregated, and parameters are updated consistently. This approach is effective when each GPU has enough memory and scales well for moderate model sizes.
Model Parallelism: the model is split across devices. For example, different layers are placed on different GPUs. This allows training models that do not fit on a single device. However, managing dependencies and transferring activations between devices significantly complicates the implementation.
DDP implements data parallelism, providing high performance and stability through efficient gradient synchronization.
How DDP works
The DDP algorithm can be described as follows:
- Each process gets an identical copy of the model and places it on a dedicated device;
- A DistributedSampler is used to give each process a non-overlapping shard of the dataset;
- After the backward pass, gradients are synchronized across all processes.
Gradient synchronization mechanism:
DDP uses an All-Reduce operation implemented via the NCCL backend (on GPU) or GLOO (on CPU/inter-node). The operation averages gradients across all processes and writes the result back into the corresponding tensors. As a result, all model copies remain synchronized at every training step.
The synchronization itself runs asynchronously and is streamed during the backward pass, minimizing overhead and making DDP efficient even with many devices.
Comparison with DataParallel
PyTorch's nn.DataParallel implements parallelism with a single controlling process that distributes inputs to devices and then collects gradients back on the CPU. This causes excessive data transfer and is a performance bottleneck.
DistributedDataParallel, by contrast, launches a separate process per device, works without routing data through the CPU, and synchronizes gradients directly between GPUs.
Result: significantly better scalability, lower latency, and stability as the number of devices grows.
Implementation specifics and limitations
- Each GPU must be served by its own process.
- DistributedSampler is used for correct, non-overlapping data distribution.
- Initial model state and random number generators must be synchronized.
- Global variables should be minimized, since each process runs independently.
- Logging, checkpoint saving, and metrics should be centralized (typically on a single process, e.g., rank 0).
DDP launch example in PyTorch
import torch
import torch.distributed as dist
import torch.multiprocessing as mp
from torch.nn.parallel import DistributedDataParallel as DDP
def train(rank, world_size):
dist.init_process_group("nccl", rank=rank, world_size=world_size)
torch.cuda.set_device(rank)
model = MyModel().to(rank)
ddp_model = DDP(model, device_ids=[rank])
sampler = DistributedSampler(dataset, num_replicas=world_size, rank=rank)
train_loader = DataLoader(dataset, sampler=sampler, ...)
for epoch in range(num_epochs):
for batch in train_loader:
optimizer.zero_grad()
outputs = ddp_model(batch)
loss = loss_fn(outputs, targets)
loss.backward()
optimizer.step()
dist.destroy_process_group()
if __name__ == "__main__":
world_size = torch.cuda.device_count()
mp.spawn(train, args=(world_size,), nprocs=world_size)Practical value
Using DDP enables:
- training models that exceed the capacity of a single device,
- accelerating experiments with proportional resource scaling,
- scaling to distributed clusters and multi-machine setups.
DDP is the recommended method for parallel training in PyTorch and serves as the foundation for higher-level frameworks and scalable platforms.
Conclusion
DistributedDataParallel implements an efficient, well-proven scheme of synchronous training based on data parallelism and gradient synchronization via all-reduce. It is a key technology for scaling deep learning, improving compute efficiency, and shortening training time while preserving accuracy and reproducibility.