Summary
Choose a collective by each rank’s required output: concatenate pieces, combine aligned values, or distribute a completed result.
Problem
The process group contains ranks 0 and 1, holding [1, 2] and [3, 4].
Both buffers have shape (2,), dtype int64 and the same index meanings.
Specify what each rank holds after a sum all-reduce, reduce-scatter and all-gather.
Use rank order for concatenation and equal, index-aligned chunks for scattering.
Picture: Add Aligned Positions or Join Pieces
Sum reduction combines corresponding positions: [1+3, 2+4] becomes [4, 6].
All-reduce gives that result to both ranks.
Reduce-scatter keeps [4] on rank 0 and [6] on rank 1.
All-gather of those reduced shards restores [4, 6] on both.
All-gather of the original buffers instead produces [1, 2, 3, 4].
It concatenates; it does not add.
The all-reduce identity therefore needs compatible reduction, chunk boundaries and rank order.
Six Output Contracts
| Operation on the stated inputs | Rank 0 afterward | Rank 1 afterward |
|---|---|---|
| Broadcast rank 0 | [1, 2] | [1, 2] |
| Sum reduce to rank 0 | [4, 6] | No required result |
| Sum all-reduce | [4, 6] | [4, 6] |
| All-gather original buffers | [1, 2, 3, 4] | [1, 2, 3, 4] |
| Sum reduce-scatter | [4] | [6] |
| All-to-all, one value per destination | [1, 3] | [2, 4] |
Sum does not divide by rank count. Equal-sized data-parallel batches can average by dividing the summed gradients appropriately. Unequal local batches require weighting by example counts, not blindly averaging local means.
Reference the Values Before the Network
This reference models values and order, not transport, dtype enforcement or synchronization.
def sum_by_index(buffers):
if not buffers:
raise ValueError("the process group must be nonempty")
width = len(buffers[0])
if any(len(buffer) != width for buffer in buffers):
raise ValueError("rank inputs must have matching shapes")
return [sum(contributions) for contributions in zip(*buffers)]
def reduce_scatter_reference(buffers):
reduced = sum_by_index(buffers)
ranks = len(buffers)
if len(reduced) % ranks:
raise ValueError("equal chunks require a divisible width")
width = len(reduced) // ranks
return [reduced[rank * width:(rank + 1) * width] for rank in range(ranks)]
def all_gather_reference(shards):
if not shards or any(len(shard) != len(shards[0]) for shard in shards):
raise ValueError("this reference requires equal-sized rank shards")
return [value for shard in shards for value in shard]
buffers = [[1, 2], [3, 4]]
assert sum_by_index(buffers) == [4, 6]
assert reduce_scatter_reference(buffers) == [[4], [6]]
assert all_gather_reference(reduce_scatter_reference(buffers)) == [4, 6]
assert all_gather_reference(buffers) == [1, 2, 3, 4]
The Ring All-Reduce
Now use four ranks and full input size MB, with decimal MB. Split each input into four equal, aligned chunks. For chunk A, rank-local contributions are . Three receive-and-add steps finish ; three forwarding steps distribute it without further addition. Other chunks follow the corresponding schedule concurrently.
Each rank sends MB per step: six steps send 12 MB. It also receives 12 MB; do not count sent-plus-received bytes as sent traffic. Generally, ring sent traffic is per rank over steps.
Reduce each chunk on the ring, then circulate its completed sum
Derivation
After reduction reaches ten, should all-gather change its value?
Change 1: The Consumer Needs Only Its Shard
New task. The optimizer needs its own portion of the summed gradient, not the full sum everywhere.
Picture and change. Stop after reduce-scatter and preserve the optimizer’s shard mapping. Do not all-gather a result merely to discard the other shards.
Check. The two-rank outputs remain [4] and [6], not two full buffers.
In the four-rank ring model, this phase sends 6 MB per rank instead of 12 MB.
Use when. Downstream work can consume sharded results.
Change 2: Thousands of Tiny Buffers
New task. Replace one 8 MB buffer with 1,000 separate 8 KB buffers.
Picture and change. Total ring bytes stay fixed, but sequential communication steps multiply. For effective bandwidth and per-step latency , use the simplified unoverlapped model
At four ranks, microseconds and GB/s give 12.03 ms for the single buffer. The 1,000 serial collectives take 42 ms in this model, despite sending the same 12 MB per rank.
Check. Measure backend launch/hop costs, bucketing, tree alternatives and actual topology before selecting an algorithm. These calculations are not hardware measurements.
Use when. Fixed overhead dominates small messages.
Failure: Ranks Disagree About the Operation
A skipped call or mismatched shape, dtype, chunk order or collective sequence can hang, fail or invalidate results. All participating ranks must follow the process group’s matching communication contract.
Transfer Check
Does a long total collective duration establish the training bottleneck?
Reasoned Answer
No. Measure its exposed critical-path tail after overlap and contention. Summed durations can include work already hidden behind computation.
Where Each Appears
| Distributed pattern | Collectives |
|---|---|
| DDP (data parallel) | Gradient all-reduce at synchronized backward boundaries; accumulation can defer synchronization |
| FSDP / ZeRO-3 | Parameter all-gather when needed; gradient reduce-scatter under the selected sharding strategy |
| Tensor parallelism | All-reduce, reduce-scatter or all-gather according to input/output layouts |
| Pipeline parallelism | Point-to-point sends (not collective) between adjacent stages |
| Expert parallelism (MoE) | All-to-all to route tokens to experts |
| Embedding lookup at scale | All-to-all to gather sharded embedding rows |
All-to-all
Each rank sends destination-specific pieces; receive order follows the agreed source layout. MoE routing and layout changes use this exchange. Cost depends on peer traffic, balance, bisection bandwidth and topology, not the operation name alone.
Hardware backends
- NCCL: NVIDIA accelerator communication.
- RCCL: AMD accelerator communication.
- MPI: broader high-performance message passing.
- Gloo: commonly used for CPU collectives and supported distributed interfaces.
Supported operations and selected algorithms depend on version, hardware, group and message size.
Bandwidth and topology
Distinguish local accelerator links from inter-node or inter-slice networks. Hierarchical algorithms combine local and scale-out phases. Use measured effective bandwidth for the message and schedule, not advertised peak bandwidth.
Cost model in DDP
For parameters and gradient element size , bytes in the ring model. Suppose seven billion gradients use BF16, 32 ranks participate, and effective ring bandwidth is 200 GB/s. Then GB and per-rank sent traffic is 27.125 GB. The bandwidth term is 135.625 ms, not a measured step time.
Common pitfalls
Buckets trade fixed overhead against readiness, memory and overlap. DDP/FSDP overlap depends on configuration and schedule; inspect traces rather than assuming it. Reduction order can change floating-point rounding even when mathematical outputs agree. The integer reference does not model those numerical differences or diagnose distributed hangs.
Related
- Tensor parallelism. Heavy collective user.
- FSDP and ZeRO. Uses all-gather and reduce-scatter.
- Pipeline parallelism. Uses point-to-point, not collectives.
- Sharded matrix multiplication. Select collectives from array layouts.
- Accelerator network topology. Understand the links that carry them.