Worked examples — Superscalar — multiple execution units
This page is a workout. The parent note built the machinery: issue width and IPC (Instructions Per Cycle). One more symbol appears in the effective-IPC formula, so we define it before we ever write it down:
Here we hit every kind of case you could be asked, one worked example per case, so no exam scenario surprises you. Before the numbers, one reminder in plain words:
The scenario matrix
Every superscalar timing/throughput problem lands in one of these cells. We will fill all of them with a worked example.
| # | Case class | What is special | Example |
|---|---|---|---|
| A | Fully independent code | best case, IPC hits the ceiling | Ex 1 |
| B | Pure dependency chain | worst case, IPC collapses to 1 | Ex 2 |
| C | Mixed / partial parallelism | realistic middle, use the formula | Ex 3 |
| D | Multi-cycle latency op | a result isn't ready next cycle | Ex 4 |
| E | Structural hazard | only ONE unit of a needed type | Ex 5 |
| F | False dependency (WAR and WAW) | fixed by register renaming | Ex 6 |
| G | Limiting behaviour | , , | Ex 7 |
| H | Real-world word problem | translate a spec into IPC & time | Ex 8 |
| I | Exam-style twist | in-order vs out-of-order same code | Ex 9 |
Convention for all examples unless stated: 2-wide machine, two identical integer ALUs, each op has latency 1 (result ready at the end of the cycle it issues, usable the next cycle), in-order issue. We change one knob per example.
Case A — fully independent (IPC hits the ceiling)
Forecast: guess the cycle count before reading on. Four independent adds, two lanes…
- Check dependencies. Every destination register (
r1, r4, r7, r10) is distinct from every source. Why this step? Independence is the only thing that lets two ops share a cycle — no RAW hazard means the second lane is legal to use. - Pack the lanes. Cycle 1: issue I1, I2. Cycle 2: issue I3, I4. Why? With 2 ALUs and no dependency, we fill both lanes each tick — nothing forces a stall.
- Count. cycles for instructions. Why this step? IPC is defined as instructions ÷ cycles, so to get the number we simply total each and divide.
Verify: A scalar (1-wide) machine takes 4 cycles → IPC 1. Speedup , the peak for a 2-wide core. Ceiling respected ( ✓).
The figure below draws the schedule as a grid: columns are cycles, rows are the two ALU lanes. Look at how both boxes in each column are filled (blue on ALU 1, green on ALU 2) — that "no white gaps" pattern is exactly what IPC = N looks like.

Case B — pure dependency chain (IPC collapses to 1)
Forecast: each op eats the previous one's output. Guess whether width matters here.
- Trace the chain. I2 needs
r1(made by I1), I3 needsr4(made by I2), I4 needsr6. Each result is ready only at the end of its cycle. Why this step? A RAW hazard forbids issuing the consumer until the producer's value exists. - Schedule. C1: I1. C2: I2. C3: I3. C4: I4. The second ALU sits idle every cycle. Why? There is never a second independent instruction to hand it.
- Count. cycles for instructions. Why this step? Same reason as Ex 1 — total instructions ÷ total cycles gives IPC, and here that ratio exposes the collapse.
Verify: This is (nothing parallelizable) in . Matches. Width bought us nothing — the parent note's "dependency chains are the enemy," proven.
Contrast this figure with the previous one: the red boxes march diagonally, one per cycle on ALU 1, while the whole ALU 2 row is dashed and empty ("idle"). The yellow arrows show each result feeding the next instruction — that arrow chain is why the second lane never fills.

Case C — mixed / partial parallelism (the realistic middle)
Forecast: is the ceiling. Guess whether we get close.
-
Find . = fraction that parallelizes (defined at the top of this page). Here of are independent, parallel work. Why this step? The formula needs the split between "shareable" and "chained" work.
-
Plug into the formula. Why? It's Amdahl's Law for ILP — the serial part sets a hard floor.
= \frac{1}{0.4375} \approx 2.2857 $$ -
Now hand-schedule to confirm. Why? The formula is an idealized estimate; scheduling the actual instructions tells us the real integer cycle count. Our lane-assignment policy is fixed and simple: each cycle, scan instructions in program order and place onto the lowest-numbered free lane every instruction whose operands are already available; skip any whose operands aren't ready yet. Applying that policy tick by tick:
Cycle Lane assignment (by the policy above) C1 Lane 1←, Lane 2←, Lane 3←, Lane 4← — all four are ready and independent, so the first four fill lanes 1–4. C2 Lane 1←, Lane 2←, Lane 3←, Lane 4←idle — ready → lanes 1–2; has no unmet operand → lane 3; nothing left to place → lane 4 idle. C3 Lane 1←, lanes 2–4 idle — 's operand became ready end of C2, so it takes lane 1; no other instruction remains. That is instructions in cycles:
The figure draws this exact schedule: four columns are lanes, three stacked bands are cycles. Watch the fully packed C1 row, the single idle lane in C2, and the lonely in C3 — the emptying staircase is the serial chain draining the parallelism.

Verify: The formula's and the hand-schedule's bracket the realistic range (both between 2 and 3, both far below the ceiling ) — exactly the parent's "real IPC ≈ 1.5–2.5 on a 4-wide core." The hand-schedule is a touch higher because it packed the independent work maximally; the formula is conservative. Both confirm: the 2-long serial chain, not the width, is what stops us reaching 4. ✓
Case D — a multi-cycle latency op
Now we change one knob: latency > 1. Here the multiplier is pipelined (see the definition callout above): it takes 3 cycles to produce a result, but it accepts a new multiply every cycle and does not block the port for the consumer's independent neighbours. (Case E will show the opposite, blocking, situation.)
Forecast: I2 is stuck behind a slow multiply — but I3, I4 are free. Guess the schedule.
- Latency accounting. I1 issues in C1; a 3-cycle latency means
r1is ready at the end of C3, usable in C4. Why this step? Latency is how many cycles pass before a result exists — the consumer's earliest issue is producer-issue + latency. - Fill the wait with independent work. Why? Because the multiplier is pipelined, its 3-cycle
latency does not tie up the ALU — we can slot the independent adds alongside. C1: I1 (mul) + I3
(add). C2: I4 (add). C3: nothing ready (I2 still waiting on
r1). C4: I2 (add), nowr1exists. - Count. Instructions retire by C4 → instructions in cycles. Why this step? We total instructions and the cycle at which the last one is placed, then divide, because IPC is exactly that ratio.
The figure lays the four cycles across, with a multiply lane on top and an ALU lane below. The yellow multiply spans its 3-cycle latency shadow, and you can see the independent adds I3, I4 tucked into that shadow while I2 waits at the far right.

Verify: In-order without renaming/OoO would stall I2 and everything after it at C2, wasting lanes — you'd still finish by C4 here because I3/I4 got hoisted. The multiply latency (not the ALU count) set the critical path: cycles. Units check: cycles, dimensionless IPC ✓.
Case E — structural hazard (only one of a unit, blocking)
Forecast: independent code — but is width enough?
- Spot the structural clash. Why this step? Independence removes data hazards, but both instructions demand the same physical resource — the single multiplier. That's a structural hazard, the parent's obstacle #2.
- Serialize on the scarce unit. C1: I1 uses the multiplier. C2: I2 uses it. Why? One multiplier can start one multiply per cycle; there is no second lane of that type.
- Count. cycles for instructions. Why this step? Instructions ÷ cycles is the definition of IPC, so we tally both to read off the throughput.
The figure shows two lanes but the adder lane greyed out ("no multiply here") so both multiplies are forced onto the single multiplier lane in consecutive cycles — the visual reason width alone couldn't help.

Verify: Had there been two multipliers, both issue in C1 → IPC 2. So the limiter here is port/unit count, not data flow. Fix in real hardware: add multiple execution ports (parent's "multiple ports dissolve structural hazards"). ✓
Case F — false dependencies (WAR and WAW) killed by renaming
Two kinds of false dependency exist — both are naming artifacts, not real data flow, and both vanish under register renaming. We show both.
Forecast: does writing the same name force serialization? Guess yes, then see.
- Naive (no renaming). I1 and I2 both write
r1; hardware fears a WAW conflict and serializes to preserve which write is "last": C1 I1, C2 I2, C3 I3. That's 3 cycles, IPC . Why this step? Without renaming, the register name is the only handle, so the machine can't tell the twor1s apart. - Rename. Map I1's
r1→ physicalp1, I2'sr1→ physicalp7. Why? The WAW is a naming artifact — different physical registers make the conflict vanish. - Re-schedule and count. I1 and I2 are now genuinely independent → issue together in C1. I3
must read the latest
r1=p7(from I2), ready end of C1, so I3 issues C2. Total cycles. Why this step? We divide 3 instructions by the new 2-cycle span because that ratio is IPC.
The figure stacks two mini-schedules: before (three red boxes marching diagonally) versus
after (I1+I2 packed in C1, I3 in C2) — the physical-register relabelling r1→p1, r1→p7 shown
in the boxes makes the fix concrete.

Forecast: I2 overwrites the very r1 that I1 is reading — is that a real conflict?
- Name the hazard. This is a WAR: a later write (
I2→r1) that must not destroy the value an earlier read (I1readsr1) still needs. Why this step? If I2 issued first and wroter1, I1 would read the wrong value — but that's purely about the shared namer1. - Naive in-order. In program order I1 reads first, then I2 writes — safe, but a naive machine still refuses to reorder or overlap them for fear of the WAR, serializing to 2 cycles.
- Rename and count. I2 writes physical
p9instead of reusingr1's physical register. Why? Now I1's read of old-r1and I2's write ofp9touch different physical registers — no ordering constraint at all. They issue together in C1. Total cycle; instructions ÷ cycle gives IPC.
Verify (both): WAW dropped 3 cycles → 2 (IPC 1 → 1.5); WAR dropped 2 cycles → 1 (IPC 1 → 2). Both improvements come only from renaming false dependencies, and in both cases the real data values read/written are unchanged — correctness preserved. This covers both false-dependency kinds promised in the matrix. ✓
Case G — limiting behaviour (the three extremes)
Forecast: guess which extreme still refuses to exceed some fixed number.
- (a) Perfectly parallel, . Why? Tests the ceiling. Hits the ceiling exactly — matches Case A.
- (b) Fully serial, . Why? Tests the floor. No width helps — matches Case B.
- (c) Half parallel, infinite width . Why? Shows the hard ceiling Amdahl imposes even with unlimited units. As , :
Verify: With , throwing infinite execution units at the problem still caps IPC at 2 — the serial half is the wall. This is the deep lesson: the serial fraction, not the unit count, sets the ultimate limit. ✓
The figure plots effective IPC (vertical) against issue width (horizontal) as four rising curves, one per . Watch the green line climb forever (IPC = N), while the yellow curve flattens toward a dotted ceiling at 2 and the red line stays pinned at the floor 1 — the curves visually flattening is Amdahl's wall.

Case H — real-world word problem
Forecast: 2 billion instructions, ~2.5 per cycle, 4 billion cycles/sec — order of magnitude?
- Cycles needed. Why this step? Time flows through cycles; convert instructions → cycles via IPC.
- Seconds per cycle. Why? We need real time, not a cycle count. The clock period is just the reciprocal of the frequency: (We keep this single form — ns — throughout, no second notation.)
- Multiply. Why? Time = cycles × time-per-cycle (the parent's ). With :
Verify: Direct plug-in: s. Units: (instr)(s/cycle)/(instr/cycle) = s ✓. So the program finishes in 0.2 seconds.
Case I — exam-style twist: in-order vs out-of-order, same code
Forecast: the load stalls its consumer; guess whether OoO rescues the independent ops.
- In-order. Why this step? In-order forbids a younger instruction from issuing before an
older stalled one. C1: I1 (load) issues; I2 needs
r1(ready C3) so it blocks, and I3, I4 behind it are stuck too. C2: stall. C3:r1ready; issue I2 + I3 (both now legal). C4: I4. → 4 cycles, IPC . - Out-of-order. Why? OoO lets ready instructions overtake the stalled I2. C1: I1 (load) +
I3 (add, independent). C2: I4 (add). C3: I2 (now
r1ready). → 3 cycles, IPC . - Compare and count. Why this step? Dividing the same 4 instructions by each schedule's cycle count (IPC = instructions ÷ cycles) is what makes the two policies directly comparable.
The figure puts the two schedules side by side: the in-order grid shows a wasted stall column in C2, while the OoO grid shows I3, I4 hoisted forward to fill it — the removed white gap is the speedup.

Verify: OoO never violates correctness — instructions execute out of order but retire in order via the Reorder Buffer, so the architectural state ends identical. Speedup . This is the classic exam contrast between in-order and OoO on identical code. ✓
Recall Self-test: match example → matrix cell
IPC = N happened in which example? ::: Ex 1 (Case A, fully independent). Which example proved width buys nothing? ::: Ex 2 (Case B, pure dependency chain, IPC = 1). Where did renaming fix a WAW, and where a WAR? ::: WAW in Ex 6a (IPC 1→1.5), WAR in Ex 6b (IPC 1→2). With and infinite units, what is the IPC ceiling? ::: 2 (Ex 7, Case G limit). How long did the 2-billion-instruction program take? ::: 0.2 seconds (Ex 8, Case H). OoO vs in-order on the load example gave which two IPCs? ::: 1.0 in-order, 4/3 ≈ 1.333 OoO (Ex 9).
Prerequisites worth revisiting: Instruction-Level Parallelism (ILP), Data Hazards (RAW WAR WAW), Register Renaming, Out-of-Order Execution, Reorder Buffer (ROB), and the throughput ceiling logic in Amdahl's Law.