Parallel Algorithms: Work, Span & Decomposition
How to expose independent work as a dependency DAG, derive processor bounds from work and span, reason about speedup and Amdahl's limit, and turn reductions, scans, and sorting networks into efficient parallel schedules.
A parallel algorithm is not a sequential loop with more threads. It is a dependency design: split the computation into operations, state which results each operation needs, and expose every operation that may run concurrently. Work measures the total computation; span measures the longest unavoidable dependency chain. Only after both are sound does processor count enter the analysis.
The computation DAG #
Represent one execution as a directed acyclic graph (DAG):
- a vertex is a unit-time operation in the abstract model;
- an edge means cannot execute until completes;
- sources are initially ready; a sink represents completion.
Independent vertices may run together. The model describes semantic dependencies rather than a particular assignment of tasks to workers.
flowchart LR
A1["a+b"] --> C1["(a+b)+(c+d)"]
A2["c+d"] --> C1
A3["e+f"] --> C2["(e+f)+(g+h)"]
A4["g+h"] --> C2
C1 --> Z["total"]
C2 --> Z
For this eight-value reduction, the DAG has seven additions but only three dependent layers.
Any execution on processors obeys two lower bounds:
The first is a capacity bound; processors cannot perform more than operations per step. The second is a dependency bound; processors cannot shorten a causal chain. Therefore
Brent's scheduling principle supplies the matching idealized upper shape: schedule ready operations greedily and
The useful consequence is not an exact hardware prediction. It is a design target: keep work near the best sequential algorithm and make span much smaller than work.
Work efficiency comes before speedup #
A parallel algorithm is work-efficient when is asymptotically no worse than a good sequential algorithm for the same problem. An algorithm with tiny span but work may lose to a sequential algorithm even on many processors.
Define measured speedup and efficiency as
Use the same implementation quality and workload when interpreting them. Apparent superlinear speedup often signals a changed cache regime, memory capacity, algorithm, or baseline—not more than units of abstract compute per step.
The ratio estimates the useful processor ceiling. When , the span bound dominates and extra processors mostly wait. The exact crossover arrives earlier once scheduling, communication, and memory costs are included.
Worked example: reduction #
A left-to-right sum has
Reassociating an associative operation into a balanced binary tree preserves but reduces span to . The algorithm now exposes roughly average parallelism.
At level 0, add adjacent input pairs; at level 1, add adjacent pair-sums; continue until one result remains. If , each level can process its ready operations in batches, and the bound captures both limited throughput and the logarithmic chain.
Prefix scan: parallelizing an apparent chain #
Given an associative operator , identity , and input , an exclusive scan returns
Each prefix appears to depend on the previous one, but Blelloch's tree algorithm separates the computation:
- up-sweep: build partial reductions in a balanced tree;
- set the root to ;
- down-sweep: propagate the reduction of all preceding leaves to each child.
For a power-of-two length, both passes have total work and span. Padding or an irregular tree handles other lengths. The scan is work-efficient and becomes a building block for stream compaction, radix partitioning, polynomial evaluation, sparse allocation, and many graph algorithms.
A tempting alternative computes every prefix with its own parallel reduction. Its span is still logarithmic, but its total work is ; it spends processors to hide an avoidable algorithmic cost. Work/span analysis exposes that failure immediately.
Decomposition patterns #
| Pattern | Independent units | Dependency risk | Typical load-balancing issue |
|---|---|---|---|
| Data parallel | chunks or elements | boundary exchange, reductions | unequal data cost per element |
| Divide and conquer | recursive subproblems | join after children | irregular subtree sizes, tasks too fine |
| Pipeline | stages on different items | bounded buffers and stage order | slowest stage limits throughput |
| Task graph | heterogeneous operations | arbitrary DAG edges | scheduling and placement overhead |
| Speculative | alternative paths or guesses | commit/validation order | wasted work and cancellation |
Good decomposition separates three questions:
- Coverage: does every required unit of work execute exactly as the algorithm requires?
- Dependencies: are all read-after-write, write-after-read, and write-after-write constraints represented?
- Granularity: is each task large enough to amortize spawn, queue, synchronization, and cache costs?
Removing a true dependency creates a race or wrong result. Adding a false dependency preserves correctness but lengthens span. That is why dependency analysis—not thread count—is the core algorithmic skill.
Scheduling irregular task trees #
Static equal-size partitions work when task costs are predictable. Recursive search, sparse graphs, and adaptive meshes create uneven subtrees. A work-stealing scheduler lets an idle worker take ready work from another worker's deque.
Cilk's analysis formalizes this for structured fork–join computations: under its assumptions, randomized work stealing achieves expected running time of the form
The theorem is conditional on the computation model, scheduler, and cost assumptions. Blocking I/O, priority constraints, unstructured futures, memory contention, or tasks whose runtime changes with placement can fall outside it. Work stealing balances ready tasks; it does not remove an algorithm's critical path or memory bottleneck.
Amdahl's limit and scaling the question #
If a fraction of a fixed workload remains serial and the rest parallelizes perfectly, normalized time on processors is
so speedup is
With , unlimited processors cannot exceed speedup in this model. The serial fraction includes not only visibly sequential code but also critical-path dependencies and overhead that does not shrink with .
Amdahl's law answers strong scaling: how fast can the same problem instance run? Weak scaling grows the problem with processor count and asks whether time stays controlled. Larger instances may expose more parallel work, but scaling the input does not repeal dependencies; it changes the experiment. Always state which quantity is held fixed.
Sorting networks: a schedule in hardware-like form #
A comparator takes two values and emits (min,max). A sorting network connects fixed comparators on fixed wires; its comparison pattern is independent of input values.
- size is the number of comparators, analogous to work;
- depth is the number of comparator layers when disjoint comparators in one layer run together, analogous to span.
Batcher's bitonic sorting network sorts powers of two with comparators and depth. It uses more work than optimal sequential comparison sorting but offers a regular, branch-free, data-oblivious schedule. That trade can be attractive in circuits, SIMD-like execution, and settings where a fixed access pattern matters.
Network correctness has two parts: each comparator preserves the multiset, and the full network orders every input. The zero–one principle reduces the latter proof for comparison networks to binary inputs—but does not make exhaustive testing practical for large without further structure.
The hardware model eventually matters #
Work/span abstracts away costs that often dominate:
- cache misses, memory bandwidth, NUMA placement, and false sharing;
- message latency, bandwidth, and collective communication;
- synchronization, atomic operations, barriers, and task creation;
- vector width and divergence;
- replication, retries, and failures in distributed execution.
Two DAGs with identical and can perform very differently if one streams contiguous memory and the other issues dependent random loads. Refine the model with block transfers, communication volume/rounds, or measured per-task costs when those resources are load-bearing. See CPU caches for the locality layer and memory models for the execution semantics of shared state.
Parallel-algorithm audit #
- What are the DAG vertices and true dependency edges?
- What are , , and in the declared cost model?
- Is the parallel algorithm work-efficient against the best relevant sequential baseline?
- Which operator laws justify reassociation or reordering?
- What is the worst load imbalance, and can idle workers reach ready work?
- Is task grain large enough to pay for scheduling and synchronization?
- Does the claimed speedup use strong or weak scaling and the same baseline?
- Which serial fraction or critical path sets the Amdahl/span ceiling?
- Are bandwidth, cache, NUMA, or communication costs absent from the abstract proof but dominant in the implementation?
- Is output deterministic, reproducible within a tolerance, or deliberately schedule-dependent?
The central optimization order is: remove avoidable work, shorten genuine dependencies, expose enough coarse ready tasks, and only then tune their placement on a real machine.
Sources
- paperBrent — The Parallel Evaluation of General Arithmetic Expressions
- paperBlelloch — Prefix Sums and Their Applications
- paperAmdahl — Validity of the Single Processor Approach to Achieving Large Scale Computing Capabilities
- paperBlumofe et al. — Cilk: An Efficient Multithreaded Runtime System
- paperBatcher — Sorting Networks and Their Applications