Parallel patterns: map, reduce, scan
Most parallel programs are combinations of three shapes — map, reduce and scan. Their work and span tell you the speedup before you write a line of code.
Most parallel algorithms are built from a startlingly small vocabulary. You map a function over a collection, independently. You reduce a collection to one value by combining pairs. You scan a collection to produce all the running prefixes. Merge sort is divide-and-reduce; a histogram is map then reduce; a convolution is pattern-dependent map. Learn these three shapes and a surprising amount of parallel programming becomes "which combination is this?"
The vocabulary matters because these shapes have known costs. For each, you can compute two numbers — work (total operations, the cost on one processor) and span (the longest dependency chain, the cost on infinite processors) — and those two numbers bound the achievable speedup before you build anything. This is the discipline that separates parallel design from parallel fiddling.
Work and span
Work is how much total effort the computation does. Span is the length of the longest chain of dependent steps. With processors, your runtime is at least — you cannot go faster than the work divided by the processors, and you cannot go faster than the dependency chain. Parallelism is : the average number of operations available to run at once.
Resize the array and switch between reduction and scan below.
Tree reduction — pairs collapse toward one total
work (additions)
15
span (parallel steps)
4
vs sequential
3.8× ideal
work / span = 3.8 is the most parallelism this schedule can expose — real speedup is lower once memory and scheduling overhead are paid. The sequential baseline shown is 15 additions.
Reduction is work-optimal (n-1 additions) and is the canonical map- reduce combine step. Hillis-Steele scan trades work — n·log2(n) additions — for the same logarithmic span, which is a good deal on wide hardware and a bad one on a CPU. When work matters, a Blelloch up-sweep/down-sweep scan gets an optimal n-1 total at the cost of two passes. Arrays are small and illustrative.
Map
Map applies a pure function to every element:
Work is calls to ; span is — every element is
independent, so with enough processors the whole map takes one function's
time. Map is embarrassingly parallel and the easiest pattern to get right,
precisely because there are no dependencies and therefore no races. In
practical libraries (OpenMP, std::for_each, CUDA) a map is where you start.
Reduce
Reduce combines a collection to a single value with an associative operator :
Associativity is the licence to reorder. A sequential reduce does combines in a chain of span — no parallelism at all. A tree reduction pairs neighbours and collapses level by level:
The work is the same but the span shrinks from to , so the parallelism jumps from roughly 1 to . That is the entire trick: reassociation turns a chain into a tree. It is why floating-point sum is not bit-identical to the sequential answer — changing the grouping changes the rounding — a genuine, accepted cost of parallel reduction.
Scan
A prefix scan computes all prefixes of the operator:
It is the sequence operation behind streams compaction, radix sort, run-length encoding and many graph algorithms. A sequential scan is again a chain of span . The Hillis-Steele algorithm gets span by, at each step , adding each element's value from positions back:
The catch is work: every one of the steps touches all elements, so — a factor of redundant work. Blelloch's scan does an up-sweep (a reduction tree) and a mirrored down-sweep, achieving optimal work at span , at the cost of two passes and more code. The choice is the classic work-versus-span trade: Hillis-Steele is simpler and fine when processors are plentiful, Blelloch when work matters.
Putting the numbers together
Brent's theorem bounds the runtime on processors:
The term is the work divided across processors; the term is the unavoidable serial tail. Speedup is then bounded by both the work term and the span:
You cannot beat (there are only so many processors) and you cannot beat (there is only so much parallelism). A reduce with has parallelism about , so a 64-core machine is nowhere near the span limit. A scan algorithm whose span is on the same data has parallelism , still comfortable. But a naive program with a serial fraction of 5% has no matter how large — the chain, not the work, is the wall.
Careful
Associativity is not commutativity. Parallel reduce and scan may reorder operands freely; they may not swap them. With floating-point that is the difference between losing precision and getting a different, sometimes nonsensical, answer. And these patterns assume a pure combine — if the operator has side effects or hidden state, the reordering that gives you span is also the reordering that breaks correctness.
Illustrative vs real
The tree shown reduces a small power-of-two array with integer addition, so the arithmetic is exact and visible. Real reductions handle non-power-of-two sizes, use vectorised loads, and face memory bandwidth long before the span bound. The work and span counts are exact for the schedules drawn; the constants that decide real runtime are not.
Check yourself
Eduspheria wiki · Systems for AI, Concurrent programming
0 / 5 answered
From the exam paper
Modeled on NITJ AI-619, End-Sem June 2025
0 / 5 answered
Where next: into the serving stack — containers, orchestration, and turning a model into a service.