Parallel algorithms use multiple processors to solve a problem faster.
Idea: split the work into independent tasks that can run at the same time.
We can use the fork–join model:
spawn = create a parallel task
sync = wait for tasks to finish
Key challenge: more processors doesn’t always mean faster — depends on how tasks are divided.
2. How Do We Measure Efficiency? (1 min)
Work (T1): total number of operations (like runtime on one core).
Span (T∞): longest chain of dependent tasks — the “critical path.”
Parallelism:T∞T1 = maximum possible speedup.
Goal kinda: keep span small and balance work across processors.
3. Example: Parallel Fibonacci (1.5 min)
parallel_fib(n): if n <= 2: return 1 else: x = spawn parallel_fib(n-1) y = parallel_fib(n-2) sync return x + y
Work:T1(n)=O(2n) (huge, exponential).
Span:T∞(n)=O(n) (linear).
Parallelism:n2n — looks amazing on paper.
But in practice: too much redundant work, so it’s inefficient. (it prevents Memoization instead)
This example just shows how span vs work can differ dramatically.
4. Example: Parallel Merge Sort (1.5–2 min)
Idea:
Split the array into halves in parallel.
Recursively sort each half in parallel.
Merge results in parallel.
Work:O(nlogn) (same as regular merge sort).
Span:O(log2n) (because merging takes O(logn) and happens at logn levels).
Parallelism:O!(lognn) → excellent for large inputs.
Unlike Fibonacci, this one actually scales well in practice.
(Draw recursion tree splitting arrays, with arrows showing parallel merges.)
5. Wrap-Up (30 sec)
Parallel algorithms are about reducing the span so many processors can work together.
Work is like total effort, span is like bottleneck time.
Fibonacci shows theory vs practice; merge sort shows the right balance.
In real systems, overhead from spawning too many tiny tasks must be managed — that’s where scheduling strategies come in.