5.3.14 · D2Advanced Microarchitecture

Visual walkthrough — Simultaneous multithreading (SMT - hyperthreading)

2,281 words10 min readBack to topic

The parent note claims that two-way SMT gives a throughput speedup of about not the you might expect. This page builds that number from absolute zero, one picture at a time. We assume nothing except: a processor does work each clock tick, and sometimes it can't.

If you have not yet met the idea of many execution units working at once, peek at Superscalar Processors first — but everything you need is re-built here.


Step 1 — What a "cycle" and an "execution unit" actually are

WHAT. A processor runs to the beat of a clock. Each tick is one cycle. Inside the core there are little worker-machines called execution units — an ALU adds numbers, an FPU handles decimals, a load/store unit talks to memory. We write the number of these units as ; in our running example , so four pieces of work can happen in one tick.

WHY start here. Every quantity later — utilization, IPC, speedup — is just counting boxes filled per tick. If you can see the boxes, the algebra is bookkeeping.

PICTURE. Look at the grid below. Each column is one cycle (one tick of the clock). Each row is one execution unit — so there are rows. A filled box means "this unit did useful work this cycle"; an empty box means "idle — nobody had work ready for it."

Figure — Simultaneous multithreading (SMT - hyperthreading)

Step 2 — Why a single thread leaves boxes empty

WHAT. A thread is one running program: a stream of instructions with a Program Counter (PC) marking "where am I." Feed one thread into our -wide core and count the filled boxes.

WHY. Programs are not smooth. Three things create empty columns (the parent calls them bubbles):

  1. Data dependencies — instruction needs 's answer, so it must wait (see Pipeline Hazards).
  2. Cache misses — memory is far away; a miss freezes the thread for 50–200 cycles (see Memory Hierarchy).
  3. Branch mispredictions — a wrong guess flushes work (see Branch Prediction).

PICTURE. The red block below is a cache-miss stall: every unit is idle because the thread has no ready instruction to give anyone. The hardware exists; it simply has no work.

Figure — Simultaneous multithreading (SMT - hyperthreading)

Read it as: filled boxes ÷ all boxes in a column. Most of the grid is empty. That waste is the whole opportunity.


Step 3 — Turn IPC into a time (the baseline number)

WHAT. We want a concrete cycles figure to compare against. Time is just work divided by rate. Fix a workload of instructions per thread — we will reuse this same for every case on this page so the comparisons stay honest.

WHY division. IPC is "instructions per cycle." Flip it: cycles per instruction is . Multiply by how many instructions we owe and the cycles fall out. This is the only tool we need — no calculus, just the definition of a rate.

With IPC on an -wide core, utilization is — five-eighths of the grid sits empty.

PICTURE. The bar shows 667 cycles of wall-clock time, colour-split into the ~37.5% that did work and the ~62.5% wasted on stalls.

Figure — Simultaneous multithreading (SMT - hyperthreading)

Step 4 — Drop a second thread into the empty boxes

WHAT. Now replicate the cheap state — a second PC and a second architectural register file — so the core holds two thread contexts at once. Everything expensive (the four execution units, the caches, the branch predictor) stays shared.

WHY it works. When thread A stalls (its column would be empty), thread B usually has unrelated work ready — B's cache miss and A's cache miss don't happen at the same instant. So B's filled boxes slot into A's empty ones.

PICTURE. Same grid as Step 1, now two-coloured: blue boxes = thread A, pink boxes = thread B. Notice the red stall column from Step 2 is now mostly pink — B filled A's hole.

Figure — Simultaneous multithreading (SMT - hyperthreading)

Step 5 — Count the combined work, and why each thread slows a little

WHAT. With both threads live, measure IPC again. Each thread now runs at IPC , lower than its solo .

WHY the slowdown? They now share finite things:

  • cache capacity → more misses each (see Cache Coherence for what happens when they touch the same data),
  • reorder-buffer slots → a smaller out-of-order window each,
  • one branch predictor → they overwrite each other's history.

But the combined rate rises (still capped by ):

Utilization climbs to . Per thread the time is worse (same ):

...but both threads finish in that same 833 cycles, because they ran together, not one-after-the-other.

PICTURE. Two stacked bars. Top: solo thread, 667 cycles, mostly empty. Bottom: SMT, 833 cycles but doing two threads' worth of work — visibly fuller.

Figure — Simultaneous multithreading (SMT - hyperthreading)

Step 6 — The speedup number

WHAT. Speedup = (work rate with SMT) ÷ (work rate without).

WHY a ratio. "Speedup" only means anything as a comparison. Put total instructions over total cycles for each case, then divide.

Both numerator and denominator are just "instructions ÷ cycles," i.e. IPC — the 's cancel — so the whole thing collapses to . The dream fails by exactly the amount the threads slowed each other (Step 5).

Figure — Simultaneous multithreading (SMT - hyperthreading)

Step 7 — The two extreme cases (why "it depends")

WHAT. Speedup swings with what the threads do. We show both ends, keeping the same instructions per thread as everywhere else — only the IPC changes, so every cycle count below is just .

Case A — complementary (memory-bound + CPU-bound). Thread A is pure compute, solo IPC ; thread B is a pointer-chase that stalls most cycles, solo IPC .

  • Solo cycles: A , B .
  • Sequential (run one, then the other): cycles.
  • SMT: A's compute drops into B's stall-holes, so A barely slows (IPC ) and B slightly speeds (IPC ): A , B .
  • Both run together, so finish .
  • Speedup . Big win — the long memory pole hid A's work inside it.

Case B — identical (both CPU-bound). Both threads hammer the ALUs, solo IPC each.

  • Solo cycles: each .
  • Sequential: cycles.
  • SMT: no empty boxes to steal, so contention splits the units and each drops to IPC : each .
  • Both together, so finish cycles.
  • Speedup . Almost nothing.

WHY show both. SMT is opportunistic, not magic: it can only fill boxes that were empty. Two greedy threads leave no gaps.

PICTURE. Left panel: complementary threads interlocking like puzzle pieces (near-full grid). Right panel: identical threads colliding, grid already full so no gain.

Figure — Simultaneous multithreading (SMT - hyperthreading)

Step 8 — Degenerate limits and the model behind them

WHAT. Cases A and B suggest the answer depends on how often the units sit idle. Let be the fraction of a thread's cycles that are memory-bound (execution units idle, boxes empty) — so is the fraction that is CPU-bound (units already full). We now build a formula for the SMT speedup directly from these two fractions.

WHY split the timeline this way. SMT can only help where there is an empty box. So split a thread's total time into the two kinds of cycle and handle each on its own:

  • CPU-bound cycles, fraction . The execution units are already saturated — the grid column is full. A second thread finds no empty box, so these cycles get no help: they still cost their full share. This contributes the term to the SMT running time.
  • Memory-bound cycles, fraction . The column is empty, so the second thread can fill boxes. Conservatively assume it fills half of the wasted work — the two threads share the idle window. So this chunk now costs only instead of . This contributes the term .

Add the two chunks to get the SMT running time (as a fraction of the solo time), then invert to turn "less time" into "more speed":

The on top is the solo running time (100% of itself); dividing gives how many times faster SMT is.

PICTURE. The speedup curve from to : flat near on the left (nothing to fill), rising to on the right (everything can be filled), with our three checkpoints marked.

Figure — Simultaneous multithreading (SMT - hyperthreading)

Now push to its edges as a sanity check:

(memory-bound share) meaning speedup
never stalls, grid always full (no gain)
always stalling, grid always empty (ideal ceiling)
heavily memory-bound

Why these matter. They bracket reality. You can never do worse than (SMT never removes work already fitting) nor better than with two threads (you only have two threads to fill boxes). Every real workload lands strictly between.


The one-picture summary

Everything above in a single frame: the empty solo grid, the second thread pouring into the holes, and the resulting arrow — plus the reminder that the gain lives only in the empty boxes.

Figure — Simultaneous multithreading (SMT - hyperthreading)
Recall Feynman retelling — say it in plain words

A processor is a factory row of four machines, ticking once per clock. One worker (a thread) hardly ever keeps all four busy — half the time he's waiting for parts to arrive from the far-away warehouse (memory), and every machine just sits there. So we invite a second worker onto the same floor. He doesn't need his own machines — he uses the ones standing idle whenever the first worker is stuck waiting. Now, in the same amount of time, roughly one-and-a-half to two times as many parts get built. But two catches: they share the warehouse, so both wait a bit longer for parts (each is a touch slower alone); and if both workers only ever want the same machine, there are no idle machines to share and you gain almost nothing. So SMT is worth a lot when the two jobs need different things, and almost nothing when they need the same thing.

Recall

What single quantity, read off the grid, is "filled boxes per column"? ::: IPC (instructions per cycle) — average execution units doing useful work each cycle, capped at . Why is two-way SMT speedup and not in the worked example? ::: Because shared caches, ROB slots and the branch predictor drop each thread from IPC to ; combined . In the model, what speedup do you get at and ? ::: (never stalls, no empty boxes to fill) and (always stalls, second thread fills half of everything). When does SMT help most? ::: When threads are complementary — one memory-bound (leaves holes), one CPU-bound (fills them).