4.1.21 · D4Computer Architecture (Deep)

Exercises — Branch prediction — static, dynamic (2-bit predictor, BTB)

3,241 words15 min readBack to topic

Throughout, we reuse the parent's master formula. Let us restate it so nothing is unnamed:


L1 — Recognition

Exercise 1.1 (L1)

A 2-bit saturating counter has four states written as two bits: . Which of these states predict Taken, and why is it exactly the top bit that decides?

Recall Solution

States and predict Taken. States and predict Not-taken. Why the top bit: the counter counts confidence in "taken" from (never) up to (always). Written in binary, = 10 and = 11 — both have a leading ; = 00 and = 01 — both have a leading . So the most significant (top) bit is exactly "is the count ?", which is exactly "do we lean Taken?". Reading one bit is free in hardware, which is the whole point.

Exercise 1.2 (L1)

For each branch below, what does BTFNT (Backward-Taken, Forward-Not-taken) predict? (a) A branch that jumps backward to the top of a loop. (b) A forward branch that skips an error-handling block.

Recall Solution

(a) Taken. Backward jump = loop bottom returning to the top → taken almost every iteration. (b) Not-taken. Forward jump that skips code = usually an if/error path that doesn't fire → falling through is the right bet.

Exercise 1.3 (L1)

Match each job to the right hardware piece: "decide the direction (taken vs not-taken)" and "supply the target address during IF".

Recall Solution
  • Direction → the 2-bit saturating counter (the prediction bits).
  • Target address in IF → the BTB (Branch Target Buffer). They are two different answers to two different questions; you need both for a taken branch to cost zero cycles.

L2 — Application

Exercise 2.1 (L2)

A pipeline has , , and a predictor with accuracy (so ). Compute the CPI.

Recall Solution

Plug into : What we did: multiplied the three independent quantities (how often a branch, how often wrong, cost each time), then added to the ideal . Branches cost only overhead.

Exercise 2.2 (L2)

Same machine (, ) but now use naïve predict-not-taken where loop-heavy code makes . Compute the CPI and the percentage slowdown vs Exercise 2.1.

Recall Solution

Slowdown vs : What we did: the only thing that changed is (accuracy). A worse predictor turned a tax into a tax — a real slowdown. This is the 80/20 payoff of good prediction.

Exercise 2.3 (L2)

A loop runs a body times, then exits. Using static BTFNT, how many times does the backward branch mispredict per full loop entry?

Recall Solution

BTFNT predicts the backward branch Taken every time. The branch is taken on iterations through (returning to the top) and not-taken once — the exit (iteration ).

  • Correct on taken outcomes.
  • Wrong on the exit. Why: the loop exit is the single event a static scheme can never anticipate — it's the irreducible cost.

L3 — Analysis

Exercise 3.1 (L3)

A 1-bit predictor runs a loop of length many times back-to-back. In steady state (after the loop has already run at least once), explain precisely why it mispredicts twice per full loop execution, and identify the two mispredicted outcomes.

Recall Solution

A 1-bit predictor stores exactly one bit and simply copies the last outcome as its next prediction. We do not need to assume any particular starting value: after the loop's first pass the bit is guaranteed to be left in a definite state, so we analyse the steady state — how every pass behaves once the loop is already running repeatedly.

Why the steady-state entry bit is "Taken": each pass ends with the exit outcome Not-taken, which sets the bit to Not-taken; but then the exit is itself the last event before the next entry... so let us trace carefully. During a pass the outcomes are Takens followed by Not-taken. The final Taken (iteration ) sets the bit to Taken, and the exit (iteration , Not-taken) then sets it to Not-taken. So at the moment the loop re-enters, the bit is Not-taken — carried in from the previous exit. Trace one steady-state pass with that carried-in bit:

  • Re-entry, iteration : outcome Taken, but bit says Not-taken (carried from last exit) → ✗ (miss #1). Bit flips to Taken.
  • Iterations : outcome Taken, bit says Taken → ✓. Bit stays Taken.
  • Iteration (exit): outcome Not-taken, bit still says Taken → ✗ (miss #2). Bit flips to Not-taken, ready to poison the next re-entry.

The two misses are the re-entry and the exit, and this pattern repeats identically every pass — that is what "steady state" guarantees.

Figure — Branch prediction — static, dynamic (2-bit predictor, BTB)

Figure description (for text-only readers): the figure shows eight branch executions in a row for a loop of length run twice: outcomes T, T, T, N (exit) then T, T, T, N (exit). Each dot is coloured green when the prediction was correct and red when it was a miss. Above each dot is the prediction the 1-bit bit made (pred T/pred N); below is OK or MISS. A dashed yellow line marks where the loop re-enters. The steady-state pattern shows two red dots per pass — one at the exit and one at the following re-entry — because the exit's Not-taken poisons the bit for the next entry.

Root cause: the 1-bit predictor changes its mind after one surprise, so the exit "poisons" its memory for the next entry. That is exactly the flaw the second bit fixes.

Exercise 3.2 (L3)

Take the same repeating loop of length , but now use a 2-bit saturating counter starting in state (Strongly Taken). Show that it mispredicts only once per loop, and state which single outcome is wrong.

Recall Solution

On Taken → increment (saturate at ). On Not-taken → decrement. Top bit is the prediction; recall and both predict Taken.

  • Iterations : Taken, state , predict Taken → ✓. Stays (already saturated).
  • Iteration (exit): Not-taken, state predicts Taken → ✗ (the one miss). State decrements .
  • Next entry, iteration : Taken, state still predicts Taken → ✓. Increments .

The only miss is the exit; re-entry is now correct because hasn't crossed below the top-bit threshold.

Figure — Branch prediction — static, dynamic (2-bit predictor, BTB)

Figure description (for text-only readers): the figure plots the counter's numeric state (0=00, 1=01, 2=10, 3=11) across the same eight executions T, T, T, N, T, T, T, N. The upper band (states 10/11) is shaded green and labelled "predict TAKEN"; the lower band (00/01) is shaded red and labelled "predict NOT-taken", with a dotted threshold line between them. The counter sits at 11 through the three taken iterations, dips to 10 on the exit (the single red MISS), then climbs straight back to 11 — never crossing into the red "predict NOT-taken" band, so re-entry stays correct.

Conclusion: 2-bit halves the loop misprediction count () purely by requiring two surprises to flip.

Exercise 3.3 (L3)

A benchmark has , . A 1-bit predictor gives ; a 2-bit predictor gives . Compute CPI for each and the speedup of 2-bit over 1-bit.

Recall Solution

1-bit: . 2-bit: . Speedup (time is proportional to CPI): What we did: only differs; a better accuracy shaved the branch tax roughly in half, giving ~ more throughput.


L4 — Synthesis

Exercise 4.1 (L4)

A CPU designer deepens the pipeline. Branch resolution moves from EX (penalty ) to a later stage giving . With and predictor accuracy (), the deeper pipeline also raises the clock frequency by . Does the deeper pipeline run a program faster? Compare execution time.

Recall Solution

Execution time . Convention: we normalize the shallow machine's clock to unit — i.e. set shallow frequency so its cycle time is time-unit. The deep machine is then faster clock, so frequency (its cycle time is time-units). All "time" numbers below are in these shallow-cycle time-units, so they are directly comparable.

  • Shallow: . Time .
  • Deep: . Time .

Speedup of deep over shallow: What we did: deeper pipeline raises (more CPI) but the higher clock more than compensates given good prediction. The whole strategy only works because stayed low — connect this to why Pipeline Depth and CPI makes prediction accuracy critical.

Exercise 4.2 (L4)

For the deep machine of 4.1 (, , frequency ), find the break-even misprediction rate at which the deep pipeline's execution time equals the shallow one's (, time ).

Recall Solution

We need deep time = shallow time (both in the same shallow-cycle time-units from 4.1): Multiply both sides by : Now substitute and . Note the key simplification: the coefficient of is exactly, so the whole penalty term collapses to just : Interpretation: the deep pipeline only loses if its predictor is worse than ~ mispredictions — catastrophically bad. Any realistic predictor keeps the deep design ahead.


L5 — Mastery

Exercise 5.1 (L5)

A program's branches split into two classes:

  • Loop branches: of all branches, each in a loop of average length , so each loop entry is taken + not-taken. A 2-bit predictor mispredicts each such loop once per entry.
  • Data-dependent branches: of all branches, essentially random, mispredicted of the time.

Compute the overall misprediction rate across all branch dynamic executions, then the CPI given , .

Recall Solution

Step 0 — what is a "dynamic execution", and what will we weight by? A static branch is one line in the source code; the split counts these static lines. But CPI is charged every time a branch actually runs at runtime — a dynamic execution. A loop branch written once runs times per loop entry; a data branch runs once. So to average miss rates correctly we must weight each class by its dynamic-execution weight = (its share of static branches) × (how many times each runs). We call this weight a share unit: it is simply "expected number of dynamic branch executions contributed by this class, per unit of program", and it is what the misprediction penalty is actually charged against.

Step 1 — per-class miss rate. Per loop entry: dynamic branch executions, mispredict → loop miss rate . Data branches: miss rate .

Step 2 — dynamic-execution share units.

  • loops: share units
  • data: share units
  • total share units.

Step 3 — frequency-weighted overall miss rate. Weight each class's miss rate by its share units (total mispredictions ÷ total dynamic executions):

Step 4 — CPI. Plug into the master formula with , : What we did: the mastery move is realising loop branches execute more often than data branches, so they dominate the dynamic mix. A share-unit (frequency-weighted) average — not a simple average — is required.

Exercise 5.2 (L5)

In Exercise 5.1, suppose the CPU gets a perfect BTB so that every correctly-predicted taken branch costs cycles (versus only on a misprediction). Does the BTB change the computed CPI of ? Explain what the BTB does and does not fix.

Recall Solution

No change — CPI stays . The formula already assumes correctly-predicted branches cost ; the only cost baked in is the misprediction penalty . The BTB is precisely the hardware that delivers that "correct taken = 0 cycles" assumption by supplying the target during IF.

  • BTB fixes: the bubble on a correctly predicted taken branch (turns it from cycle to ).
  • BTB does NOT fix: mispredictions. Wrong direction still flushes instructions regardless of how fast the target was fetched.

So the BTB is baked into the baseline of this model; our and already reflect a world with it. Removing the BTB would add a per-taken-branch penalty term — a different, larger CPI. This ties into Speculative Execution: the BTB lets fetch keep running speculatively past a taken branch with no stall.