Worked examples — Out-of-order execution
This page is the drill floor for Out-of-order execution. The parent note built the ideas: renaming, reservation stations, the reorder buffer (ROB), the ready condition. Here we throw every kind of situation at those ideas and grind through each one by hand.
Before we start, one promise: every symbol you see here was earned in the parent note, and I re-anchor it the first time it appears. If a term feels new, that is a bug — tell me.
We follow two timing rules on this whole page so every answer is reproducible. Read them once carefully — they resolve every "is it 3 or 4 cycles?" question later.
The scenario matrix
Everything OoOE can throw at you falls into one of these cells. The examples below are labelled with the cell(s) they cover, and together they hit every row.
| # | Case class | What makes it tricky | Covered by |
|---|---|---|---|
| A | No dependencies (fully parallel) | limited only by execution width | Ex 1 |
| B | RAW chain (true dependency) | critical path — cannot be beaten | Ex 2 |
| C | WAW / WAR false dependency | must rename to unlock parallelism | Ex 3 |
| D | Mixed: slow LOAD + independents + dependent | reorder around latency | Ex 4 |
| E | Not enough ports (execution-width limit) | ready ≠ can-fire | Ex 5 |
| F | Exception / speculative squash | precise state via ROB | Ex 6 |
| G | Degenerate: window too small | can't see the independent work | Ex 7 |
| H | Limiting case: zero dependencies, infinite width | IPC → execution/retire width | Ex 8 |
| I | Real-world word problem (cache miss) | memory-level parallelism | Ex 9 |
| J | Exam twist: is a reorder legal? | spot the true vs false hazard | Ex 10 |
The three ceilings from the parent note govern every cell (IPC = Instructions Per Cycle, just defined above): Each example below is really asking: which of these three ceilings is binding here? (Example 8 will unmask a hidden fourth ceiling: retire bandwidth.)
The figure below draws those three ceilings as three bars of illustrative heights. The green dashed line sits at the shortest bar — that is the one that actually limits IPC. Every example is a hunt for which bar is shortest: the blue "window" bar (can you see the work?), the yellow "execution width" bar (is there space to run it?), or the red "critical path" bar (are you forced to sequence?).

Example 1 — Cell A: fully independent instructions
Forecast: guess before reading — is it 1, 2, or 4 cycles?
- Check dependencies. No instruction reads a register another writes. So no RAW, no WAR, no WAW. Why this step? Dependencies are the only reason OoOE is forced to serialize. Zero dependencies means the critical-path ceiling = 1 (any single instruction is the whole chain).
- Check resources. 4 ready instructions, but only 2 add ports. So cycle 1 fires I1, I2 (ready end cyc 1); cycle 2 fires I3, I4 (ready end cyc 2). Why this step? Being ready is necessary but not sufficient — you also need a free port. Here the execution-width ceiling = 2/cycle is the binding one.
- Apply the total-cycles rule. Execution-bound = last-ready (cyc 2) + 1 = 3. Retire-bound = cycles of committing. With matched to the port width, commit keeps pace and does not dominate; total commit of the last pair lands in cycle 3. Why this step? We deliberately set so retire matches issue — this is the "commit hides" case.
- Total = 3 cycles (2 to compute, +1 to commit the final pair).
Verify: last result ready cyc 2, +1 commit ⇒ 3. Sustained IPC ; the steady-state throughput while ports are saturated is 2/cycle = execution width, the binding ceiling. ✓
Example 2 — Cell B: a pure RAW chain
Forecast: with unlimited execution units, does this run in 1 cycle?
- Draw the data-flow graph: I1 → I2 → I3 → I4, a single strand. Why this step? A RAW edge is a true dependency — I2 literally needs the number I1 produces. No renaming, no extra port removes it.
- Time each fire. I1 fires cyc 1, ready end of cyc 1. I2 fires cyc 2 (after its operand). I3 cyc 3. I4 cyc 4. Why this step? This is the critical-path ceiling at work. It is the one limit no hardware can beat — even Apple's 600-entry window cannot make I4 run before I3 finishes.
- Apply the total-cycles rule. Results trickle out one per cycle (I1 ready cyc1, …, I4 ready cyc4). Execution-bound = last-ready (cyc 4) + 1 = 5. Retire-bound = commits, but they start as I1 finishes and keep pace at 1/cycle — never larger than the execution term. So the picks the execution term. Why this step? Chains are the case where commit hides: one result per cycle exactly matches .
- Total = 5 cycles (chain of 4 + one final commit). IPC ; steady-state throughput along the chain is 1/cycle, matching critical-path-limited ILP.
Verify: chain length 4 ⇒ last ready cyc 4, +1 commit ⇒ 5. Adding ports does nothing: still forces the chain. ✓
Example 3 — Cell C: false dependency killed by renaming
Forecast: does I3 have to wait for I1 to finish?
- Classify each shared use of R1. I2 reads I1's R1 (true RAW). I3 writes R1 again — the name collides with I1's write (WAW) and with I2's read (WAR). Those two are false: they exist only because the assembler reused the name
R1. Why this step? False dependencies are artifacts of naming, not data. The parent note's insight: rename them away. - Rename onto physical registers. Recall
P-prefixed names are the CPU's many real storage slots. Give I1's result physical slotP1and I3's result a different slotP2. Now:- I2 reads
P1, I4 readsP2. The two chains {I1→I2} and {I3→I4} touch different physical slots, so they are independent. Why this step? Using two distinct physical slots for the twoR1writes exposes the real data-flow graph — two separate 2-long strands instead of one tangle.
- I2 reads
- Schedule (2 ports). Cyc 1: fire I1 and I3 (both ready, independent), ready end cyc 1. Cyc 2: fire I2 and I4, ready end cyc 2. Execution-bound = 2 + 1 = 3; retire-bound with keeps pace. Total = 3 cycles.
- Without renaming: I3 stalls until I1's write retires and I2's read completes → serial-ish: cyc1 I1, cyc2 I2, cyc3 I3, cyc4 I4, last ready cyc 4, +1 commit ⇒ 5 cycles.
Verify: speedup . With renaming, steady-state throughput = 2/cycle = execution width, the binding ceiling once false deps vanish. ✓
The figure shows this split visually: the top row is the tangled single strand forced by name reuse (blue → red edges), and the bottom row is the two clean, parallel strands after P1/P2 separate them.

Example 4 — Cell D: slow LOAD + independents + dependent (the parent's showcase, re-derived)
Forecast: the LOAD takes 3 cycles — do I2 and I4 have to sit behind it?
- In-order. I1 occupies cyc 1–3. I2 cannot start until I1 leaves → cyc 4. I3 → cyc 5. I4 → cyc 6. Total 6. Why this step? In-order issue means one stall poisons everyone behind it, even the innocent I2/I4.
- Out-of-order — fire independents early. I2 has no operand waiting → fire cyc 1 on the ALU port (ready end cyc 1). I4 → fire cyc 2 on the ALU port (ready end cyc 2). The LOAD I1 runs cyc 1–3 on its own port. Why this step? Different ports = I2/I4 don't compete with the LOAD. This is the whole point of OoOE.
- Fire the dependent I3. I3 needs I1 (ready end cyc 3) and I2 (ready end cyc 1). Last operand ready end of cyc 3 → I3 fires cyc 4, result ready end cyc 4. Why this step? I3 sits on the genuine dependency chain I1 → I3; that chain, not the ports, sets its earliest fire.
- Apply the total-cycles rule. I3 is last in program order; its result is ready end cyc 4. Execution-bound = 4 + 1 = 5. Retire-bound: only 4 instructions, committed in order at but no burst (I2 ready cyc1, I4 cyc2, I1 cyc3, I3 cyc4 — one per cycle) so retire keeps pace and does not dominate. The gives 5. Why this step? The parent note's "≈5 cycles including commit settling" is exactly this: the +1 is the in-order commit of the last result I3.
Verify: speedup , matching the parent note. Residual latency = the genuine chain I1→I3 plus one in-order commit step. ✓
The timeline figure stacks both schedules: the top block is in-order (I2/I4 stranded behind the red LOAD, red dashed finish line at 6), the bottom block is out-of-order (I2/I4 pulled early, green dashed finish line at 5).

Example 5 — Cell E: ready ≠ can-fire, AND retire bandwidth bites
Forecast: all six are ready immediately — so 1 cycle? Or does something else cap it?
- All ready at once. No dependency delays them. Critical-path ceiling = 1. Window is big enough (window ceiling ≥ 6). Why this step? Two ceilings say "go fast"; we must find the binding one.
- Ports limit starting. 2 per cycle: cyc 1 fires I1,I2 (ready end 1); cyc 2 fires I3,I4 (ready end 2); cyc 3 fires I5,I6 (ready end 3). Why this step? Execution-width ceiling = 2/cycle. Execution alone would finish producing results by end of cyc 3.
- Now apply the total-cycles rule honestly. Six results become ready in a burst (2 per cycle) but retire only drains per cycle. Retire-bound = commit cycles. Committing starts in cyc 2 (one cycle after I1/I2 ready) and proceeds 1/cycle: I1 cyc2, I2 cyc3, I3 cyc4, I4 cyc5, I5 cyc6, I6 cyc7. Why this step? This is the reviewer's exact point: with only one retire slot, six results cannot all be committed by cycle 4. The retire term wins the . Execution-bound = 3+1 = 4, retire-bound reaches cyc 7 → Total = 7 cycles.
- The binding ceiling here is retire bandwidth, not ports. Ports fill the ROB with finished results faster than commit can publish them.
Verify: results produced by end cyc 3, but in-order commits starting cyc 2 ⇒ last commit cyc . Sustained IPC (retire bandwidth), the true bottleneck. ✓ Lesson: readiness lets you choose, ports decide how many start, retire decides how many become real.
Example 6 — Cell F: exception squashes speculative work
Forecast: do I4 and I5's results survive, since they finished?
- I3 is at the head and faulted. Commit rule:
Can_Commit(I_h) = Complete(I_h) ∧ ¬Exception(I_h). HereExceptionis true → cannot commit. Why this step? Only the head can commit, and it must be exception-free. This enforces the illusion of sequential execution. - Flush everything after the head. I4, I5 are younger than the faulting I3. Their computed values live only in the ROB, never in architectural registers/memory → discard them. Why this step? Precise exceptions demand: "I3 faulted, nothing after it happened." Speculative results must be invisible.
- Roll back. Architectural state = state right after I2 (I3's inputs). Signal the OS.
Verify: committed set = {I1, I2}; discarded = {I4, I5}; I3 pending. Count of visible results = 2, exactly the pre-fault instructions. ✓ (Same principle powers speculative execution and branch prediction recovery.)
Example 7 — Cell G: window too small to see the work
Forecast: there's free independent work at I50 — will OoOE grab it?
- Window holds only 4 in-flight instructions. While I1 stalls on the miss, the window fills with I1..I4 and cannot advance (I1 blocks the ROB head from committing). Why this step? The window ceiling caps how far ahead the CPU can see. I50 is 46 instructions past the window edge — invisible.
- CPU stalls ~100 cycles doing nothing useful, even though I50 could run. Why this step? Independent work you cannot see is independent work you cannot use. This is exactly why modern chips grow windows to 200–600 entries.
- Fix: a window ≥ 50 would pull I50 in and hide the miss latency behind it.
Verify: with window 4, reachable-independent-work = 0 during the miss → binding ceiling is window-limited ILP ≈ 0 useful IPC for those cycles. Enlarge window past 50 → I50 reachable. ✓
Example 8 — Cell H: the clean limit (infinite width) — unmasking retire bandwidth
Forecast: does IPC grow forever, or hit a wall?
- Execution finishes fast. With zero dependencies the critical-path ceiling = 1 (never binds) and window ≥ N (never binds), so execution needs only cycles. As , that is 1 cycle: every result is ready at end of cycle 1. Why this step? We removed three ceilings on purpose to expose whatever is left.
- But results must still be committed in order at /cycle. Publishing all results takes commit cycles. Apply the total-cycles rule: . Why this step? Infinite ports are useless if you can only make results real at /cycle. This is the hidden fourth ceiling — retire bandwidth.
- Take the limit. As , sustained IPC (for ). So IPC saturates at the retire width , never at the infinite execution width. Why this step? It pins down which ceiling truly caps a burst of independent work: not ports, but commit.
Verify: . With : total , IPC . With : total , IPC (and , since ). With : IPC , capped by not . ✓ IPC , confirming retire bandwidth is the wall.
Example 9 — Cell I: real-world word problem (memory-level parallelism)
Forecast: two 100-cycle misses — is that 200 cycles or 100?
- In-order (blocking). I1 stalls the pipe 100 cycles, then I2 stalls another 100 → ~200 cycles, then I3. Why this step? A blocking machine serializes the misses even though they are independent.
- Out-of-order + non-blocking loads. I1 and I2 are independent → both miss requests launch in the same window. Memory serves them overlapped (memory-level parallelism). Both back at ~cycle 100. I3 fires cycle 101. Why this step? OoOE's window lets both miss addresses be outstanding at once — the misses share the ~100-cycle wait instead of stacking. Commit of just 3 instructions at 1/cycle is trivially not the bottleneck against a 100-cycle miss.
Verify: in-order ≈ 200 cyc; OoO ≈ 101 cyc; speedup . ✓ (Interacts with cache coherence and memory consistency when other cores are involved.)
Example 10 — Cell J: exam twist — "is this reorder legal?"
Forecast: I2 reads R1, I3 overwrites R1 — safe to run I3 first?
- Identify the hazard between I2 and I3. I2 reads R1; I3 writes R1. Write-After-Read = WAR = a false dependency. Why this step? WAR is not real data flow — it only says "don't clobber R1 before I2 reads it."
- Apply renaming. Give I3's write a fresh physical register
P2(distinct from I1'sP1that I2 reads). Now I3 writesP2, I2 readsP1— no conflict. Why this step? Renaming converts the ordering constraint into a naming fact. With separate physical slots, execution order is free. - Conclusion: Yes, legal — after renaming, I3 may execute before I2. The engine must still commit them in program order (ROB, 1/cycle), so architectural R1 ends up holding I3's value regardless of execution order.
Verify: hazard = WAR (false) → removable by rename → reorder legal. Contrast: had I3 read R1 (RAW), reorder would be illegal. ✓
Recall Self-test (reveal after answering)
IPC in one sentence ::: instructions finished per clock cycle, — the headline speed score
Cell A binding ceiling ::: execution width (with matched retire), commit adds one settling cycle
Cell B binding ceiling ::: critical path (dependency chain length)
Why can't a huge window beat a RAW chain? ::: the chain is a true data dependency — later instructions literally need earlier results
WAR and WAW are removed by ::: register renaming (they are false / naming-only)
What does a P-prefixed name mean on this page? ::: a physical register — a real CPU storage slot handed out by the renamer (vs R = architectural register the programmer wrote)
What does mean in these examples? ::: unlimited — pretend that resource never runs out, so we can isolate one ceiling
Why does Example 4 total 5 and not 4 cycles? ::: the in-order commit of the last instruction (I3) adds one settling cycle after its result is ready
Why does Example 5 total 7 and not 3 cycles? ::: six results burst out but retire drains only 1/cycle — retire bandwidth binds
What hidden fourth ceiling does Example 8 unmask? ::: retire / commit bandwidth — sustained IPC saturates at R, never at infinite execution width
What makes I4/I5 discardable after an I3 fault? ::: they are younger than the faulting head; their results live only in the ROB, never committed
During a cache miss, what lets OoOE keep working? ::: a large enough instruction window to reach independent work
Two independent misses take ~100 not ~200 cycles because ::: memory-level parallelism — both requests are outstanding at once