A deep, wide, out-of-order pipeline has a terrible enemy: the branch. When the machine
meets if (x < 10), it does not yet know whether execution goes down the "then" path or the
"else" path — the condition may depend on a value still crawling back from memory. But the front end can't
just wait: a modern pipeline is fifteen-plus stages deep and fetches several instructions per
cycle, so stalling until the branch resolves would leave dozens of instruction slots empty. That is the
So the processor does something audacious: it guesses. It predicts which way the branch
will go and charges ahead fetching and executing down the predicted path, speculatively. If the guess is
right — and modern predictors are right well over
The simplest predictors don't learn at all. Static prediction makes the same guess every time, decided at compile time or by a fixed rule:
| Static scheme | Rule | Typical accuracy |
|---|---|---|
| Predict not-taken | always assume the branch falls through | ~50–60% |
| Predict taken | always assume the branch jumps | ~60–70% |
| BTFN (backward-taken, forward-not) | loops branch backward and usually repeat, so predict backward branches taken | ~65–80% |
BTFN is clever — the branch at the bottom of a loop jumps backward and is taken on every iteration but the last, so "backward = taken" is right most of the time. But static schemes can't adapt to a branch that changes its mind. For that we need to remember history.
The simplest dynamic predictor keeps a single bit per branch: "last time, was it taken?" Predict the same as last time; when the branch resolves, update the bit. This is a branch history table (BHT) — a small array of counters indexed by the branch's address. A 1-bit predictor tracks a loop beautifully in the steady state… until you notice it mispredicts twice per loop. Consider a loop that runs 10 times:
One bit remembers only the single most recent outcome, so a lone exception (the loop-exit) flips it, and then the next entry pays for that flip. Two mispredicts every time the loop runs. We want a predictor with a little inertia — one that ignores a single anomaly.
The fix is a 2-bit saturating counter: a small state machine with four states — Strongly Not-taken (00), Weakly Not-taken (01), Weakly Taken (10), Strongly Taken (11). Each taken outcome nudges the counter up (saturating at 11); each not-taken nudges it down (saturating at 00). The prediction is just the top bit: states 10 and 11 predict taken, states 00 and 01 predict not-taken. The word saturating means it never wraps past the ends — 11 stays 11 on another taken.
The genius is the two "weak" middle states acting as a buffer. A branch sitting in Strongly Taken (11) that hits one surprise not-taken drops only to Weakly Taken (10) — still predicting taken. It takes two consecutive not-takens to actually flip the prediction. So a single anomaly (like a loop exit) is absorbed without changing the guess for the next iteration.
Rerun the 10-iteration loop with a 2-bit counter starting in Strongly Taken (11):
The 2-bit counter mispredicts only the exit of each loop pass (one miss per ten), where the 1-bit version missed twice. On a loop that iterates many times, a 2-bit predictor approaches near-perfect accuracy. This tiny FSM, replicated thousands of times in a table, was the workhorse of 1990s branch prediction and is still the building block inside fancier predictors.
The BHT is a fixed-size array, so the low bits of the branch's address pick the entry — and two different
branches whose addresses share those low bits land on the same counter. This is
aliasing (or interference): the two branches stomp on each other's history, and if they
behave differently, both suffer. Bigger tables reduce it; hashing the address in more cleverly reduces it
more. Aliasing is a recurring headache that the
A branch predictor as described answers only "taken or not-taken?" — a single bit of direction. But to actually fetch down the taken path, the front end also needs the target address, and it needs it in the same cycle, before the branch has even been decoded. That's a separate structure — the branch target buffer (BTB), a cache from branch address to predicted target — covered in the next lesson. Don't conflate them: direction prediction (this lesson) and target prediction (the BTB) are two different jobs the front end must do at once.
Accuracy isn't a vanity metric — it feeds straight into performance. If a branch is mispredicted with
probability
With