Skip to content
mlmentorship

All-reduce and other collectives

Choose a collective from each rank's required output, then account for reduction, shard ownership, traffic, latency and overlap.

Published · 10 min read ·Role-specific ·Advanced
Reading depth

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 inputsRank 0 afterwardRank 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

Reduce a chunk, then circulate its completed sumFor one illustrative chunk, rank values 1,2,3,4 reduce to 10. All-gather distributes that completed chunk; it does not add the sum again.Reduce the partial; circulate thecompleted chunkReduction adds each localcontribution once1+13+26+310+4All-gather copies the completed ten10101010
Derivation
All four chunks move concurrently; reduction adds, while all-gather only copiesFour ranks form a directed ring. At one concurrent send step ranks zero, one, two, and three send chunks A, B, C, and D respectively to the next rank. Every message has size B over four. A separate trace follows one coordinate of chunk A with local values one, two, three, and four: three receives and additions produce three, six, then ten at rank three. Three all-gather hops copy ten unchanged to ranks zero, one, and two. Six equal sent-chunk cells give three B over two sent bytes per rank, not sent-plus-received traffic.Four ranks; four equal chunks A, B, C, DONE CONCURRENT SEND STEPr0r1r2r3A: B/4B: B/4C: B/4D: B/4FOLLOW ONE COORDINATE IN CHUNK ALocal contributions at r0..r3: 1, 2, 3, 4REDUCE-SCATTER: receive, then addr0 -> r11 + 2 = 3r1 -> r23 + 3 = 6r2 -> r36 + 4 = 10ALL-GATHER: forward the same finished valuer3 -> r010 -> 10r0 -> r110 -> 10r1 -> r210 -> 10EACH RANK SENDS SIX B/4 CHUNKSB/4B/4B/4B/4B/4B/4(3 + 3) * B/4 = 3B/2 sent; not sent + received.
The ring inset is one simultaneous step, not the whole schedule. Follow A separately: 1 becomes 3, then 6, then 10; all-gather preserves 10. Every rank sends one B/4 chunk in each of six steps, for 3B/2 sent bytes.
Check: Where are the other chunks while A moves? Why does each rank send six chunks of size B/4?
Read it this way: For one illustrative chunk, rank values 1,2,3,4 reduce to 10. All-gather distributes that completed chunk; it does not add the sum again.
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 patternCollectives
DDP (data parallel)Gradient all-reduce at synchronized backward boundaries; accumulation can defer synchronization
FSDP / ZeRO-3Parameter all-gather when needed; gradient reduce-scatter under the selected sharding strategy
Tensor parallelismAll-reduce, reduce-scatter or all-gather according to input/output layouts
Pipeline parallelismPoint-to-point sends (not collective) between adjacent stages
Expert parallelism (MoE)All-to-all to route tokens to experts
Embedding lookup at scaleAll-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.