Section 3 · 9 min read
Instruction pipelining and hazards
Pipeline timing, speedup and throughput, CPI with stalls, and how structural, data and control hazards are detected, counted and reduced.
The idea
Pipelining splits instruction execution into stages separated by pipeline registers (latches), so that up to instructions are in progress at once, each in a different stage. It does not make one instruction faster; it raises throughput. The classic RISC pipeline has five stages:
| Stage | Work |
|---|---|
| IF | fetch instruction, PC PC |
| ID | decode, read registers |
| EX | ALU operation or address calculation |
| MEM | load or store data memory access |
| WB | write result to the register file |
Timing formulas
Clock period
With stage delays and latch delay :
The slowest stage sets the clock for all stages. A non-pipelined unit doing the same work takes per instruction (no latches).
Time for instructions with no stalls
The first instruction takes cycles; each later one completes one cycle after the previous:
Speedup and efficiency
With equal stages and no latch overhead, and . Efficiency is in that ideal case. Throughput is , approaching instructions per second.
CPI with stalls
When comparing pipelines, always compare , not either factor alone. Speedup of a pipelined design over a non-pipelined one with stalls:
Hazards
A hazard is a situation that prevents the next instruction from proceeding in its planned cycle.
Structural hazards
Two instructions need the same hardware in the same cycle. Classic case: a single memory port shared by IF and MEM. Fixes: separate instruction and data memories (or caches), more ports, duplicate units, or pipelining the shared unit. A non-pipelined multi-cycle unit (for example a divider that takes 4 cycles) is also a structural hazard for the next instruction that needs it.
Data hazards
For instructions before :
- RAW (read after write, true dependence): reads what writes. The only kind that is a real data flow.
- WAR (write after read, anti dependence): writes what reads. A problem only if can write before reads, which needs out-of-order execution or early writes.
- WAW (write after write, output dependence): both write the same register. A problem only if writes can complete out of order.
In the classic in-order 5-stage pipeline (read in ID, write in WB), only RAW hazards cause stalls. WAR and WAW become hazards with multi-cycle units of different lengths or out-of-order issue, and register renaming removes them.
Handling RAW hazards
Without forwarding, the consumer must read the register after the producer writes it. With the common split-cycle register file (write in the first half of WB, read in the second half of ID), the consumer's ID can be in the same cycle as the producer's WB. For back-to-back dependent instructions in a 5-stage pipeline, that is 2 stall cycles; with one independent instruction in between, 1 stall; with two in between, none.
With forwarding (bypassing), results go from the EX/MEM and MEM/WB pipeline registers straight to the ALU inputs. ALU-to-ALU dependences then need no stall. A load-use hazard still costs 1 stall, because the loaded value exists only at the end of MEM, and the next instruction wants it at the start of EX in the same cycle.
Compilers reduce load-use stalls by instruction scheduling: moving an independent instruction between the load and its user.
Control hazards
The pipeline fetches past a branch before knowing whether it is taken or where it goes. If the branch outcome and target are known at the end of stage , the instructions fetched in the cycles after the branch may be wrong:
Techniques:
- Stall (freeze): every branch pays the full penalty.
- Predict not taken: keep fetching sequentially; only taken branches pay (flush).
- Predict taken: needs the target early (branch target buffer).
- Delayed branch: the compiler fills the slot(s) after the branch with instructions that execute anyway.
- Early resolution: compute the condition and target in ID, cutting the penalty to 1.
- Dynamic prediction: 1-bit or 2-bit counters per branch; penalty paid only on mispredictions.
Effective CPI with branches: , where is the fraction of branches that actually pay (all of them for stalling, taken ones for predict-not-taken, mispredicted ones for dynamic prediction).
Worked examples
Worked example 1: speedup with unequal stages
Five stages of 120, 150, 100, 180 and 130 ps, latch delay 20 ps, 500 instructions, no stalls.
- ps. ps.
- Pipelined: ps ns.
- Non-pipelined: ps ns.
- Speedup . As it tends to , well below 5 because of the uneven stages and the latch.
Worked example 2: CPI from instruction mix
25% of instructions are loads, and 40% of loads are followed immediately by a user (1 stall each). 15% are branches; the pipeline predicts not-taken with a 2-cycle penalty, and 70% of branches are taken.
Worked example 3: the same code with and without forwarding
I1: LW R1, 0(R2)
I2: ADD R3, R1, R4
I3: SUB R5, R3, R6
I4: SW R5, 8(R2)
No forwarding, split-cycle register file, stalled instructions wait in ID (shown as s):
| Cycle | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| I1 | IF | ID | EX | MEM | WB | |||||||||
| I2 | IF | s | s | ID | EX | MEM | WB | |||||||
| I3 | IF | s | s | ID | EX | MEM | WB | |||||||
| I4 | IF | s | s | ID | EX | MEM | WB |
I2 reads R1 in cycle 5 when I1 writes it; I3 reads R3 in cycle 8; I4 reads R5 in cycle 11. Total 14 cycles, 6 of them stalls. (I3 sits in IF during cycles 4 and 5 and I4 waits in IF behind it; those cycles are part of the same stalls.)
Full forwarding into EX, store data also needed at the start of EX:
| Cycle | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|
| I1 | IF | ID | EX | MEM | WB | ||||
| I2 | IF | ID | s | EX | MEM | WB | |||
| I3 | IF | s | ID | EX | MEM | WB | |||
| I4 | IF | ID | EX | MEM | WB |
Only the load-use stall remains: R1 leaves MEM at the end of cycle 4 and reaches I2's EX in cycle 5. I3 and I4 get their operands from EX/MEM. Total 9 cycles.
Worked example 4: resolving branches earlier
A 5-stage pipeline resolves branches in EX (stage 3), so the penalty is 2 cycles. 20% of instructions are branches and every branch stalls. Moving the comparison into ID cuts the penalty to 1 cycle without changing the clock.
- Before: CPI .
- After: CPI .
- Speedup .
If the extra comparator in ID lengthened the clock by more than 17%, the change would not pay off. That is the kind of trade-off GATE asks about.
Worked example 5: deeper is not always better
Design X: 4 stages, 2 ns clock, branch penalty 1, every branch pays. Design Y: 8 stages, 1.2 ns clock, branch penalty 4. 20% branches.
- X: ns per instruction.
- Y: ns per instruction.
Y wins by , far less than the clock ratio . With 35% branches Y would take ns against X's ns and would lose.
Common traps
- Forgetting the latch delay in , or adding it to the non-pipelined time.
- Using cycles instead of .
- Speedup limit is , which equals only with equal stages and no latch overhead.
- Load-use stall survives forwarding. One cycle in the classic 5-stage design.
- Split-cycle register file changes no-forwarding stall counts from 3 to 2 for adjacent dependent instructions. Read the assumption.
- Branch penalty applies only to the branches that actually pay under the stated policy.
- WAR and WAW do not stall an in-order pipeline that reads in ID and writes in WB.
- Compare execution time, not CPI or clock alone.
Quick revision
- ; .
- Speedup ; ideal limit .
- Efficiency for an ideal pipeline.
- CPI frequency penalty; time CPI .
- RAW is true dependence; WAR and WAW are name dependences removed by renaming.
- Forwarding removes ALU-to-ALU stalls; load-use still costs 1.
- Branch resolved at end of stage : penalty .
- Predict-not-taken: only taken branches pay; stall policy: all branches pay.