Around 2005, something broke that had held for thirty years: clock frequencies stopped rising. A 2004 desktop CPU ran at 3.8 GHz; a 2024 one runs at… roughly 5 GHz. Meanwhile the transistor budget kept right on doubling — from tens of millions to tens of billions. So chip architects faced a genuinely strange problem, the opposite of scarcity: an enormous and growing pile of transistors that could no longer buy single-thread speed. What do you spend them on?
The GPU is the most radical answer anyone gave: spend them on more, simpler cores — hundreds of them — and stop pretending any one thread matters. This lesson is about why that answer was not a stylistic choice but a corner the whole industry was backed into, and why, for the right class of work, the wimpy-cores bet wins on every metric that matters: operations per joule and operations per square millimetre.
Two independent forces converged. First, the end of
Second, the
Put the two together: transistors kept arriving (Moore continued for years after Dennard died), but neither frequency nor cleverness could convert them into serial speed. The only direction left was sideways: use the budget for parallel speed — many cores, each one small, simple, in-order, and cheap.
A sea of simple cores is useless unless work arrives in a matching shape: millions of independent, identical tasks. It turns out an enormous class of computing looks exactly like that:
These are throughput workloads: nobody cares when any individual task finishes, only when all of them do. Latency per task is irrelevant; tasks per second is everything. For this shape of work, a processor optimised to make one thread fast is spending almost its entire area and energy on a property nobody asked for.
Here is the tradeoff in one picture. Take a fixed die area and slice it into
Read both curves at
The area argument has an even sharper energy twin. Ask what a big out-of-order core actually spends per instruction, and split it into work (the arithmetic you asked for) and tax (everything the core does to find, order and speculate around that arithmetic). Illustrative numbers for one 32-bit operation:
| Energy sink | Big OoO core | Simple in-order lane |
|---|---|---|
| Fetch + decode (per instruction) | ~20 pJ | shared across 32 lanes: ~1 pJ |
| Branch prediction, rename, scheduling | ~25 pJ | — (in-order, no speculation) |
| Register file, bypass network | ~15 pJ | ~3 pJ (small, local) |
| The ALU operation itself | ~5 pJ | ~5 pJ |
| Total per operation | ~65 pJ | ~9 pJ |
The punchline sits in the bottom-left cell: on a big core, the operation you wanted is about a tenth of the energy bill — the rest is overhead for making one stream go fast. A simple lane that fetches one instruction and applies it across 32 pieces of data amortises almost all of the tax away, landing at roughly 10–20× less energy per operation. Under a fixed power budget — which, post-Dennard, every chip lives under — energy per operation is throughput. The wimpy cores don't just fit more ops per mm²; they fit more ops per joule, and the joules are the binding constraint.
Before crowning the throughput machine, Amdahl's law demands its say. If a fraction
A workload that is 95% parallel caps at
In May 2004 Intel cancelled Tejas, the successor to the Pentium 4 — a core designed for a rumoured 7 GHz that, in prototype, ran so hot the roadmap collapsed under it. The company pivoted its entire line to multicore within two years; insiders called it "the right-hand turn." The Pentium 4's NetBurst pipeline was the last great monument to frequency-at-any-cost — over 30 pipeline stages, a design that only made sense if Dennard scaling kept paying. It didn't. The GPU makers, watching from the graphics side, realised their sea-of-simple-lanes design wasn't a niche adaptation for pixels — it was the general answer to the wall everyone had just hit. Three years later NVIDIA shipped CUDA, and "graphics card" quietly stopped being an accurate name.
The seductive error: "the GPU has 10,000 cores, so my program will run 10,000× faster." Port a sequential algorithm to a throughput processor and it runs on one wimpy lane — a lane deliberately built slower than a CPU core (no out-of-order window, no branch predictor worth the name, a modest clock). Your code gets slower, often dramatically. The throughput win exists only past thousands of independent work items — enough to fill the lanes and hide the stalls. One task: CPU. A dozen tasks: still CPU. A million tasks: now the GPU is unbeatable. Counting your independent work items is the first act of any porting decision, before a single line moves.