Skip to content

← Production ML — Training Stack step 9 of 12

Hard Primitives

Ring All-Reduce

Implement ring_all_reduce: simulate workers passing tensor chunks around a ring until every worker has the elementwise sum. Return one worker's final result. No processes, network connections or multiple devices are needed.

Inputs and output

worker_tensors has shape (N, M, ...). Axis zero selects one of N workers; each worker holds a tensor of shape (M, ...). N, M and any trailing dimensions are positive, and M is divisible by N. Inputs are finite floating-point values.

Return shape (M, ...), preserving dtype and device and leaving the input unchanged. The result is the sum across workers, not the average and not a stack of N copies. With N=1, return that worker's values without running rounds.

Chunk layout and rounds

Split each worker's tensor along its leading axis into N chunks. A chunk has shape (M/N, ...), so keep trailing dimensions intact. Number workers and chunks from 0 to N-1. Worker i sends to its right neighbor (i+1) modulo N.

Each phase has N-1 rounds, numbered r=0 through N-2. Treat a round as simultaneous: read every outgoing chunk from the buffers at the start of that round and write received data into separate next-round buffers. Do not let a worker forward a value it has just received in the same round.

1. Reduce-scatter: add incoming chunks

In round r, worker i sends chunk (i-r) modulo N. Its right neighbor adds the received values to the same chunk slot in its own buffer. All other slots keep their current values.

After this phase, worker i owns the fully summed chunk (i+1) modulo N. The complete chunks are spread across the workers; other slots may still contain partial sums. The completed chunk is not necessarily chunk i.

2. All-gather: copy completed chunks

In round r, worker i sends chunk (i+1-r) modulo N. Its right neighbor replaces that chunk slot with the received values. Copy in this phase; adding would count contributions again.

After N-1 rounds, every worker has all completed chunks in their original positions. Return worker zero's final buffer. Simulate the two phases using chunk reads, additions and copies rather than replacing the exercise with a direct sum across the worker axis or a collective-library call.

Worked example

For worker 0=[1,2] and worker 1=[3,4], each chunk has one element. Reduce-scatter has one round: worker 0 sends chunk 0 to worker 1, while worker 1 sends chunk 1 to worker 0. Their buffers become [1,6] and [4,4]. Worker 0 now owns completed chunk 1=[6]; worker 1 owns completed chunk 0=[4].

During all-gather, they exchange those completed chunks. Both buffers become [4,6], so return [4,6]. Do not divide by two.

With N workers and S elements per worker, this equal-chunk simulation sends S/N elements per worker in each of 2*(N-1) rounds: 2*(N-1)*S/N in total per worker. This counts transmitted elements, not latency or measured speed.