Seyed Masoud Hosseini · Overview · Study log · Weekly summaries · Ideas · Search · Transcript · RSS feed

Performance Engineering of Software Systems · Lecture 20 of 23 · 1:23:03

Lecture 20: Speculative Parallelism and Leiserchess

20. Speculative Parallelism & Leiserchess on YouTube

Study guide

What this lecture covers

The lecture asks how to get parallel speedup from code, like alpha-beta search, that is inherently serial because pruning depends on the order of evaluation. The answer is speculative parallelism: guessing that work will be needed, doing it in parallel anyway, and aborting cleanly when the guess is wrong. The second half applies this directly to the Leiserchess course project, walking through parallel alpha-beta search and a long list of optimizations available in the codebase.

This lecture continues directly from the prior lecture's introduction to the Leiserchess project and its search code. After watching, you can explain why naive parallel alpha-beta wastes work, describe the young-siblings-wait technique for parallelizing it safely, and recognize which of several existing search optimizations (transposition table, killer moves, iterative deepening, endgame databases, null-move pruning, Zobrist hashing) matter most for the project.

Key ideas

  • Speculative parallelism: spawning parallel work that a serial execution might not have performed, betting it will be needed, with a way to abort it cheaply if it turns out not to be.
  • Threshold sum example: a short-circuiting parallel sum uses an abort flag, checked before being set (to avoid unnecessary cache-line contention) and requiring no memory fence, since the flag only ever transitions false-to-true and either observed value keeps the algorithm correct.
  • Rule of thumb for speculation: don't spawn speculative work unless there's little other parallelism available and a good chance the work will be needed; speculating on all branches of a minimum-finding search, for example, can look like superlinear speedup but is really wasted work compared to a better serial algorithm.
  • Best-ordered game trees and the young-siblings-wait algorithm: in a best-ordered tree a node's effective branching factor is either 1 (a cutoff) or maximal (full exploration); the parallelization strategy searches the first child, and only if it fails to produce a cutoff does it speculatively search the remaining "young sibling" children in parallel.
  • Abort propagation: each node tracks whether an ancestor has aborted; workers periodically (not on every step) check up the tree so wasted speculative work stops without paying the cost of checking on every node.
  • Shared search data structures under parallelism: the transposition table, killer-move table, and best-move table are all shared, mutable structures, and each requires a separate decision about locking, per-worker replication, or accepting a benign race for performance.
  • Iterative deepening: searching depth 1, then 2, then 3, and so on costs only a constant factor more work in total, but produces progressively better move-ordering information (via the transposition table) that makes each deeper search prune far more effectively.
  • Zobrist hashing: a position's hash is the XOR of per-piece-per-square random values; because XOR is its own inverse, moving a piece updates the hash with just two XOR operations instead of recomputing from scratch.
  • Distance-to-mate encoding: endgame databases and mate scores must track distance to mate, not just win/loss/draw, otherwise search can cycle indefinitely among winning positions without ever converging on checkmate.

Walkthrough

Speculative parallelism and the threshold-sum example (2:02)

The lecture opens by noting that code like alpha-beta search is inherently serial, since parallelizing it naively causes cutoffs to be missed and wasted work to be performed. It introduces speculative parallelism as the general fix, then works through a short-circuiting parallel sum: strip-mining reduces the overhead of checking a threshold every iteration, and a shared abort flag lets parallel branches stop early. The lecture explains why checking the flag before writing it avoids unnecessary cache contention, and why no memory fence is needed since the flag only transitions one direction.

When speculation pays off (12:20)

The lecture states the rule of thumb: only spawn speculative work when there's little other parallelism available and a good chance it will be needed, illustrating with a cautionary example of researchers claiming superlinear speedup from speculatively searching for a minimum in parallel, when a better serial algorithm (dovetailing) would have been more efficient.

Parallelizing alpha-beta with the young-siblings-wait algorithm (14:24)

Building on the Knuth-Morris result that best-ordered alpha-beta search examines roughly the square root of the naive node count, the lecture presents the young-siblings-wait technique from a paper by Burkhardt and colleagues: search the first child serially, and only if it fails to generate a cutoff, speculatively search the remaining children in parallel, since a best-ordered tree either fully explores all children or cuts off after the first. Each node periodically checks up the tree for an aborted ancestor so unnecessary speculative work can stop.

Managing shared search data structures (22:34)

The lecture discusses three shared, mutable data structures the parallel search touches: the transposition table, killer-move table, and best-move table, each requiring its own choice between locking, replication, or accepting a race. Leiserson recounts his own program's decision, in a past world computer chess championship, to run with an unguarded race on the transposition table after estimating the odds that a race actually changes the outcome of a game were vanishingly small, since locking every access would have been more costly. He emphasizes having a way to disable speculation and transposition-table access entirely so other parts of the program can be tested deterministically.

Iterative deepening, endgame databases, and other search techniques (38:54)

The lecture explains why iterative deepening (searching depth 1, 2, 3, ... instead of jumping straight to the target depth) is worthwhile: it costs only a constant factor of extra total work but yields much better move ordering at each depth via the transposition table. It covers endgame databases (illustrated with king-versus-king endings), stressing that a database must store distance-to-mate rather than a simple win/loss/draw value to avoid the search cycling between winning positions forever. It also covers quiescence search, null-move pruning (and its failure mode in zugzwang-like positions), and late-move reductions, which search moves further down a sorted move list at reduced depth since they're less likely to be good.

Zobrist hashing and the transposition table in detail (54:11)

The lecture re-explains Zobrist hashing: a table of precomputed random numbers indexed by square, piece type, and orientation, XORed together to hash a position, updated incrementally with just two XORs per move since XOR is its own inverse. It also covers the transposition table's quality score (based on search depth) and the special handling needed to convert a stored "mate in N" value into the correct mate distance from the root of the current search.

Board representation and the laser-coverage heuristic (1:02:24)

The lecture surveys board representation choices, bit boards using shift operations for fast move generation versus piece lists that shrink as the game progresses, and recommends refactoring all board accesses through a function before changing representation so the old and new versions can be validated against each other. It closes with a detailed walkthrough of the laser-coverage evaluation heuristic, which estimates how close a side's laser can get to the opponent's king across all one-move lookahead paths, and flags it as one of the most expensive parts of the evaluation function worth optimizing, along with move sorting.

Before you watch

  • Watch the prior lecture in this course, "Leiserchess Codewalk," which introduces the game, the board and move representation, and alpha-beta and principal variation search.
  • Familiarity with races, locks, and memory fences from earlier lectures in this course is assumed, since the speculative-parallelism examples depend on them.
  • Basic exposure to XOR-based bit tricks helps with the Zobrist hashing explanation.

Check your understanding

  1. Why is naive parallelization of alpha-beta search likely to waste processor time compared to serial execution?
  2. In the young-siblings-wait algorithm, under what condition does the search speculatively parallelize the remaining children, and why does that condition follow from best move ordering?
  3. Why doesn't the threshold-sum abort flag need a memory fence, even though it's read and written by multiple parallel branches?
  4. Why is it cheaper to search depth 1, then 2, then 3, and so on (iterative deepening) than to search directly to the target depth, despite appearing redundant?
  5. Why must an endgame database store distance-to-mate rather than a simple win/loss/draw value?

Vocabulary

speculative parallelism (noun)
Doing extra work in parallel that might not actually be needed, betting it will be useful.
Speculative parallelism guesses that a branch will be needed and computes it early.
inherently serial (adjective)
Describing a process that fundamentally needs to run in order, one step after another.
Alpha-beta search is inherently serial because pruning depends on evaluation order.
abort (verb)
To stop an operation before it finishes, usually because it is no longer needed.
The program can abort wasted speculative work once a cutoff is found.
short-circuit (verb)
To stop a computation early once the final result is already certain.
The threshold sum can short-circuit once the total passes a limit.
strip-mining (noun)
Splitting a loop into blocks so a check is done less often, reducing overhead.
Strip-mining reduces how often the threshold check runs.
superlinear speedup (noun)
A speedup greater than the number of processors used, which usually signals a flaw in the comparison.
The claimed superlinear speedup actually came from wasted parallel work.
dovetailing (noun)
A technique that interleaves multiple approaches so useful work isn't wasted while waiting.
A better serial algorithm using dovetailing would have been more efficient.
branching factor (noun)
The number of choices or child nodes at each step of a search tree.
A best-ordered tree's branching factor is either 1 or the full number of children.
young-siblings-wait (noun)
A parallel search technique that explores the first child before speculatively exploring the rest.
Young-siblings-wait searches the first move seriously before trying the others in parallel.
abort propagation (noun)
The process of signaling to related tasks that a search should stop because it's no longer needed.
Abort propagation lets workers check up the tree and stop wasted work.
shared data structure (noun)
A structure in memory that multiple threads read and write at the same time.
The transposition table is a shared data structure needing careful handling.
replication (noun)
Making separate copies of data for each thread instead of sharing one copy.
Replication of the killer-move table avoids contention between threads.
benign race (noun)
A data race that is technically unsafe but doesn't meaningfully affect the correctness of the result.
The team accepted a benign race on the transposition table for speed.
iterative deepening (noun)
A search method that repeatedly searches to greater depths, one level at a time.
Iterative deepening searches depth 1, then 2, then 3, improving move order each time.
endgame database (noun)
A precomputed table storing the correct outcome for positions with few pieces left.
The endgame database must track distance to mate, not just win or loss.
distance-to-mate (noun)
A count of how many moves remain until a forced win is achieved.
Distance-to-mate encoding prevents the search from looping between winning positions.
zugzwang (noun)
A situation in a game where any move a player makes worsens their position.
Null-move pruning can fail in zugzwang-like positions.
late-move reduction (noun)
Searching moves later in a sorted list with less effort since they're less likely to be good.
Late-move reduction saves time on moves unlikely to be the best choice.
bit board (noun)
A way of representing a board using binary numbers, where each bit marks a square's state.
Bit boards use fast shift operations for move generation.
piece list (noun)
A data structure that lists the positions of all active pieces instead of describing the whole board.
A piece list shrinks in size as pieces are removed during the game.
refactor (verb)
To restructure existing code without changing what it does, usually to improve it.
The lecture recommends refactoring board accesses through one function first.
laser coverage (noun)
A heuristic estimating how much of the board a player's laser could threaten.
Laser coverage is one of the most expensive parts of the evaluation to compute.

Chapters

From the YouTube description

MIT 6.172 Performance Engineering of Software Systems, Fall 2018
Instructor: Charles Leiserson
View the complete course: https://ocw.mit.edu/6-172F18
YouTube Playlist: https://www.youtube.com/playlist?list=PLUl4u3cNGP63VIBQVWguXxZZi0566y7Wf

Prof. Leiserson discusses speculative parallelism and its applications in parallel alpha-beta search and jamboree search. The lecture ends with a discussion of computer-chess programs.

License: Creative Commons BY-NC-SA
More information at https://ocw.mit.edu/terms
More courses at https://ocw.mit.edu

← Lecture 19: Leiserchess Codewalk · Lecture 21: Tuning a TSP Algorithm →