Data Parallelism › The All-Reduce Operation

Michael BrenndoerferJanuary 16, 20268 min read
Language AI HandbookAll chapters
Part 31 of 67Training Infrastructure
Chapter 222Data Parallelism › The All-Reduce Operation
Reading section 4 of 12 · The All-Reduce Operation · 8 minSection 4 of 12 · 8 min

Book contents

Section 4 of 12 · 8 min read

Data Parallelism › The All-Reduce OperationOne section · Sections in this chapter4 / 12 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

All-reduce is the communication primitive at the heart of DDP. Understanding how it works explains both why DDP is efficient and what its scaling limits are. All-reduce is not a single algorithm but a family of algorithms with different performance characteristics. Choosing the right one for your hardware topology can make a substantial difference in training speed.

The Reduce-Broadcast Baseline

The simplest correct all-reduce is a two-step reduce-broadcast:

  1. One designated root rank aggregates all tensors from all other ranks (reduce to root)
  2. The root broadcasts the result to all other ranks

This approach has the same parameter server bottleneck problem we described earlier. The root rank must receive data from N−1N-1 other ranks (inbound traffic =(N−1)⋅∣g∣= (N-1) \cdot |\mathbf{g}| bytes) and send data to N−1N-1 ranks (outbound traffic =(N−1)⋅∣g∣= (N-1) \cdot |\mathbf{g}| bytes). The root's required bandwidth scales linearly with NN. For large NN, this approach is impractical.

Ring All-Reduce

The standard solution is ring all-reduce, popularized by Baidu Research (Gibiansky, 2017) for deep learning. In ring all-reduce, the NN ranks are arranged in a logical ring topology. The algorithm has two phases, each consisting of N−1N-1 communication steps.

Phase 1: Reduce-Scatter

In the reduce-scatter phase, each rank splits its gradient tensor gr\mathbf{g}_r into NN equal chunks: gr=[gr(0),gr(1),…,gr(N−1)]\mathbf{g}_r = [\mathbf{g}_r^{(0)}, \mathbf{g}_r^{(1)}, \ldots, \mathbf{g}_r^{(N-1)}]. Over N−1N-1 communication steps:

  • Each rank sends one chunk to its right neighbor in the ring
  • Each rank receives one chunk from its left neighbor and adds it to its local copy of that chunk

After N−1N-1 steps, each rank holds one chunk that contains the sum of that chunk across all ranks. Specifically, rank rr holds ∑i=0N−1gi(r)\sum_{i=0}^{N-1} \mathbf{g}_i^{(r)}, the fully summed version of chunk rr.

Phase 2: AllGather

In the all-gather phase, each rank sends its fully-summed chunk to its right neighbor over N−1N-1 steps. By the end:

  • Every rank holds every chunk of the fully summed gradient
  • The complete synchronized gradient ∑rgr\sum_{r} \mathbf{g}_r is on every rank

The total data transferred by each rank across both phases is:

Data per rank=2⋅N−1N⋅∣g∣\text{Data per rank} = 2 \cdot \frac{N-1}{N} \cdot |\mathbf{g}|

where:

  • ∣g∣|\mathbf{g}| is the total size of the gradient tensor in bytes
  • N−1N\frac{N-1}{N} approaches 1 as NN grows, so each rank transfers approximately 2∣g∣2|\mathbf{g}| bytes regardless of the number of GPUs
  • The factor of 2 accounts for both phases: one reduce-scatter send and one all-gather send, each of approximately ∣g∣|\mathbf{g}| bytes total

The defining property of ring all-reduce is that the per-rank communication volume is independent of NN. Adding more GPUs does not increase the amount of data each GPU must send or receive. This is what makes ring all-reduce bandwidth-optimal: it achieves the theoretical minimum data transfer for the all-reduce problem.

To understand why this is remarkable, compare it to the naive reduce-broadcast: a single root rank must send and receive approximately N⋅∣g∣N \cdot |\mathbf{g}| bytes, which grows linearly with NN. Ring all-reduce eliminates this growth by distributing the aggregation work evenly.

Out[4]:
Visualization
Diagram of ring all-reduce reduce-scatter phase with 4 GPUs and clockwise arrows.
Ring all-reduce reduce-scatter phase for 4 GPUs. After N-1 steps, each GPU holds the fully summed gradient for its assigned chunk, with GPU 0 owning sum[0], GPU 1 owning sum[1], and so on. The clockwise arrows show the flow of one chunk per step.
Diagram of ring all-reduce all-gather phase with 4 GPUs holding the full gradient.
Ring all-reduce all-gather phase. Starting from the partial sums computed in reduce-scatter, each GPU propagates its summed chunk clockwise until every GPU holds the complete gradient. Each GPU transfers approximately 2 x gradient bytes total across both phases, independent of GPU count.

Tree All-Reduce

Ring all-reduce is optimal for large messages where bandwidth is the bottleneck. For small messages (small models, small gradient tensors), the latency of 2(N−1)2(N-1) communication steps becomes significant even if each step transfers little data. In this regime, a tree topology performs better.

In a binary tree all-reduce, ranks are organized as a binary tree. The reduce phase propagates data up the tree from leaves to root, with each internal node summing the values it receives from its children. The broadcast phase propagates the result back down from root to leaves. The total number of communication steps is 2log⁡2N2 \log_2 N rather than 2(N−1)2(N-1) steps, which is dramatically fewer for large NN.

The tradeoff is bandwidth: the root of the tree must process and retransmit O(log⁡N)O(\log N) times as much data as leaf nodes, creating mild hotspots. For the large gradient tensors typical in language model training, ring all-reduce generally wins on throughput because bandwidth, not latency, is the bottleneck. For very small tensors (such as bias gradients in individual layers), tree-based all-reduce can be faster.

NCCL and Hardware Topology

In practice, PyTorch uses NVIDIA Collective Communications Library (NCCL) rather than implementing ring all-reduce directly in Python or CUDA. NCCL is topology-aware: it detects whether GPUs are on the same PCIe bus, connected via NVLink, or spread across multiple nodes connected by InfiniBand or RoCE, and chooses the optimal communication algorithm for each case. NCCL also handles buffer management, stream synchronization, and error recovery, all of which are tedious to implement correctly.

NVLink (the high-bandwidth GPU interconnect available on NVIDIA A100 and H100 servers) provides roughly 600 GB/s aggregate bidirectional bandwidth on modern 8-GPU nodes, compared to roughly 32 GB/s for PCIe. For multi-node training, InfiniBand HDR provides roughly 200 Gb/s per link (about 25 GB/s). The bandwidth difference between NVLink and InfiniBand is enormous: NVLink is roughly 24 times faster per GPU. This means that gradient communication that takes 20 ms on NVLink takes nearly 500 ms on InfiniBand, completely dominating compute time.

The topology distinction is particularly important for multi-node training. Within a single node with 8 GPUs connected via NVLink, NCCL uses a ring or tree topology that fully exploits the high NVLink bandwidth. Across nodes, NCCL routes traffic through the node's InfiniBand host channel adapter (HCA), which has far lower bandwidth per GPU than NVLink. A typical multi-node DDP job therefore has a two-level communication hierarchy: intranode communication using the fast NVLink fabric, and internode communication using the slower InfiniBand fabric.

Understanding this hierarchy helps you make informed decisions about how to partition GPUs. If you have a cluster of 8-GPU nodes, it is almost always better to fill entire nodes before spreading to additional nodes. Keeping communication within a single node maximizes the use of NVLink and minimizes InfiniBand traffic. Once you must cross nodes, you want to keep the cross-node gradient transfers as small as possible, which is one motivation for combining DDP with ZeRO (which reduces the gradient tensor that must be communicated across nodes).

NCCL vs. Gloo Backends

PyTorch's dist.init_process_group supports multiple backends. NCCL is the correct choice for GPU training: it is tightly integrated with NVIDIA hardware and provides the best performance. Gloo is the alternative backend that runs on CPU and works across platforms including macOS and Windows. Gloo is useful for debugging and for single-machine multi-process experiments where GPU communication is unavailable. MPI is a third option for clusters that have MPI already configured, but NCCL is generally preferred for pure GPU training. When you see code examples using "gloo", that is always for portability or demonstration, not for production performance.