What Is Actually Inside a CPU
A CPU is not one thing that runs instructions. It is a front end that fetches and decodes them, a set of execution units that do the work, a register file they read and write, and caches feeding all of it — with most of the silicon spent on keeping those units busy rather than on the arithmetic itself.
Software view, hardware view
The gap between what you wrote and what the machine does is where this whole domain lives.
The blocks, and what each one is for
A useful first model of a core has four regions. The front end turns bytes in memory into operations the machine can schedule: it fetches from the instruction cache, decodes, and predicts where control flow goes next. The back end executes: a scheduler picks operations whose inputs are ready and issues them to execution units — integer ALUs, load/store units, vector units, and usually more than one of each. The register file is the working storage those units read and write. The cache hierarchy sits behind everything, because main memory is far enough away that a core would otherwise spend most of its life waiting.
What is worth noticing is the proportion. The arithmetic — the part that corresponds to a + b in source — is a small fraction of the machine. Most of the transistor budget goes to *keeping the arithmetic fed*: caches, prediction, buffering, scheduling. That proportion is the entire reason this domain exists. If arithmetic were the bottleneck, performance work would be about counting operations. It usually is not, so it usually is not.
The split between front end and back end is the first diagnostic distinction to learn. A core can be starved at the front (it cannot supply operations fast enough — instruction cache misses, mispredictions, decode limits) or stalled at the back (it has operations but cannot complete them — waiting for data, waiting for a busy unit, waiting on a dependency). These are different problems with different fixes, and Busy Is Not the Same as Working is where that split gets turned into a measurement.
Front end starved, or back end stalled?
Because the two halves fail differently, the same symptom — "the CPU is busy but slow" — has two completely different explanations, and the counters distinguish them cleanly. A front-end problem means the machine cannot get operations to the scheduler: the code footprint is too large for the instruction cache, or branches are unpredictable enough that the fetcher keeps being redirected. A back-end problem means operations are queued and cannot retire: they are waiting for a load, for a divider, or for each other.
This is not an academic split. The fixes are disjoint. Front-end pressure is addressed by making code smaller and more predictable — less inlining, fewer branches on unpredictable data, hot paths kept together (Your Code Is Data Too, Branch Prediction: Guessing Well Enough to Matter). Back-end pressure is addressed by making data closer or dependency chains shorter (Memory Moves in Lines, Not Variables, Dependency Graphs: The Real Shape of Your Code). Applying the wrong one is how a week disappears with nothing to show.
A learner should leave this lesson able to ask, of any slow loop: is the machine failing to *supply* work, or failing to *finish* it? That question is answerable with counters and unanswerable by reading source code, which is the recurring shape of this whole domain.
| Front end starved | Back end stalled | |
|---|---|---|
| What is happening | Scheduler is empty; nothing to issue | Scheduler is full; nothing can complete |
| Typical causes | Instruction cache misses, branch mispredictions, decode bottlenecks | Data cache misses, long dependency chains, contended units like the divider |
| Counter signature | High front-end stall cycles; high branch-miss or I-cache-miss rate | High back-end stall cycles; high data cache or memory counters |
| What helps | Smaller hot code, more predictable branches, better layout | Better data locality, shorter dependency chains, more parallel work |
| What does not | Optimising arithmetic that was never the constraint | Shrinking code that was already fitting comfortably |
| Read next | Your Code Is Data Too, Misprediction: What a Wrong Guess Costs | The Memory Hierarchy, Instruction-Level Parallelism |
What this picture deliberately leaves out
Every block diagram of a CPU is a lie of omission, and it is worth naming the omissions rather than discovering them later as contradictions. The model above shows one of each unit; real cores have several integer ALUs and can issue multiple operations per cycle (Superscalar Execution). It shows instructions flowing in order; real cores execute them as inputs become available and only *retire* them in order (Out-of-Order Execution, The Reorder Buffer and Precise State). It shows a register file with the registers the ISA defines; real cores have many more physical registers and rename onto them (Register Renaming).
It also omits everything about how bytes become operations on a variable-length ISA, where a single instruction may decode into several internal operations, and where a cache of already-decoded operations can bypass the decoders entirely. That machinery is real and it is microarchitectural — meaning it differs between vendors, between generations from the same vendor, and sometimes between cores on the same die.
The reason to state this explicitly is that these diagrams are how misconceptions get installed. A learner who believes the picture is the machine will later conclude that a modern CPU executes one instruction at a time, in order, and that its speed is its clock rate. All three are wrong, and ISA vs Microarchitecture: The Distinction Everything Depends On and The Clock: Why GHz Is Not Performance exist to unpick exactly those.
- Omitted: width. Real cores fetch, decode, issue and retire several operations per cycle.
- Omitted: order. Execution is out of order; only retirement is in order.
- Omitted: renaming. The registers the ISA names are not the registers the machine uses.
- Omitted: the memory pipeline. Load/store queues, fill buffers and prefetchers sit between the load unit and the cache.
- Omitted: everything heterogeneous. Many current CPUs mix core types with different capabilities on one die.
| Omitted | Reality | Covered in |
|---|---|---|
| Width | Several operations fetched, issued and retired per cycle | Superscalar Execution |
| Ordering | Execution follows operand readiness; only retirement is in order | Out-of-Order Execution |
| Renaming | Architectural register names are mapped onto a larger physical file | Register Renaming |
| Memory pipeline | Load/store queues, fill buffers and prefetchers sit before the cache | Prefetching: The Hardware Guesses What You Will Read Next |
| Heterogeneity | Many current CPUs mix core types with different capabilities on one die | Core, Hardware Thread, Software Thread |
Key points
- A CPU is a front end that supplies operations and a back end that executes them; most silicon exists to keep the arithmetic fed rather than to perform it.
- Front-end starvation and back-end stalling produce the same "busy but slow" symptom and have disjoint fixes.
- The register file, execution units and caches are the parts your code actually interacts with, mostly without saying so.
- Every CPU block diagram omits width, out-of-order execution and renaming — treat it as a teaching device, not a description.
- The question worth carrying forward: is the machine failing to supply work, or failing to finish it?
Follow the mechanism
The path through the machine, hop by hop — and the conclusions it invites that are wrong.
- 1Program counter → instruction cache: the front end requests the bytes at the current address.
- 2Instruction cache → decode: raw bytes are turned into internal operations the scheduler understands.
- 3Decode → scheduler: operations wait here until their input operands are available.
- 4Scheduler → execution unit: an operation whose inputs are ready is issued to a free ALU, load/store unit or vector unit.
- 5Execution unit → register file → retirement: the result is written back and the instruction becomes architecturally visible in program order.
- • "The CPU is at 100%, so it is compute-bound." Utilisation counts cycles in which the core was not idle, including cycles spent entirely stalled on memory.
- • "Fewer instructions means faster." Only if the machine was limited by instruction supply; if it was waiting for data, the shorter instruction stream waits just as long.
- • "The diagram shows one ALU, so one operation happens at a time." Width and out-of-order issue are precisely what the simplified picture omits.
Consequences, controls and cost
- • Code with a large hot footprint can be slow for reasons entirely unrelated to the arithmetic it performs.
- • Two programs executing the same number of instructions can differ in runtime by an order of magnitude depending on whether the back end ever stalls.
- • Adding more arithmetic can be free when the machine is waiting on memory, which is why micro-optimising an inner expression often changes nothing.
- • Measure which half is the constraint before optimising either — front-end and back-end stall counters answer this directly ([[performance-counters]]).
- • Keep hot code small and hot data close; these address the two halves respectively.
- • Expose independent work so the scheduler has something to issue while one chain waits ([[instruction-level-parallelism]]).
- • Accept that most of this is not directly controllable, and that measuring it is the skill rather than steering it.
- • Read front-end versus back-end stall cycles from the performance counters your platform exposes; the split is the first useful measurement on any hot loop.
- • Compare instructions retired against cycles elapsed to get IPC, then interpret it with [[cpi]] rather than in isolation.
- • Profile to find the hot region first — a whole-program counter reading averages over code that does not matter ([[cpu-profiling]]).
- • The four-region model is simple enough to reason with and wrong in every detail that matters for peak performance work.
- • Counter-based diagnosis requires platform-specific tooling and privileges that are often unavailable inside containers and cloud VMs.
- • Reasoning at this level is only worth it once profiling has established that a specific loop matters; applied everywhere it is a waste of engineering time.
Scope
§224 — what these claims are specific to.
- SIMPLIFIEDA four-region teaching model. Real cores add micro-op caches, multiple schedulers, load/store queues and retirement machinery not shown here.
- MICROARCH-SPECIFICUnit counts, issue width, buffer sizes and decode behaviour differ between vendors, between generations from one vendor, and between core types on the same heterogeneous die.
Misconceptions
Where the rest of this lives
Everything the front end fetches was produced by a compiler or JIT, and what it chose to emit determines how much work the front end has to do. That translation step is a domain of its own.