Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 29

Branch Prediction Strategies

Recommended resources

Hello! Welcome to the fifth lesson in our module on CPU and System Architecture.

In our last lesson, we examined the classic RISC pipeline and the hazards that disrupt its smooth operation. We identified control hazards, caused by branch instructions, as a major source of performance loss. You learned that naively stalling the pipeline on every branch is too costly, leading to the use of a simple "predict-not-taken" strategy combined with a pipeline flush on a misprediction.

Today, we will build directly on that foundation. The simple prediction we discussed is a form of static prediction. To achieve the performance levels required by modern CPUs at companies like AMD, Nvidia, and Intel, much more sophisticated dynamic prediction techniques are necessary.

Your learning outcome for this lesson is to: Compare static vs. dynamic branch prediction strategies and their role in mitigating control hazard penalties.

We will explore:

  • The principles and limitations of static prediction, including compiler heuristics.
  • The evolution of dynamic predictors, from simple 1-bit and 2-bit counters to advanced correlating and tournament predictors.
  • The hardware components involved, such as the Branch History Table (BHT) and Branch Target Buffer (BTB).
  • Real-world examples from Intel architectures to bridge theory with practice.

The High Cost of Being Wrong

Before we dive into the strategies, let's reinforce why branch prediction is so critical. In a deep pipeline (modern CPUs can have 10-20+ stages), a single misprediction means all the instructions in flight behind the branch are on the wrong path and must be discarded. This wastes a significant number of clock cycles and dramatically reduces the processor's effective throughput.

Control Flow Penalty and Branch Prediction in Processor Pipelines
This diagram visualizes the penalty of a mispredicted branch. The 'work lost' is proportional to the depth of the pipeline. In a superscalar processor that fetches multiple instructions per cycle, this penalty is even more severe. Excellent branch prediction is not a luxury; it's a necessity for high performance.

Our goal is to maximize the number of correct predictions to minimize this penalty.


Static Branch Prediction: The Fixed-Rule Approach

Static prediction strategies follow fixed rules that do not change based on the runtime behavior of the code. The prediction is either hard-wired into the CPU design or determined by the compiler.

The simplest strategies, which we touched on last time, are:

  • Always Predict Not-Taken: Assumes branches are never taken. This is effective for simple if statements that handle exceptional cases, but performs poorly on loops.
  • Always Predict Taken: Assumes branches are always taken. This is better for loops but poor for if statements.

A more intelligent static approach relies on compiler heuristics. The most common is Backward Branch Taken, Forward Branch Not-Taken (BTFNT).

  • Backward Branch: A branch whose target address is lower than the current Program Counter (PC). These are almost always loop-closing branches, which are taken most of the time. The compiler can generate code where loops jump backward.
  • Forward Branch: A branch whose target address is higher than the PC. These are typically if-then-else constructs or error checks, which are less predictably taken.

This simple heuristic is surprisingly effective and is often used as a fallback mechanism in modern processors when the dynamic predictor has no information about a branch.

Another static technique, now largely historical but important to know for interviews, is the Branch Delay Slot. The ISA is designed such that the instruction immediately following a branch is always executed, regardless of the branch outcome. The compiler's job is to find an independent instruction to place in this "slot."

While simple and low-power, static predictors are not adaptive. They fail when branch behavior is complex or changes over the program's execution.


Dynamic Branch Prediction: Learning from History

Dynamic branch prediction uses dedicated hardware to record the past history of branches and use that history to predict future outcomes. This allows the processor to adapt to the program's runtime behavior.

Let's begin by watching an overview of the core concepts.

Lec 9: Control Hazards and Branch Prediction

This video introduces the motivation for dynamic prediction and the fundamental hardware components. It also begins our journey through the evolution of predictors.

Please watch the following clips from this lecture by NPTEL IIT Guwahati: 19:38 - 21:52: This segment introduces the core idea of dynamic branch prediction, the role of a Branch Prediction Buffer (BPB) or Branch History Table (BHT), and how it's used during the instruction fetch stage. 21:52 - 26:29: This part explains the mechanics of the two most fundamental dynamic predictors: the 1-bit and 2-bit finite state machines. Pay close attention to the state transition diagrams.

Let's break down those initial predictors in more detail.

1-Bit Predictor (Last-Time Predictor)

This is the simplest dynamic predictor. The BHT stores one bit for each branch, recording whether it was Taken or Not-Taken the last time it was executed. The next prediction is simply the last outcome.

  • State Machine: A simple 2-state machine (Predict Taken, Predict Not-Taken).
  • Flaw: It performs poorly for branches that are almost always taken, like a for loop. It will correctly predict every taken branch inside the loop, but it will mispredict the final iteration when the loop exits. Then, the next time the loop is entered, it will mispredict the first iteration because its last recorded state was "Not-Taken." This results in two mispredictions per loop execution.

2-Bit Predictor (Bimodal Predictor)

To fix the flaw of the 1-bit predictor, a 2-bit scheme is used. This adds hysteresis, preventing the prediction from changing on a single different outcome. Each branch has a 2-bit saturating counter in the BHT, representing four states:

  • 11: Strongly Taken
  • 10: Weakly Taken
  • 01: Weakly Not-Taken
  • 00: Strongly Not-Taken

The prediction is based on the most significant bit (1 = Taken, 0 = Not-Taken). The counter is incremented when the branch is actually taken (saturating at 11) and decremented when not-taken (saturating at 00).

2 1 7 Introduction to Dynamic Branch Prediction

This video provides a very clear, focused explanation of the weakness of the 1-bit predictor in loops and demonstrates precisely how the 2-bit predictor's hysteresis solves it.

Watch from 05:03 to 08:33. The example with the nested loops from an MPEG encoder clearly illustrates the double-misprediction problem of a 1-bit scheme. Then, observe how the 2-bit finite state machine requires two consecutive mispredictions to change its strong prediction, reducing the mispredictions for a typical loop from two to just one.

Because of this improved accuracy, 2-bit predictors (or variants) form the basis of virtually all modern branch predictors.


Advanced Dynamic Prediction: Exploiting Correlation

A 2-bit bimodal predictor works well for branches with a strong bias (like loops), but it can fail when a branch's outcome is correlated with the outcome of other recent branches.

Consider this example:

if (d == 0) { ... } // branch 1
if (d == 2) { ... } // branch 2

if (d == 0 || d == 2) { // branch 3
    // This branch is correlated with branches 1 and 2
}

The outcome of branch 3 is not random; it is highly dependent on the outcomes of branch 1 and branch 2. A simple bimodal predictor for branch 3 would not capture this relationship.

This leads to two-level predictors.

17 Advanced Branch Prediction - CS Notes

To understand how processors exploit correlation, please read the following sections from this article. It clearly defines the two main types of correlation.

Please read the section 'Two-Level Branch Prediction: Exploiting Correlation'. Focus on understanding the difference between: Global Branch Correlation: Uses a Global History Register (GHR) to track the outcomes of the last N branches executed anywhere in the program. This history pattern is used to select a 2-bit counter from a Pattern History Table (PHT). Local Branch Correlation: Tracks the last N outcomes of that specific branch in a Local History Register to select a counter from a PHT.

These two-level predictors dramatically improve accuracy by learning more complex patterns in program behavior.


State-of-the-Art: Hybrid / Tournament Predictors

Some branches are best predicted by their local history, while others are better predicted by global history. So, why not use both?

This is the idea behind Hybrid or Tournament Predictors.

  • Multiple predictors (e.g., a local predictor and a global predictor) run in parallel.
  • A meta-predictor (or choice predictor) also keeps track of which predictor has been more accurate for a given branch or history pattern.
  • The meta-predictor's output is used to select which prediction to ultimately use.
Illustrating the Tournament Branch Prediction
A diagram of the famous Alpha 21264 tournament predictor. It combines a local predictor and a global predictor. A 'Choice Predictor' table decides whether to use the local or global prediction based on which has been more successful in the past for the current global history. This 'best of both worlds' approach is a key design in many high-performance CPUs.

Interview Deep Dive: Real-World Implementations

These concepts are not just academic. They are directly implemented in the processors designed by the companies you are targeting. Information is often proprietary, but analysis reveals the strategies used. The "Demystifying Intel Branch Predictors" paper provides fantastic insight. Here are the key findings:

  • Intel P6 Architecture (Pentium Pro, Pentium II/III):

    • Relied on a local history predictor.
    • Each entry in its 512-entry Branch Target Buffer (BTB) was associated with a 4-bit local history.
    • This predictor was excellent at capturing patterns for individual branches.
  • Intel NetBurst Architecture (Pentium 4):

    • Switched to a powerful global history predictor.
    • It used a 16-bit Global History Register.
    • It also featured a very large 4K-entry BTB and an "Execution Trace Cache," which stored sequences of decoded instructions along a predicted path, effectively acting as a highly advanced form of branch prediction.

This shift from a local to a global prediction strategy between major architectures highlights a real-world design trade-off. The NetBurst design's bet on global correlation was part of its strategy to achieve very high clock speeds by enabling a very deep pipeline.


Conclusion

We've journeyed from simple static rules to the sophisticated, learning-based hardware of modern dynamic predictors. Mitigating control hazard penalties is a constant battle between hardware complexity, power consumption, and prediction accuracy.

Key Takeaways:

  • Static Prediction uses fixed rules (e.g., BTFNT). It is simple but inflexible.
  • Dynamic Prediction uses hardware to learn from past branch behavior at runtime.
  • 1-bit predictors are simple but have a "double misprediction" flaw on standard loops.
  • 2-bit predictors add hysteresis, significantly improving accuracy by requiring two consecutive wrong outcomes to flip a strong prediction.
  • Two-Level Predictors exploit correlation, using either global history (outcomes of any recent branch) or local history (outcomes of a specific branch) to make more intelligent predictions.
  • Tournament Predictors combine multiple strategies (e.g., local and global) and use a meta-predictor to choose the best one for the current situation, achieving state-of-the-art accuracy.

Preview of the Next Lesson:
We've now examined the memory system (caches, MMU) and core execution logic (pipelining, branch prediction). In the next lesson, we will step back and compare the two dominant Instruction Set Architecture (ISA) philosophies: RISC (Reduced Instruction Set Computer) vs. CISC (Complex Instruction Set Computer). We'll explore how this fundamental design choice impacts everything from compiler design to pipeline complexity and performance, using ARM and x86 as our primary examples.