5.3.7Advanced Microarchitecture

Branch prediction (static and dynamic)

3,211 words15 min readdifficulty · medium1 backlinks

The Branch Problem

In a pipelined processor, the fetch stage grabs the next instruction before the previous branch resolves. Consider:

100: CMP R1, #5
104: BEQ target    ; branch if equal
108: ADD R2, R3    ; fall-through path
...
200: SUB R4, R5    ; target path

By the time we decode the BEQ at cycle 2, we've already fetched instruction at108 (cycle 1). By the time we execute the compare and know the branch outcome (cycle 5), we've fetched 4-5 more instructions.


Static Branch Prediction

Static prediction uses a fixed rule decided at design time or compile time—no runtime learning.

Common Static Schemes

  1. Predict Not-Taken (PNT)

    • Always fetch the fall-through instruction (PC + 4).
    • Why?: Simple hardware, no state needed.
    • Works for: Forward branches (often not-taken, e.g., error checks if (rare_error) goto handler).
  2. Predict Taken (PT)

    • Always fetch the branch target.
    • Works for: Backward branches (loops! for and while usually iterate multiple times, so the backwards jump is taken90%+ of the time).
  3. Backwards Taken, Forward Not-Taken (BTFNT)

    • Compiler hint: If branch target address< PC, predict taken. Else, not-taken.
    • Accuracy: ~60-70% for typical code (loops dominate branches).

Dynamic Branch Prediction

Dynamic prediction uses runtime history to adapt. The CPU remembers "last time I saw this branch, it went taken/not-taken."

1-Bit Predictor (Branch History Table)

Structure: A table indexed by low-order bits of PC (branch address). Each entry is a 1-bit state:

  • 0 = Predict Not-Taken
  • 1 = Predict Taken

Update rule: After the branch resolves, set the bit to the actual outcome.

2-Bit Saturating Counter (Bimodal Predictor)

To fix the 1-bit predictor's loop problem, use a 2-bit counter with hysteresis (require two mispredictions to change behavior).

States:

  • 00 = Strongly Not-Taken (SNT)
  • 01 = Weakly Not-Taken (WNT)
  • 10 = Weakly Taken (WT)
  • 11 = Strongly Taken (ST)

Prediction rule: Predict Taken if counter ≥ 10, else Not-Taken.

Update rule: Increment on taken (saturate at 11), decrement on not-taken (saturate at 00).

Correlating Predictors (Two-Level Adaptive)

Insight: A branch's outcome often depends on recent branch history, not just its own past.

Example:

if (a > 0) {        // Branch B1
    if (b > 0) {    // Branch B2
        ...
    }
}

If B1 is not-taken, B2 never executes. If B1 is taken, B2's outcome depends on b. A correlating predictor uses a global history register (GHR) to track the last nn branch outcomes, then indexes a table with (PC, GHR).

Tournament Predictor (Hybrid)

Problem: No single predictor wins for all branches. Loops love bimodal (local history). Corelated code loves correlating predictors.

Solution: Tournament predictor runs multiple predictors in parallel (e.g., a local2-bit predictor + a global correlating predictor), then uses a meta-predictor (another 2-bit counter) to choose which predictor to trust for each branch.


Comparison: Static vs Dynamic

| Scheme | Accuracy (typical) | Hardware Cost | Cold Start | Aliasing | |-------------------------|-----------------|-------------------|------------| | Static BTFNT | 60-70% | Zero (just decode) | N/A | N/A | | 1-bit BHT | 70-80% | Small (1-bit/entry) | Bad | Yes | | 2-bit Bimodal | 85-90% | Small (2-bit/entry) | OK | Yes | | (2,2) Correlating | 90-93% | Medium (4× bimodal) | OK | Yes | | Tournament (Alpha 21264)| 95-97% | Large (3 tables) | Good | Reduced |

Aliasing: Multiple branches hash to the same BHT entry, interfering with each other. Larger tables reduce this.



Recall Feynman Explanation (age 12)

Imagine you're reading a choose-your-own-adventure book, but you're reading it really fast—you're on page 10, but you've already flipped ahead and started reading page 11,12, 13, all at once. Then page 10 says "If you chose the sword, turn to page 50. If you chose the shield, turn to page 60."

Uh-oh! You don't know which page you should've been reading. You've wasted time reading the wrong pages.

Branch prediction is the CPU's guess: "I bet they chose the sword (because last time they did), so I'll start reading page 50 right now." If the guess is right, you saved time. If wrong, you throw away pages11-13 and start over at page 60(that's the pipeline flush).

Static prediction: "I always guess sword" (a fixed rule). Dynamic prediction: "I remember the last 5 choices you made, and I guess based on that pattern."

The CPU gets really good at guessing (95%+ correct), so the book flies by!



Connections

  • Pipeline Hazards: Branch prediction solves control hazards (not knowing which instruction to fetch next).
  • Speculative Execution: Prediction enables speculation; mispredictions require rollback mechanisms.
  • Instruction-Level Parallelism (ILP): High ILP demands high prediction accuracy (more instructions flight = higher misprediction cost).
  • Cache Performance: Wrong-path instructions pollute the instruction cache; predictors with high accuracy reduce this.
  • Superscalar Processors: Multiple issue per cycle amplifies branch penalties; advanced predictors are critical.
  • Compiler Optimizations: Profile-guided optimization (PGO) can insert static hints or reorder code to help predictors.

#flashcards/hardware

What is the branch penalty in a pipelined CPU? :: The number of wasted cycles when a branch misprediction occurs, typically 5-10 cycles (the depth of the pipeline must be flushed and restarted from the correct path).

Why does static "backwards taken, forward not-taken" work well?
Loops (backward branches) iterate many times (taken 90%+), while forward branches (error checks) are usually not-taken. This heuristic exploits the natural behavior of typical code.
What is the difference between a 1-bit and 2-bit branch predictor?
A 1-bit predictor changes prediction on every misprediction (oscillates at loop exits). A 2-bit predictor requires two consecutive mispredictions to flip, providing hysteresis that stabilizes loop behavior.
How does a correlating (two-level) predictor improve on a bimodal predictor?
It uses a global history register (GHR) to track recent branch outcomes, allowing it to select different prediction tables based on the path taken. This captures branch correlation (e.g., nested ifs where one branch's outcome affects another).
What does a tournament predictor do?
It runs multiple predictors (e.g., local bimodal + global correlating) in parallel and uses a meta-predictor (selector) to choose which one to trust for each branch, learning which predictor works best for each branch pattern.
Why can't a 2-bit predictor eliminate all loop mispredictions?
The loop exit is always mispredicted once per loop invocation—the counter saturates at "strongly taken," so the not-taken exit mispredicts. This is 1 error per loop, which is ~1% for long loops but ~33% for short loops.
What is aliasing in branch prediction?
When multiple branches hash to the same entry in the BHT (due to limited table size), they interfere with each other's learned patterns, reducing accuracy. Larger tables or tagged entries reduce aliasing.
What is the formula for average CPI with branch mispredictions?
CPI = 1 + P_branch × P_mispredict × Penalty. Example: 20% branches, 10% mispredict rate-cycle penalty → CPI = 1.2 (20% slowdown).

Concept Map

fetches before resolve

causes

wasted flush cycles

solved by

speculatively fetch

fixed rule

runtime learning

fall-through PC+4

branch target

combines rules

good for backward loops

good for forward checks

accuracy ~60-70%

Pipelined Fetch

Branch Problem

Branch Penalty

Higher CPI

Branch Prediction

Static Prediction

Dynamic Prediction

Predict Not-Taken

Predict Taken

BTFNT

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Dekho, sabse pehle intuition samajh lo. Modern CPU ek assembly line ki tarah kaam karta hai jahan instructions execute hone se bahut pehle fetch ho jaate hain. Lekin problem tab aati hai jab ek if condition ya loop aata hai — jaise if (x > 5) — kyunki CPU ko abhi tak nahi pata ki kaunsa raasta lena hai. Condition ka result aane mein 10-20 cycles lag jaate hain, aur tab tak pipeline khali baithi rehti hai, kaam maang rahi hoti hai. Isliye CPU ek "educated guess" lagata hai — "shayad ye branch lena padega" — aur us guess ke basis pe instructions speculatively fetch kar leta hai. Agar guess sahi nikla to full speed maintain rehti hai, aur agar galat nikla to pipeline flush karni padti hai matlab kiya hua kaam bekaar chala jaata hai.

Ab ye kyun matter karta hai? Formula dekho: agar 20% instructions branches hain, 10% mispredict hoti hain, aur penalty 10 cycles ki hai, to CPI 1 se badhkar 1.2 ho jaata hai — yaani sirf branch galat guess karne se 20% performance loss! Yahi reason hai ki prediction accha hona bahut zaroori hai. Static prediction ek fixed rule use karta hai — jaise BTFNT (Backwards Taken, Forward Not-Taken). Iska logic simple hai: loops backward jump karte hain aur baar baar chalte hain (isliye "taken" maan lo), jabki forward branches jaise error checks aksar nahi chalte (isliye "not-taken" maan lo). Loop-heavy code mein ye scheme lagbhag 84% accuracy de deta hai.

Lekin static prediction ki ek badi limitation hai — ye seekhta nahi. Ek fixed ceiling pe atak jaata hai. Isliye dynamic branch prediction aata hai, jo runtime history dekh ke apne guesses ko adapt karta hai — matlab jaisa pehle hua tha uske basis pe agla guess better banata hai. Yahi real-world CPUs ki taakat hai: static rules se kaam chala lete hain simple cases mein, aur complex patterns ke liye dynamic prediction lagate hain jo actual program behaviour se seekh ke accuracy 95%+ tak le jaata hai. Iske bina modern deep pipelines efficient hi nahi ho paate.

Go deeper — visual, from zero

Test yourself — Advanced Microarchitecture

Connections