Seyed Masoud Hosseini · Overview · Study log · Weekly summaries · Ideas · Search · Transcript · RSS feed
Performance Engineering of Software Systems · Lecture 7 of 23 · 1:14:39
Lecture 7: Races and Parallelism
Study guide
What this lecture covers
This lecture develops the formal tools for reasoning about parallel programs written in Cilk: how to detect and avoid determinacy races, and how to measure how much parallelism a program actually has. It builds directly on the previous lecture's introduction to Cilk's cilk_spawn, cilk_sync and cilk_for constructs and the four concurrency platforms.
After watching, you should be able to explain what a determinacy race is and why it doesn't always reproduce, model a Cilk program's execution as a computation DAG, compute its work and span, use those quantities to bound achievable speedup, and describe how Cilk's greedy and work-stealing schedulers achieve near-linear speedup when there is enough parallel slackness.
Key ideas
- Determinacy race: two logically parallel instructions access the same memory location and at least one writes it; the result can differ depending on execution order, and the bug may not appear on every run.
- Read race vs. write race: a race is a read race if one instruction reads and the other writes; a write race if both write. Two code sections are independent if no such races exist between them.
- Computation DAG: a directed acyclic graph of strands (sequential instruction runs with no spawn, sync or return) connected by spawn, call, return and continue edges, built dynamically as a Cilk program executes.
- Work (
T1): total time to run the computation on one processor, equal to the number of nodes (strands) in the DAG. - Span (
T∞): the length of the longest path through the DAG, equal to the execution time on an unbounded number of processors. - Parallelism:
T1 / T∞, the theoretical maximum speedup achievable regardless of processor count. - Work law and span law:
TP ≥ T1/PandTP ≥ T∞, which bound achievable running time onPprocessors. - Greedy scheduler bound: any greedy scheduler achieves
TP ≤ T1/P + T∞, within a factor of two of optimal, and gives near-linear speedup when parallel slackness (T1 / (P·T∞)) is high. - Work stealing: Cilk's scheduler, where an idle worker steals from the top of a random other worker's deque; expected running time is
T1/P + O(T∞).
Walkthrough
Recap of Cilk constructs and race avoidance tips (1:02)
The lecture reviews cilk_spawn/cilk_sync and cilk_for from the prior lecture, then lists rules for avoiding races: iterations of a cilk_for must be independent, and code between a spawn and its matching sync must be independent of the parent's code. It notes that a spawned function's arguments are safely evaluated by the parent before the spawn, and warns that races can appear in packed structs on some architectures (though not on the Intel machines used in the course).
Determinacy races (4:06)
Using real-world examples (the Therac-25 radiation machine and the 2003 North American blackout), the lecture defines a determinacy race and works through a concrete example: two parallel loop iterations each incrementing a shared variable x via load-increment-store, which can interleave to produce a wrong final value. It distinguishes read races from write races and introduces the Cilksan race detector (-fsanitize=cilk), which is guaranteed to report any possible race, unlike best-effort detectors.
Computation DAGs (18:20)
The Fibonacci example from the previous lecture is unfolded step by step into a computation DAG, with spawn edges producing two children (a spawned child and a continuation), call edges producing one child, and return edges passing results back up. The DAG is built dynamically at runtime and is processor-oblivious — nothing in the code or graph mentions the number of processors.
Work, span and Amdahl's law (26:28)
Assuming each strand takes unit time, the lecture first applies Amdahl's law to the example DAG (18 nodes, 3 forced-sequential nodes) to get a loose speedup bound of 6, then introduces the tighter work/span framework: work T1 (18), span T∞ (9), giving parallelism T1/T∞ = 2. It states and justifies the work law and span law, and shows how work and span compose under series and parallel composition of subcomputations.
Speedup types and Cilkscale (38:05)
The lecture defines sub-linear, linear and (in theory, impossible under this model) super-linear speedup, applies the work/span analysis to the Fibonacci DAG (work 17, span 8, parallelism 2.125), and introduces Cilkscale, the Cilk tool that instruments a serial run to report work and span and generate speedup-bound plots. A parallel quicksort example, where the partition step runs sequentially, is analyzed to show its span is Θ(n) and parallelism only Θ(log n) — much lower than a fully parallel implementation would achieve.
Scheduling theory: the greedy scheduler (53:28)
The lecture introduces a centralized greedy scheduler that, at each step, executes as many ready strands as possible (a complete step) or all ready strands (an incomplete step) up to the processor count. Graham's 1968 theorem bounds its running time as TP ≤ T1/P + T∞, proved by bounding the number of complete steps by T1/P and incomplete steps by T∞. Corollaries show the greedy scheduler is within 2x of optimal and achieves near-linear speedup when parallel slackness is at least around 10.
Cilk's work-stealing scheduler and cactus stacks (1:02:40)
Cilk's actual scheduler is a distributed work-stealing scheduler: each worker keeps a deque of ready strands, working from the bottom like a normal call stack, and an idle worker steals from the top of a random victim's deque. Its expected running time is T1/P + O(T∞). A sketch proof relates the expected number of steals to the span. The lecture closes by describing Cilk's "cactus stack," which lets each task see its own stack frame and those of its ancestors, and bounds the total stack space of a P-processor execution by P times the single-processor stack space.
Before you watch
- Watch the previous lecture on multicore programming and Cilk basics (
cilk_spawn,cilk_sync,cilk_for), since this lecture assumes familiarity with that syntax and the Fibonacci example. - Comfort with recursion and basic graph terminology (DAG, path length) will help with the computation-DAG analysis.
Check your understanding
- Why can a determinacy race bug fail to appear on most runs of a program, and why does that make it hard to find through testing?
- In a computation DAG, what is the difference between work and span, and how do they combine to define parallelism?
- Why does Amdahl's law give a looser (less useful) upper bound on speedup than the work/span analysis in the lecture's example?
- Why did the parallel quicksort example achieve only
Θ(log n)parallelism, and what change would increase it? - According to the greedy scheduler theorem, why does high parallel slackness (
T1 / (P·T∞)) lead to near-linear speedup?
Vocabulary
- determinacy race (noun)
- A bug where two parallel operations access the same memory and the result depends on timing.
A determinacy race may not show up on every run. - read race (noun)
- A race where one parallel instruction reads and another writes the same location.
A read race happens when one thread reads while another writes. - write race (noun)
- A race where two parallel instructions both write to the same location.
A write race can silently corrupt shared data. - computation DAG (noun)
- A directed graph representing the order and dependencies of a program's execution.
The Fibonacci program unfolds into a computation DAG. - strand (noun)
- A sequential run of instructions in a computation DAG with no spawn or sync.
Each strand runs without any parallel control points. - span (noun)
- The length of the longest dependency chain in a computation.
Span sets the fastest possible time with unlimited processors. - parallelism (noun)
- The maximum theoretical speedup possible, found by dividing work by span.
Parallelism measures the best possible speedup for a program. - Amdahl's law (noun)
- A formula giving a loose upper bound on speedup based on the sequential fraction of a program.
Amdahl's law gives a looser bound than work-span analysis. - greedy scheduler (noun)
- A scheduler that runs as many ready tasks as possible at every step.
The greedy scheduler runs within a factor of two of optimal. - parallel slackness (noun)
- How much more parallelism a program has than the number of available processors.
High parallel slackness leads to near-linear speedup. - deque (noun)
- A double-ended queue that supports adding or removing items from both ends.
Each worker keeps ready strands in its own deque. - independent (adjective)
- Not depending on or affecting each other.
Loop iterations must be independent to avoid races. - interleave (verb)
- To mix the steps of two processes together in an alternating order.
Two increments can interleave and produce a wrong result. - race detector (noun)
- A tool that automatically finds determinacy races in a program.
The Cilksan race detector reports any possible race. - composition (noun)
- The way smaller parts are combined to form a larger structure.
Work and span compose differently under series and parallel composition. - sub-linear (adjective)
- Growing slower than a direct proportional increase.
Sub-linear speedup is worse than the ideal linear case. - instrument (verb)
- To add measurement code to a program so its behavior can be tracked.
Cilkscale instruments a serial run to report work and span. - centralized (adjective)
- Controlled from one single point rather than spread across many.
The theoretical scheduler discussed first is centralized. - corollary (noun)
- A result that follows naturally and easily from an already proven theorem.
A corollary shows the scheduler is within 2x of optimal. - sketch (verb)
- To outline the main idea of a proof or plan without full detail.
The lecture sketches a proof relating steals to the span.
Chapters
- 0:00 Intro
- 1:01 Recall: Basics of Cilk
- 2:21 Loop Parallelism in Cilk
- 4:12 Race Conditions
- 5:20 Determinacy Races
- 7:02 A Closer Look
- 7:49 Race Bugs
- 10:27 Types of Races
- 12:50 Avoiding Races • Iterations of a cilk_for should be independent. • Between a cilk spawn and the corresponding cilk_sync, the code of the spawned child should be independent of the code of the parent, including code executed by additional spawned or called children.
- 15:42 Cilksan Race Detector
- 18:37 Execution Model
- 24:16 Computation Dag
- 26:35 How Much Parallelism?
- 27:51 Amdahl's "Law"
- 29:20 Quantifying Parallelism What is the parallelism of this computation?
- 31:50 Performance Measures
- 35:11 Series Composition
- 36:36 Parallel Composition
- 38:04 Speedup
- 40:48 Example: fib(4)
- 42:54 Cilkscale Scalability Analyzer
- 43:22 Quicksort Analysis
- 45:53 Cilkscale Output
- 47:56 Theoretical Analysis
- 52:00 Interesting Practical* Algorithms
- 54:45 Greedy Scheduling
- 56:57 Analysis of Greedy
- 59:32 Optimality of Greedy
From the YouTube description
MIT 6.172 Performance Engineering of Software Systems, Fall 2018
Instructor: Julian Shun
View the complete course: https://ocw.mit.edu/6-172F18
YouTube Playlist: https://www.youtube.com/playlist?list=PLUl4u3cNGP63VIBQVWguXxZZi0566y7Wf
Professor Shun discusses races and parallelism, how cilkscale can analyze computation and detect determinancy races, and types of schedulers.
License: Creative Commons BY-NC-SA
More information at https://ocw.mit.edu/terms
More courses at https://ocw.mit.edu
← Lecture 6: Multicore Programming · Lecture 8: Analysis of Multithreaded Algorithms →
