AI Grimoire
Sheet
statusstandard
difficultyintermediate
timeO(N)
described1994
revisedtoday

Collective Communication

Every distributed training strategy is a choice of which tensor to split and which collective to pay for. There are only about six collectives, and the cost model for each is one line.

Standing

Current standardPart of the default recipe. Present in most frontier models trained today, and the thing a new design departs from rather than argues for.

The vocabulary every parallelism strategy is written in. Older than deep learning by two decades and unchanged in substance.

judged as of 2026-09 · what the labels mean

Theory

Distributed training is a small number of tensor-splitting decisions, and each one buys memory by paying for a collective. Knowing the six operations and what each costs is enough to reason about any of the strategies on this branch.

OperationEach rank hasEach rank ends with
broadcastrank 0 has xxxx
reducexix_irank 0 has xi\sum x_i
all-reducexix_ixi\sum x_i
all-gatherxix_i[x0,,xN1][x_0, \ldots, x_{N-1}]
reduce-scatterxix_i (full size)shard ii of xi\sum x_i
all-to-all[yi0,][y_{i0}, \ldots][y0i,][y_{0i}, \ldots]

The identity worth memorising:

all-reduce=reduce-scatter+all-gather\text{all-reduce} = \text{reduce-scatter} + \text{all-gather}
eq. 1 — why the two-phase implementations exist

That decomposition is not a curiosity. It is exactly how ZeRO turns data-parallel all-reduce into something cheaper: if each rank only needs its shard of the summed gradient, the all-gather half can be skipped.

Ring all-reduce

The naïve implementation sends everything to one rank and back, which makes that rank’s link the bottleneck and scales badly. The ring algorithm does not.

Arrange the ranks in a circle and split the tensor into NN chunks. In the reduce-scatter phase, each rank sends one chunk to its neighbour and adds what it receives — after N1N-1 steps each rank holds the complete sum for one chunk. The all-gather phase circulates those around, another N1N-1 steps.

T=2N1NSB+2(N1)αT = 2\,\frac{N-1}{N}\cdot\frac{S}{B} + 2(N-1)\,\alpha
eq. 2 — bandwidth cost, independent of N

The bandwidth term approaches 2S/B2S/B and stops growing — twice the tensor size per rank however many ranks there are, which is why data parallelism scales at all. The latency term α\alpha does grow with NN, which is why small tensors are the problem and gradient bucketing exists.

Topology

Bandwidth differs by an order of magnitude depending on which link a message crosses.

LinkBandwidth
NVLink, intra-node400–900 GB/s
PCIe 5 ×1664 GB/s
InfiniBand NDR50 GB/s per port
Ethernet12 GB/s

Every serious strategy places the chattiest parallelism inside a node. Tensor parallelism all-reduces twice per layer and belongs on NVLink; data parallelism reduces once per step and tolerates the network. Getting that assignment backwards is the most common way to lose half the throughput of a cluster, and it is a launch-configuration decision rather than a code change.

Implementation

python · torch.distributed
import torch
import torch.distributed as dist


def gradient_all_reduce(grads: list[torch.Tensor], bucket_mb: int = 25) -> None:
    """Bucket small tensors: the latency term dominates below ~1 MB, so
    one call for a hundred tensors beats a hundred calls."""
    bucket, size = [], 0
    for g in grads:
        bucket.append(g)
        size += g.numel() * g.element_size()
        if size >= bucket_mb * 2**20:
            _flush(bucket)
            bucket, size = [], 0
    if bucket:
        _flush(bucket)


def _flush(bucket: list[torch.Tensor]) -> None:
    flat = torch._utils._flatten_dense_tensors(bucket)
    dist.all_reduce(flat, op=dist.ReduceOp.AVG)
    for g, r in zip(bucket, torch._utils._unflatten_dense_tensors(flat, bucket)):
        g.copy_(r)

ReduceOp.AVG rather than SUM followed by a division: it is one kernel instead of two, and it avoids a subtle overflow when many ranks contribute large gradients in low precision.

Bucketing is what DDP does automatically, and it is why a hand-rolled data-parallel loop is usually much slower — a transformer has hundreds of parameter tensors, most of them small, and the per-call latency dominates.

Bytes per rank
2S(N−1)/N
Steps
2(N−1)
Scaling in N
flat
Ring all-reduce, N ranks, S bytes

Related

References

[1]Thakur et al. — Optimization of Collective Communication Operations in MPICH (2005)IJHPCA 19(1)
[2]Patarasuk & Yuan — Bandwidth Optimal All-reduce Algorithms for Clusters of Workstations (2009)JPDC 69(2)
[3]NVIDIA — NCCL documentationdocs.nvidia.com