---
title: Pipelining Principles
module: Pipelining
moduleNumber: 5
lessonNumber: 1
order: 501
summary: >
  A processor that runs one instruction to completion before starting the next
  wastes most of its hardware most of the time. Pipelining splits the work into
  stages separated by registers so several instructions are in flight at once. We
  separate throughput from latency, work the 300 ps example through one, two, and
  three stages, and derive the three ceilings on the gain: uneven stages, register
  overhead, and the dependencies between instructions.
topics: [Pipelining]
sources:
  - book: Bryant & O'Hallaron
    ref: "CS:APP — §4.4 General Principles of Pipelining"
---

The [SEQ processor](/computer-architecture/processor-design/the-seq-stages) is
correct and almost embarrassingly wasteful. It computes one instruction through
all six stages — fetch, decode, execute, memory, write-back, PC update — and only
then fetches the next. While the ALU works, the instruction memory, the register
file write port, and the data memory all sit idle; while the write-back happens,
the ALU sits idle. At any instant five-sixths of the datapath is doing nothing.
**Pipelining** reclaims that wasted hardware: keep every stage
busy by letting it work on a _different_ instruction than its neighbors. This
lesson develops the principle in the abstract, with real delay numbers and the
ceilings on the gain, before the next applies it to Y86-64.

## Throughput is not latency

Two different numbers measure how fast a system runs work, and pipelining trades
one for the other, so we must keep them apart.

> **Definition (Latency and throughput).** The **latency** of an operation is the
> time from when it starts to when it finishes: one instruction's elapsed time.
> The **throughput** is the rate of completed operations, instructions per unit
> time, measured at the system's output. Latency is about a single item;
> throughput is about the stream.

In SEQ the two are locked together: an instruction takes one clock period, and a
new one finishes every clock period, so latency $= 1/\text{throughput}$. The
catch is that the clock period must be long enough for the slowest instruction to
crawl through _all six stages_ in series. Pipelining breaks the lock. It does
nothing to shorten any single instruction's path — latency stays the same or
even grows slightly — but it lets a new instruction _finish_ every short clock
period, multiplying throughput. For a processor running a long stream of
instructions, throughput is what we feel, and throughput is what pipelining buys.

## The laundry analogy

Picture doing four loads of laundry, each needing wash (30 min), dry (30 min),
and fold (30 min). Done strictly in series — wash, dry, fold, then start the next
load — each load takes 90 minutes and four loads take **six hours**. But the
washer, dryer, and table are three separate machines. The moment load 1 leaves
the washer for the dryer, load 2 can start washing. Stagger them and after the
pipeline fills, a finished load comes out every 30 minutes.

$$
% caption: Four laundry loads through wash, dry, fold. Run in series each load
% caption: waits for the one before; pipelined, the washer starts load 2 the moment
% caption: load 1 moves to the dryer, and a load finishes every 30-minute slot.
\begin{tikzpicture}[font=\footnotesize,>=stealth,
  cell/.style={draw, minimum width=12mm, minimum height=7mm, inner sep=0pt}]
  \definecolor{acc}{HTML}{2348F2}
  % time axis labels (each unit = 30 min slot), 8 slots
  \foreach \t in {1,...,8} \node at (\t*1.3-0.65,0.95) {\scriptsize \t};
  \node[anchor=east] at (-0.35,0.95) {\scriptsize slot};
  % four loads, each a staircase W D F
  \foreach \i/\lab in {0/{load 1}, 1/{load 2}, 2/{load 3}, 3/{load 4}}{
    \node[anchor=east] at (-0.35,-\i*0.95) {\lab};
    \node[cell, fill=acc!8] at (\i*1.3+0.65,-\i*0.95) {W};
    \node[cell, fill=acc!8] at (\i*1.3+1.95,-\i*0.95) {D};
    \node[cell, fill=acc!8] at (\i*1.3+3.25,-\i*0.95) {F};
  }
\end{tikzpicture}
$$

Series takes $4 \times 90 = 360$ minutes; pipelined takes $90 + 3 \times 30 =
180$ minutes: the first load's full 90, then one new load every 30-minute slot.
The latency of any single load is unchanged at 90 minutes. What improved is the
rate, a load every 30 minutes instead of every 90. A processor is the same story
with the loads renamed instructions and the machines renamed stages.

## Splitting a computation into stages

The mechanism is to take one block of combinational logic that computes the whole
instruction and **cut it into stages separated by registers**. Each pipeline
register snapshots the partial result at a clock edge and hands it to the next
stage, exactly the clocked register from
[digital logic](/computer-architecture/digital-logic/memory-elements-latches-flip-flops-and-clocking).
Because a register isolates each stage, stage $s$ can work on instruction $i$
while stage $s+1$ works on the instruction ahead of it.

Bryant and O'Hallaron work this with concrete delays, and the numbers are worth
following exactly. Take a computation that needs **300 ps** of combinational
logic, followed by a register that takes **20 ps** to load. Unpipelined, the
clock period must cover both: $300 + 20 = 320$ ps. One instruction finishes per
period, so

$$
\text{Throughput} =
\frac{1 \text{ instruction}}{320 \text{ ps}} \approx 3.12 \text{ GIPS},
$$

about 3.12 billion instructions per second (GIPS), with a latency of 320 ps.
Now cut the logic into three equal 100 ps stages, a register after each:

$$
% caption: One combinational block of delay 300 ps plus its 20 ps output register
% caption: (top, clock period 320 ps) versus the same logic cut into three 100 ps
% caption: stages, each followed by a 20 ps pipeline register (bottom, clock period
% caption: 120 ps). The registers let three instructions occupy the stages at once.
\begin{tikzpicture}[font=\footnotesize,>=stealth,
  blk/.style={draw, fill=acc!8, minimum height=11mm, inner sep=2pt},
  reg/.style={draw, fill=black!10, minimum width=2.2mm, minimum height=13mm,
              inner sep=0pt}]
  \definecolor{acc}{HTML}{2348F2}
  % top: one big block + one register
  \node[blk, minimum width=50mm] (big) at (2.7,1.4) {combinational logic : 300 ps};
  \node[reg] (rt) at (5.9,1.4) {};
  \draw (-0.6,1.4) -- (big.west); \draw (big.east) -- (rt);
  \draw (rt) -- (7.1,1.4);
  % bottom: three stages with registers between
  \node[blk, minimum width=14mm] (a) at (1.0,-0.6) {A : 100};
  \node[blk, minimum width=14mm] (b) at (3.2,-0.6) {B : 100};
  \node[blk, minimum width=14mm] (c) at (5.4,-0.6) {C : 100};
  \node[reg] (r1) at (2.1,-0.6) {};
  \node[reg] (r2) at (4.3,-0.6) {};
  \node[reg] (r3) at (6.5,-0.6) {};
  \draw (-0.6,-0.6) -- (a.west); \draw (a.east) -- (r1);
  \draw (r1) -- (b.west); \draw (b.east) -- (r2); \draw (r2) -- (c.west);
  \draw (c.east) -- (r3); \draw (r3) -- (7.1,-0.6);
  \node[text=acc, anchor=west] at (7.3,1.4) {clock 320 ps};
  \node[text=acc, anchor=west] at (7.3,-0.6) {clock 120 ps};
\end{tikzpicture}
$$

Each clock period now needs to push one instruction through one stage plus its
register: $100 + 20 = 120$ ps. After the pipeline fills, an instruction completes
every 120 ps, a throughput of $1/120 \approx 8.33$ GIPS. But a single
instruction now takes three periods to get through: latency $3 \times 120 = 360$
ps, _worse_ than the 320 ps we started with. The same arithmetic for a two-stage
split (150 ps stages) gives a 170 ps clock. Tabulating all three:

| Design | Clock period | Latency | Throughput | Speedup |
| --- | --- | --- | --- | --- |
| Unpipelined | $300 + 20 = 320$ ps | 320 ps | 3.12 GIPS | $1\times$ |
| 2 stages | $150 + 20 = 170$ ps | 340 ps | 5.88 GIPS | $1.88\times$ |
| 3 stages | $100 + 20 = 120$ ps | 360 ps | 8.33 GIPS | $2.67\times$ |

Two lessons sit in this table. Throughput scales with stage count but never
reaches the ideal: three stages give $8.33/3.12 = 2.67\times$, not $3\times$,
because every stage pays the 20 ps register overhead. And latency creeps _up_ with
depth ($360/320 = 1.12\times$ for three stages), because that same overhead is paid
three times per instruction. Pipelining is purely a throughput optimization.

## The non-pipelined vs pipelined timing diagram

The signature picture of this whole module plots instructions down the side and
clock cycles across the top, filling each cell with the stage that instruction
occupies that cycle. Successive instructions form a diagonal staircase: in cycle
$c$, instruction $i$ is in stage $c - i$, so the stages slide one step right per
instruction.

$$
% caption: Top: non-pipelined, each instruction runs all three stages before the
% caption: next begins (one finishes every 3 cycles). Bottom: pipelined, the stages
% caption: stagger into a staircase and one instruction finishes every cycle.
\begin{tikzpicture}[font=\footnotesize,>=stealth,
  cell/.style={draw, minimum width=9mm, minimum height=7mm, inner sep=0pt}]
  \definecolor{acc}{HTML}{2348F2}
  % --- non-pipelined (top group) ---
  \node[anchor=west, text=acc] at (-1.7,2.05) {non-pip\/elined};
  \foreach \c in {1,...,6} \node at (\c*1.0-0.5,1.5) {\scriptsize \c};
  % instr 1 in cycles 1-3, instr 2 in cycles 4-6
  \node[anchor=east] at (-0.15,0.8) {I1};
  \node[cell, fill=acc!8] at (0.5,0.8) {A};
  \node[cell, fill=acc!8] at (1.5,0.8) {B};
  \node[cell, fill=acc!8] at (2.5,0.8) {C};
  \node[anchor=east] at (-0.15,0.0) {I2};
  \node[cell, fill=acc!8] at (3.5,0.0) {A};
  \node[cell, fill=acc!8] at (4.5,0.0) {B};
  \node[cell, fill=acc!8] at (5.5,0.0) {C};
  % --- pipelined (bottom group) ---
  \node[anchor=west, text=acc] at (-1.7,-1.05) {pip\/elined};
  \foreach \c in {1,...,6} \node at (\c*1.0-0.5,-1.5) {\scriptsize \c};
  \foreach \i/\y/\lab in {0/-2.2/I1, 1/-3.0/I2, 2/-3.8/I3, 3/-4.6/I4}{
    \node[anchor=east] at (-0.15,\y) {\lab};
    \node[cell, fill=acc!8] at (\i*1.0+0.5,\y) {A};
    \node[cell, fill=acc!8] at (\i*1.0+1.5,\y) {B};
    \node[cell, fill=acc!8] at (\i*1.0+2.5,\y) {C};
  }
\end{tikzpicture}
$$

Read the staircase: in the pipelined run, cycle 3 has I1 in stage C, I2 in stage
B, I3 in stage A — three instructions in flight, every stage busy. The
non-pipelined version finishes I2 at cycle 6; the pipelined version has already
finished I1 (cycle 3), I2 (cycle 4), I3 (cycle 5), I4 (cycle 6). Same latency per
instruction, triple the completions.

### A finite stream, counted exactly

The staircase makes the steady state look like a clean $k\times$ win, but a real
program is finite, and the first few and last few cycles never run full. Count a
concrete run: $N$ instructions through the three 120 ps stages. The first
instruction needs 3 cycles to reach stage C, and after that one instruction
finishes every cycle, so the last of the $N$ finishes at cycle $N + 2$. Total
time $= (N + 2) \times 120$ ps. The unpipelined machine takes $N \times 320$ ps.

$$
\text{speedup}(N) = \frac{320\,N}{120\,(N+2)} = \frac{2.67\,N}{N+2}.
$$

Plug in numbers to see the fill and drain cost fade as the stream grows:

| Instructions $N$ | Unpipelined | Pipelined | Speedup |
| --- | --- | --- | --- |
| 3 | 960 ps | 600 ps | $1.60\times$ |
| 10 | 3200 ps | 1440 ps | $2.22\times$ |
| 100 | 32000 ps | 12240 ps | $2.61\times$ |
| $\infty$ | — | — | $2.67\times$ |

Three instructions barely break even because two of the five cycles are spent
filling the pipe; a hundred instructions get within $2\%$ of the steady-state
ceiling. This is why pipeline speedup is quoted for long instruction streams:
the fill-and-drain overhead is a fixed $k - 1$ cycles amortized over the whole
run, negligible once $N \gg k$. A processor executing billions of instructions
lives entirely in the flat part of that curve.

Nothing about the clock has to be clever for this to work. The registers only
load on the rising edge, so signals racing through a stage's logic at different
speeds cannot interfere: whatever reaches a pipeline register's input before the
edge is captured, and nothing changes downstream until the edge. Running the
clock _slower_ than 120 ps would still compute correctly, just more slowly.
Running it faster would capture garbage — inputs that had not finished
propagating. The clocked register discipline from
[sequential logic](/computer-architecture/digital-logic/memory-elements-latches-flip-flops-and-clocking)
is the entire control mechanism.

## Limit 1: the slowest stage sets the clock

The three-stage example split 300 ps into three perfectly equal parts. Real
hardware rarely cooperates. Suppose the logic divides only into chunks of 50,
150, and 100 ps — still 300 ps total. The clock period must be long enough for
the _slowest_ stage plus its register: $150 + 20 = 170$ ps. Stage A then works
50 ps and idles 120; stage C works 100 ps and idles 70. Only stage B stays busy.

$$
% caption: Nonuniform division: stages of 50, 150, and 100 ps. The clock period
% caption: must cover the slowest stage plus a register, 150 + 20 = 170 ps, so the
% caption: faster stages idle (dashed) for most of every cycle.
\begin{tikzpicture}[font=\footnotesize,>=stealth,
  busy/.style={draw, fill=acc!8, minimum height=6mm, inner sep=0pt, anchor=west}]
  \definecolor{acc}{HTML}{2348F2}
  % scale: 0.024 units per ps -> period 170ps = 4.08
  % stage A: busy 50 (1.2), idle 120 (2.88)
  \node[anchor=east] at (-0.3,1.6) {A : 50 ps};
  \node[busy, minimum width=12mm] at (0,1.6) {};
  \draw[black, dashed] (1.2,1.3) rectangle (4.08,1.9);
  \node[black] at (2.64,1.6) {\scriptsize idle 120 ps};
  % stage B: busy 150 (3.6), idle 20 -> the register slot
  \node[anchor=east] at (-0.3,0.8) {B : 150 ps};
  \node[busy, minimum width=36mm] at (0,0.8) {};
  \draw[black, dashed] (3.6,0.5) rectangle (4.08,1.1);
  % stage C: busy 100 (2.4), idle 70 (1.68)
  \node[anchor=east] at (-0.3,0.0) {C : 100 ps};
  \node[busy, minimum width=24mm] at (0,0.0) {};
  \draw[black, dashed] (2.4,-0.3) rectangle (4.08,0.3);
  \node[black] at (3.24,0.0) {\scriptsize idle};
  % period bracket
  \draw[<->] (0,-0.75) -- (4.08,-0.75);
  \node[anchor=north] at (2.04,-0.85) {one clock period = 170 ps};
\end{tikzpicture}
$$

Throughput drops to $1/170 \approx 5.88$ GIPS — the same as an even _two_-stage
split, even though we built three stages — and latency rises to $3 \times 170 =
510$ ps. Unbalanced stages waste exactly what pipelining was meant to reclaim,
which is why so much of real pipeline design is timing optimization: shuffling
work across stage boundaries to equalize delays.

The payoff from rebalancing is worth working through, because it shows the clock
is set by a single stage. Suppose 20 ps of the middle stage's logic can be
moved into the first stage, turning the 50/150/100 split into 70/130/100. The
clock now tracks the new slowest stage, $130 + 20 = 150$ ps:

| Split (ps) | Slowest stage | Clock period | Throughput |
| --- | --- | --- | --- |
| 50 / 150 / 100 | 150 | 170 ps | 5.88 GIPS |
| 70 / 130 / 100 | 130 | 150 ps | 6.67 GIPS |
| 100 / 100 / 100 | 100 | 120 ps | 8.33 GIPS |

Shaving 20 ps off the bottleneck bought $13\%$ throughput for zero extra
hardware; the other two stages did not change at all. Only when every stage is
equal does the clock reach its floor. The whole 300 ps of logic is unchanged in
each row — the same total work — yet throughput swings by $40\%$ purely on how
evenly the boundaries fall. A designer stares at exactly this table and moves
logic across register boundaries until the column of stage delays is as flat as
the circuit allows. The problem is stubborn because
some units are indivisible. An ALU or a memory read is a single tightly coupled
block; you cannot cheaply cut it into two half-delay pieces. Whichever stage
inherits the biggest indivisible unit sets the clock for everyone.

## Limit 2: register overhead and diminishing returns

If unbalanced stages are the first cost, the pipeline registers themselves are
the second. Each register adds its 20 ps to _every_ period, no matter how short
the stage logic gets. Cut the 300 ps into six 50 ps stages and the period is
$50 + 20 = 70$ ps, a throughput of 14.29 GIPS. We doubled the number of stages
and gained only $14.29 / 8.33 = 1.71\times$, because the register delay is now
$20/70 \approx 29\%$ of every cycle. Push to $k$ stages and the period is
$300/k + 20$ ps: the logic term shrinks toward zero, the overhead term never
budges, and throughput approaches a hard ceiling of $1/20$ ps $= 50$ GIPS that
no depth can pass. Meanwhile latency, $k \cdot (300/k + 20) = 300 + 20k$ ps,
grows without bound.

$$
% caption: Throughput 1/(300/k + 20) against pipeline depth k. The dashed line is
% caption: the ideal k-fold scaling; the actual curve bends away from it as the
% caption: fixed 20 ps register overhead dominates the shrinking stage time, toward
% caption: a ceiling of 50 GIPS (off the top of the chart).
\begin{tikzpicture}[font=\footnotesize,>=stealth]
  \definecolor{acc}{HTML}{2348F2}
  % axes: x = 0.55 per stage, y = 0.1 per GIPS
  \draw[->] (0,0) -- (9.4,0) node[anchor=west] {stages k};
  \draw[->] (0,0) -- (0,3.3) node[anchor=south east] {GIPS};
  \foreach \k in {1,2,4,8,16} \node[anchor=north] at (\k*0.55,-0.02) {\scriptsize \k};
  \foreach \g in {10,20} {
    \draw (0.06,\g*0.1) -- (-0.06,\g*0.1);
    \node[anchor=east] at (-0.1,\g*0.1) {\scriptsize \g};
  }
  % ideal: 3.125k GIPS, dashed, exits top around k=10.5
  \draw[black, dashed] (0.55,0.31) -- (2.2,1.25) -- (4.4,2.5) -- (5.6,3.18);
  \node[anchor=west, black] at (1.1,2.5) {\scriptsize ideal k-fold};
  % actual: 1/(300/k+20): k=1:3.12 2:5.88 3:8.33 4:10.53 6:14.29 8:17.39
  %         10:20 12:22.2 16:25.8
  \draw[acc, thick]
    (0.55,0.31) -- (1.1,0.59) -- (1.65,0.83) -- (2.2,1.05) --
    (3.3,1.43) -- (4.4,1.74) -- (5.5,2.00) -- (6.6,2.22) -- (8.8,2.58);
  \node[anchor=north west, text=acc] at (5.7,1.75) {\scriptsize actual: 1/(300/k + 20)};
\end{tikzpicture}
$$

The curve climbs fast for small $k$, then flattens; the widening gap to the
dashed line is precisely the accumulated register overhead. There is a sweet
spot — a handful to a couple dozen stages — past which adding stages buys almost
nothing while costing area, power, and single-instruction latency. Modern
processors do run deep, fifteen or more stages, but their designers fight for
every picosecond of register delay and pay a further price this module has not
yet named.

## Limit 3: the instructions are not independent

Laundry loads are independent. Instructions are not. Everything so far
assumed the items flowing through the pipeline are independent, and for a
processor that assumption is false in two specific ways:

```asm [depend.ys]
irmovq $50, %rax        # writes rax
addq   %rax, %rbx       # reads rax, writes rbx
mrmovq 100(%rbx), %rdx  # reads rbx
```

Each instruction here consumes a value the previous one produces: a **data
dependency**. And control flow creates the second kind:

```asm [control.ys]
subq %rdx, %rbx
jne  target             # which instruction is next?
```

Until the `jne` decides, the very identity of the next instruction is unknown: a
**control dependency**.

$$
% caption: The two dependency kinds. Left: each instruction reads a register the
% caption: previous one writes (a data dependency chain). Right: until the jump
% caption: resolves, the next instruction to fetch is unknown (a control dependency).
\begin{tikzpicture}[font=\footnotesize,>=stealth,
  ins/.style={draw, fill=acc!8, minimum width=34mm, minimum height=7mm,
              inner sep=2pt, align=center}]
  \definecolor{acc}{HTML}{2348F2}
  % left: data chain
  \node[ins] (i1) at (0,1.6)  {irmo\/vq {\char36}50,\%rax};
  \node[ins] (i2) at (0,0.4)  {addq \%rax,\%rb\/x};
  \node[ins] (i3) at (0,-0.8) {mrmo\/vq 100(\%rb\/x),\%rdx};
  \draw[acc, thick, ->] (i1.south) -- (i2.north);
  \node[anchor=west, text=acc] at (0.25,1.0) {\scriptsize \%rax};
  \draw[acc, thick, ->] (i2.south) -- (i3.north);
  \node[anchor=west, text=acc] at (0.25,-0.2) {\scriptsize \%rb\/x};
  \node[anchor=north, black] at (0,-1.35) {\scriptsize data dep\/endencies};
  % right: control
  \node[ins, minimum width=26mm] (j) at (5.6,1.6) {jne target};
  \node[ins, minimum width=26mm] (t) at (4.3,-0.2) {target: ...};
  \node[ins, minimum width=26mm] (f) at (7.4,-0.6) {fall-through: ...};
  \draw[acc, thick, dashed, ->] (j.south west) ++(0.5,0) -- (t.north);
  \draw[acc, thick, dashed, ->] (j.south east) ++(-0.5,0) -- (f.north);
  \node[anchor=north, black] at (5.9,-1.35) {\scriptsize control dep\/endency};
\end{tikzpicture}
$$

In SEQ, dependencies cost nothing: each instruction fully completes — its
register writes land, its next-PC is computed — before the next begins, so every
value is always ready. That safety came from the feedback paths that carry
results from late in the datapath back to the register file and the PC at the
start. Pipelining breaks exactly those paths. When the `addq` needs `%rax`, the
`irmovq` that produces it is still two stages from writing it back; when fetch
needs the next PC, the `jne` that determines it has not yet executed. Naively
inserting pipeline registers into a system with feedback _changes what it
computes_, which is unacceptable: the pipelined machine must honor the ISA's
one-at-a-time semantics while overlapping everything it can. Making that true is
the work of the next three lessons, and the penalties it forces — stalls and
squashed instructions — are the third and final limit on pipeline speedup.

## Two ways past the single-issue ceiling

Limit 2 fixed a ceiling: a single pipeline finishing one instruction per cycle
tops out at $1/t_{\text{reg}}$, no matter the depth. Real processors blew past
that ceiling with two ideas the CS:APP pipeline does not use, both worth knowing
because they frame everything the rest of this module builds toward.

The first is **superpipelining** — cutting the pipeline far deeper than five
stages so the clock period shrinks toward the register overhead. The MIPS R4000 (1991)
ran an 8-stage integer pipeline; Intel's Pentium 4 "Prescott" (2004) reached 31
stages chasing clock frequency, splitting even a single ALU add across cycles.
Prescott is also the cautionary tale: the same depth that let the clock hit
nearly 4 GHz turned every branch misprediction into a 30-plus-cycle drain and
made the chip infamous for heat, and Intel abandoned the deep-pipeline strategy
for the wider, shallower Core architecture (Hennessy and Patterson, _Computer
Architecture: A Quantitative Approach_, on the "power wall"). Depth trades
latency and misprediction cost for clock rate, and past a point the trade stops
paying — exactly the diminishing-returns curve above, now with power as the third
axis.

The second, and the one that actually broke the one-per-cycle ceiling, is
**superscalar** execution: replicate the stages so the processor fetches,
decodes, and completes _several_ instructions per cycle. A 4-wide superscalar has
an ideal CPI of $0.25$ — impossible for any single-issue pipeline however deep.
The idea entered the mainstream with the Intel Pentium (1993, two integer
pipelines) and the DEC Alpha 21064, and every performance core since is
superscalar. Issuing multiple instructions per cycle sharpens the dependency
problem this lesson named as Limit 3: now instructions that could run together
must be found and checked against each other _within_ a single cycle. Resolving
that led to **out-of-order execution** (Tomasulo's algorithm, IBM System/360
Model 91, 1967), which the final lesson of this module returns to. For now the
takeaway is that pipelining is the first and simplest form of instruction-level
parallelism, and the ceilings derived here are precisely what superpipelining and
superscalar issue were invented to climb over.

> **Takeaway.** Pipelining cuts a computation into $k$ register-separated stages
> so $k$ instructions are in flight at once. It leaves single-instruction
> **latency** alone (or slightly worse) but multiplies **throughput**: for 300 ps
> of logic and 20 ps registers, one stage gives 3.12 GIPS, two give 5.88, three
> give 8.33 — a $2.67\times$ speedup, short of ideal because every stage pays the
> register tax. Three ceilings cap the gain: the clock is set by the **slowest
> stage**, the fixed **register overhead** bounds throughput at $1/20$ ps no
> matter the depth, and **dependencies** between instructions force the pipeline
> to sometimes wait or guess.

The next lesson stops drawing abstract A/B/C stages and pipelines the real
thing: inserting registers between
[the SEQ stages](/computer-architecture/processor-design/the-seq-stages) to
build PIPE.
