10.0 What this chapter gives you#
- You will be able to name every part inside a processor and say what it does.
- You will be able to walk one instruction from memory to result, step by
step, and say which wire carries what at each step.
- You will be able to read a line of x86-64 assembly and the same line in
ARM64 assembly, and say what the machine does with it.
- You will be able to explain what an instruction set architecture is, and
why it is a contract rather than a design.
- You will be able to explain pipelining with real timing tables, and say
exactly why a very deep pipeline made the Pentium 4 slower, not faster.
- You will be able to explain out-of-order execution, register renaming and
branch prediction without any magic, with real accuracy figures.
- You will be able to explain why processors stopped getting faster in
megahertz around 2005 and started getting wider and more numerous instead.
- You will be able to walk the MESI cache coherence protocol between two
cores by hand, and calculate Amdahl’s law for a real program.
- You will be able to read a desktop processor spec sheet and a phone
system-on-chip spec sheet and decode every single number on the page.
- You will be able to say why gigahertz alone tells you almost nothing, and
what number to look at instead.
10.1 What a CPU is: a box that repeats one loop forever#
PLAIN10.1.1 in simple words#
- A CPU is a small square of silicon that does one thing over and over.
- The one thing is: get the next order, work out what it means, do it.
- Then it does it again. And again. Billions of times each second.
- Nothing else. There is no cleverness hidden anywhere. It is a loop.
- The orders are called instructions. Each is just a number in memory.
- An instruction is tiny. “Add these two numbers.” “Copy this to there.”
“If that number is zero, jump somewhere else.”
- A program is a long list of these tiny orders, one after another.
- Everything you have ever seen a computer do is millions of these orders
running fast enough that the steps blur into one smooth thing.
- Earlier chapters built the pieces: switches, gates, an adder, an arithmetic
unit, and cells that remember a bit. This chapter wires them into a CPU.
PLAIN10.1.2 a picture in your head#
- Picture a very fast, very obedient cook alone in a kitchen.
- On the wall is a long shelf of recipe cards, numbered 1, 2, 3, and so on.
- The cook has a small clip that holds one card at a time. That clip is the
instruction register.
- The cook has a counter on a string round the neck showing which card is
next. That is the program counter.
- The cook has a few small bowls on the bench, always within reach. Those are
the registers.
- There is a big cold store at the far end of the building. That is main
memory. Walking there takes a long time.
- Near the bench there is a small fridge holding whatever was fetched
recently, so the cook does not have to walk. That is the cache.
- The cook does exactly what the card says, moves the counter to the next
number, and picks up the next card. Forever.
Where this comparison breaks: the cook understands the recipe. The CPU does
not understand anything. The bit pattern of the instruction physically opens
and closes switches, and the result falls out. There is no reading and no
deciding, only voltage arriving at gates.
PLAIN10.1.3 a worked example#
- Say memory holds the number 7 at address 64 and the number 5 at address 65.
- We want their sum written to address 66.
- The CPU does this in four orders, and each order is one trip round the loop.
| Step |
The order |
What moves |
| 1 |
Load address 64 into bowl 0 |
7 arrives in bowl 0 |
| 2 |
Load address 65 into bowl 1 |
5 arrives in bowl 1 |
| 3 |
Add bowl 1 into bowl 0 |
bowl 0 becomes 12 |
| 4 |
Store bowl 0 to address 66 |
12 arrives in memory |
- Notice that steps 1, 2 and 4 move data around and do no maths at all.
- Only step 3 does arithmetic. That is normal. Most instructions in a real
program are moving things, not calculating things.
- Notice also that the CPU never added “7 plus 5”. It added whatever happened
to be in bowl 0 and bowl 1. It has no idea what those numbers mean.
PLAIN10.1.4 what is really happening inside#
- Split the CPU into two halves: the part that carries numbers, and the part
that decides what happens to them.
- The carrying part is the datapath: registers, the arithmetic unit, and
the wires between them.
- The deciding part is the control unit. It reads the bit pattern of the
instruction and switches on exactly the right set of wires.
- Think of the control unit as a room full of light switches. An instruction
pattern is a hand that flips a particular set of switches on.
- Those control wires say things like: “let register 1 out onto bus A”,
“tell the arithmetic unit to add, not subtract”, “write the answer back
into register 0”, “increase the counter by one”.
- Everything else is plumbing. Buses are shared bundles of wires that carry
an address, or data, or control signals.
- A memory controller sits at the edge of the chip and talks the exact
electrical language that memory chips expect.
- All of it moves in step with a clock, a square wave that ticks billions of
times a second and says “now everyone move”.
TECHNICAL10.1.5 the engineer’s version#
- The functional blocks of any general-purpose CPU core are as follows.
- Control unit: decodes the instruction and drives the control signals.
In a modern x86 core it is itself a small program in read-only memory,
plus hardwired fast paths. See section 10.6 on microcode.
- ALU (arithmetic logic unit): add, subtract, AND, OR, XOR, shift,
compare. Built from the adders and multiplexers of Chapter 5.
- Register file: a small multi-ported SRAM array. Multi-ported means
several reads and writes can happen in the same clock cycle.
- Program counter (PC), called RIP on x86-64 and PC on ARM64: holds the
address of the next instruction.
- Instruction register (IR): holds the instruction word currently being
decoded. On out-of-order cores this is not one register but a queue.
- Caches: L1 instruction, L1 data, L2 per core, L3 shared. Section 10.11.
- Buses and interconnect: on-die, these are now networks, not buses.
AMD uses Infinity Fabric, Arm uses CMN mesh interconnects.
- Memory controller: generates DDR5 command sequences, refresh, training.
On AMD desktop parts it lives on a separate I/O die, not on the core die.
| Chip |
Year |
Transistors |
Clock |
| Intel 4004 |
1971 |
2,300 |
740 kHz |
| Intel 8086 |
1978 |
29,000 |
5 MHz |
| Intel 80386 |
1985 |
275,000 |
12 MHz |
| Intel Pentium |
1993 |
3.1 million |
60 MHz |
| Core 2 Duo |
2006 |
291 million |
2.93 GHz |
- The Intel 4004 shipped on 15 November 1971. Federico Faggin led the design
with Ted Hoff, Stan Mazor and Masatoshi Shima of Busicom.
- An AMD Zen 4 core complex die holds 6.57 billion transistors in 70 mm2 on
TSMC N5. The Zen 5 equivalent is roughly 8.3 billion in about 70.6 mm2 on
TSMC N4P. Treat the Zen 5 figure as approximate.
- To observe the blocks on Linux:
lscpu, lscpu -C for caches,
cat /proc/cpuinfo. On macOS: sysctl -a | grep machdep.cpu.
WORDS10.1.6 remember these#
- Instruction — one tiny order — an encoded operation in the ISA.
- Datapath — the pipes numbers flow through — registers, ALU, buses.
- Control unit — the switchboard — logic that drives control signals from the
decoded opcode.
- Program counter — a bookmark — register holding the next instruction
address, RIP on x86-64, PC on ARM64.
- Instruction register — the clip holding the current card — latch holding
the instruction word being decoded.
- Register file — the small bowls on the bench — multi-ported SRAM array of
architectural or physical registers.
- Memory controller — the translator at the door — on-die block generating
DDR command and timing sequences.
10.2 The stored-program idea#
PLAIN10.2.1 in simple words#
- Here is the single biggest idea in computing, and it is very simple.
- Instructions are numbers. Data is numbers. Memory holds numbers.
- So instructions and data can live in the same memory, side by side.
- Nothing in memory has a label saying “this one is an order”. It is only an
order because the program counter pointed at it.
- That is why one machine can run any program. You do not rewire it. You
just put different numbers in memory.
- Before this idea, changing a computer’s job meant physically replugging it.
- It also means a program can write numbers that are then run as orders. That
is enormously useful and enormously dangerous, and both are covered below.
PLAIN10.2.2 a picture in your head#
- Picture a huge wall of numbered pigeonholes, all identical.
- Some hold shopping lists. Some hold instructions for the shop assistant.
- Nothing on the outside of a pigeonhole tells you which is which.
- The assistant simply starts at pigeonhole 100 and treats whatever is there
as an instruction, because that is where they were told to start.
- If someone slips a shopping list into pigeonhole 100, the assistant will
try to obey the shopping list as if it were an order.
- That mistake is the root of a whole class of security holes.
Where this comparison breaks: real hardware now does add labels. Modern memory
pages carry permission bits saying “may be executed” or “may be written”, and
in most cases not both. The pigeonholes are still identical, but the shelf now
has rules about which ones the assistant may read orders from.
PLAIN10.2.3 a worked example#
- Take the four bytes
48 83 C0 01 sitting in memory.
- Read as data, that is just four numbers: 72, 131, 192, 1.
- Read as an x86-64 instruction, it is
add rax, 1, meaning “add one to
register RAX”.
- Both readings are correct. The bytes do not choose. The CPU chooses, by
where the program counter points.
- Now take the two bytes
EB FE. As an x86-64 instruction that is
jmp -2, a jump back to itself: an infinite loop in two bytes.
- If a program accidentally jumps into a block of text, the CPU will happily
try to execute the letters.
H is 0x48, which starts many valid x86
instructions. That is why crashes from bad jumps look so random.
PLAIN10.2.4 what is really happening inside#
- There are two ways to arrange memory, and they have names.
- Von Neumann: one memory, holding both instructions and data, reached
through one set of wires.
- Harvard: two separate memories with separate wires, one for
instructions and one for data.
- Von Neumann is flexible. You can load a program as if it were data, then
run it. That is exactly what an operating system does when it starts an app.
- Von Neumann has a bottleneck. One set of wires must carry both the next
instruction and the data it needs, so they take turns.
- Harvard has no such traffic jam, because the two roads are separate.
- Real CPUs use a mixture called modified Harvard. Deep down there is one
memory, von Neumann style. But right next to the core there are two
separate small caches: one for instructions, one for data.
- So the core sees Harvard, with two roads, and the system sees von Neumann,
with one memory. You get the speed of one and the flexibility of the other.
TECHNICAL10.2.5 the engineer’s version#
- John von Neumann’s “First Draft of a Report on the EDVAC” circulated on
30 June 1945 and described a single addressable store for code and data.
The design work was joint with J. Presper Eckert and John Mauchly, whose
names were left off the draft, a dispute that is still argued about.
- The Harvard Mark I, completed in 1944 under Howard Aiken at Harvard, read
instructions from punched paper tape and data from separate counters. That
physical separation is where the name comes from.
- Modified Harvard in practice: separate L1 instruction and L1 data caches
fed from a unified L2. On AMD Zen 5 that is 32 KB L1i and 48 KB L1d per
core, backed by a unified 1 MB L2.
- This split means self-modifying code needs explicit help. Writing bytes
goes into L1d. The fetcher reads L1i. The two can disagree.
- On x86-64 the hardware keeps them coherent for you, but a serializing
instruction is still required before the new bytes are guaranteed run.
- On ARM64 you must do it by hand:
DC CVAU to clean the data cache to
the point of unification, IC IVAU to invalidate instruction cache,
then DSB and ISB barriers. Skipping this is a classic JIT bug.
- The danger side. A stack buffer overflow writes past the end of an array
and overwrites the saved return address, so the CPU returns into attacker
bytes. The Morris worm of 2 November 1988 used exactly this against the
Unix
fingerd service.
- The defence is the no-execute page permission bit. AMD shipped NX with
Athlon 64 in 2003, Intel called it XD, and Microsoft enabled it as Data
Execution Prevention in Windows XP Service Pack 2 in August 2004.
- Attackers answered with return-oriented programming: chain together
fragments of code that already exists and is already marked executable.
No new code is written, so NX does not stop it.
- The useful side. A just-in-time compiler writes machine code into a data
buffer at run time, flips the page permission from writable to executable,
and jumps into it. The JavaScript engines in every browser do this
thousands of times while a page loads.
| Term |
Code and data memory |
Seen in |
| Von Neumann |
One shared |
Program model of a PC |
| Harvard |
Two separate |
Small microcontrollers |
| Modified Harvard |
Split caches, one store |
Every mainstream CPU |
- Tools:
readelf -l shows segment permissions, R E for read-execute and
RW for read-write. On Linux cat /proc/self/maps shows the same live.
WORDS10.2.6 remember these#
- Stored program — orders live in memory like any other number — code and
data share one address space.
- Von Neumann architecture — one memory for everything — unified instruction
and data store, with a shared bus bottleneck.
- Harvard architecture — two separate memories — separate instruction and
data address spaces and buses.
- Modified Harvard — one memory but two front doors — split L1i and L1d over
a unified lower hierarchy.
- Buffer overflow — writing past the end of a box — overwriting adjacent
stack or heap state, classically the saved return address.
- NX bit — a page marked “not runnable” — no-execute permission bit in the
page table entry, XD on Intel.
- JIT — writing new code while running — just-in-time compilation into a
writable page later marked executable.
10.3 The fetch-decode-execute cycle#
PLAIN10.3.1 in simple words#
- The loop the CPU repeats forever has three parts and standard names.
- Fetch: read the instruction at the address in the program counter.
- Decode: work out what it is and where its inputs are.
- Execute: do it, and write the answer somewhere.
- Then increase the program counter and start again.
- Some instructions add a fourth part, a memory access, and a fifth, writing
the result back into a register. We will use the five-part version later.
- If the instruction was a jump, the program counter is not increased by one.
It is loaded with a new address. That is how loops and decisions work.
- That is the entire behaviour of a processor. Everything after this in the
chapter is a trick to run this loop faster.
PLAIN10.3.2 a picture in your head#
- Picture a postal sorting clerk at a desk.
- The clerk reads the number on a slip of paper: “go to shelf 100”.
- They walk to shelf 100 and bring back the envelope. That is fetch.
- They open it and read what kind of job it is. That is decode.
- They do the job at their desk. That is execute.
- They cross out 100 and write 101 on the slip, then start again.
- If the envelope said “next, go to shelf 240”, they write 240 instead.
Where this comparison breaks: a real CPU does not finish one envelope before
starting the next. It has several envelopes open at once, in different stages,
and it often guesses which shelf comes next before it knows. Those are
pipelining and branch prediction, sections 10.7 and 10.8.
PLAIN10.3.3 a worked example#
- Let us invent a small machine so nothing is hidden. Call it KB-1.
- It has four registers R0 to R3, 16-bit words, and 16-bit instructions.
- An instruction splits into fields like this.
bit 15..12 11..10 9..8 7..0
+--------+--------+--------+----------+
| opcode | rd | rs | imm/addr |
+--------+--------+--------+----------+
- Five opcodes are enough for our program.
| Opcode |
Name |
Meaning |
| 0001 |
LOAD rd,[a] |
rd <- MEM[a] |
| 0010 |
STORE rd,[a] |
MEM[a] <- rd |
| 0011 |
ADD rd,rs |
rd <- rd + rs |
| 0100 |
SUB rd,rs |
rd <- rd - rs |
| 0111 |
JZ addr |
jump if last result 0 |
- Memory starts with 7 at address 0x40, 5 at 0x41, and 0 at 0x42.
- The program, with its exact machine words, is this.
addr word assembly
0x00 0x1040 LOAD R0, [0x40]
0x01 0x1441 LOAD R1, [0x41]
0x02 0x3100 ADD R0, R1
0x03 0x2042 STORE R0, [0x42]
- Check the encoding of 0x1441 by hand. In binary it is
0001 01 00 01000001. Opcode 0001 is LOAD, rd is 01 meaning R1, the low
eight bits are 0x41. So: load from address 0x41 into R1.
- Now run it and watch every value change.
| After |
PC |
R0, R1 |
Mem[0x42] |
| start |
0x00 |
0, 0 |
0 |
| instr 1 |
0x01 |
7, 0 |
0 |
| instr 2 |
0x02 |
7, 5 |
0 |
| instr 3 |
0x03 |
12, 5 |
0 |
| instr 4 |
0x04 |
12, 5 |
12 |
- Three kinds of instruction did all the work: a load, an arithmetic
operation, and a store. Almost all real code is these three kinds.
PLAIN10.3.4 what is really happening inside#
- Now walk one cycle at the level of the wires. Take instruction 3, ADD.
- Tick 1, fetch. The program counter holds 0x02. Its value is driven onto the
address bus. The read line is raised.
- Memory answers with the 16 bits 0x3100 on the data bus.
- The write-enable line of the instruction register is raised, so on the next
clock edge the instruction register latches 0x3100.
- Tick 2, decode. The top four bits, 0011, go into the control unit. The
control unit is a lookup that turns 0011 into a set of raised wires.
- Bits 11 to 10 pick R0 as the destination and as source A. Bits 9 to 8 pick
R1 as source B. Those go to the register file’s address inputs.
- The register file drives 7 onto bus A and 5 onto bus B. No clock needed:
reading is combinational, the values just appear after a small delay.
- Tick 3, execute. The control unit puts the code for “add” on the ALU’s
function input. The adder settles and 12 appears at its output.
- The write-back line for R0 is raised. On the next clock edge, R0 latches 12.
- In parallel, the program counter’s own adder computes 0x02 plus 1, and the
program counter latches 0x03 on the same edge.
- Every one of these steps is switches opening and closing, as in Chapter 4.
Nothing is interpreted. The bit pattern is the command.
PC --> [ address bus ] --> MEMORY
|
[ data bus ]
v
INSTRUCTION REG
|
+----------+----------+
| |
CONTROL UNIT REGISTER FILE
| | |
control wires busA busB
| | |
+--------------> [ ALU ]
|
write back
TECHNICAL10.3.5 the engineer’s version#
- The same program in real x86-64, with RDI holding the base address.
8B 07 mov eax, dword ptr [rdi]
03 47 04 add eax, dword ptr [rdi+4]
89 47 08 mov dword ptr [rdi+8], eax
- Note the byte counts: two, three, three. x86-64 instructions are variable
length, from 1 to 15 bytes, with 15 a hard architectural limit.
- Note also that x86-64 can add straight from memory. One instruction does a
load and an add. That is the classic CISC habit.
- The same program in ARM64, with X0 holding the base address.
B9400001 ldr w1, [x0]
B9400402 ldr w2, [x0, #4]
0B020021 add w1, w1, w2
B9000801 str w1, [x0, #8]
- Every ARM64 instruction is exactly four bytes. Always. That is the classic
RISC habit, and it makes the decoder far simpler.
- ARM64 cannot add from memory. Loads and stores are the only instructions
that touch memory. This is called a load-store architecture.
- Decode 0xB9400402 by hand. Bits 31 to 22 are 1011100101, the 32-bit LDR
unsigned-offset form. Bits 21 to 10 are the scaled immediate, value 1, and
the scale for a 32-bit load is 4, so the offset is 4 bytes. Bits 9 to 5 are
Rn equals X0. Bits 4 to 0 are Rt equals W2.
- On a modern core, none of this happens in three tidy ticks. The x86-64
add eax, [rdi+4] is split into two internal micro-operations, an address
generation plus load, and an add. Section 10.6.
- To watch real instructions:
objdump -d ./a.out disassembles a binary,
gdb with layout asm and stepi single-steps them, and
perf stat ./a.out counts how many actually retired.
WORDS10.3.6 remember these#
- Fetch — go and get the next order — read memory at the PC into the
instruction register.
- Decode — work out what it says — map opcode and operand fields onto
control signals and register file ports.
- Execute — do it — drive the ALU or address unit and produce a result.
- Write-back — put the answer away — latch the result into the destination
register on the clock edge.
- Load-store architecture — only two instructions touch memory — arithmetic
operates on registers only, as in ARM64, RISC-V and MIPS.
- Combinational — settles by itself, no clock — logic whose output depends
only on current inputs after a propagation delay.
10.4 Instruction set architecture#
PLAIN10.4.1 in simple words#
- An instruction set architecture, or ISA, is the list of orders a CPU
understands, and exactly how each one is written as a number.
- It also says how many registers there are, what they are called, how big a
number is, and what happens when something goes wrong.
- It is a promise, not a design. It says what the chip must appear to do. It
says nothing about how the chip does it inside.
- That promise is why a program compiled in 2003 still runs today. The inside
of the chip changed completely. The promise did not.
- Two chips can keep the same promise with wildly different insides. An
Intel and an AMD desktop chip share almost nothing internally, yet run the
same programs, because they honour the same ISA.
- The three ISAs that matter today are x86-64, ARM64 and RISC-V.
PLAIN10.4.2 a picture in your head#
- Think of an ISA as the menu in a restaurant, not the kitchen.
- The menu says: item 14 is soup, item 22 is rice, item 30 is bread.
- Any kitchen that produces the right dish for each number is a valid
kitchen. One may use a wood fire, another an induction hob.
- A customer who learned the menu twenty years ago can still order item 14
and get soup, even though the kitchen was rebuilt three times.
- If a new kitchen decided item 14 now means salad, every old customer breaks.
That is why ISAs almost never remove things.
- That is also why x86-64 still carries instructions from 1978.
Where this comparison breaks: menus are short, and ISAs are not. A full
x86-64 manual set from Intel runs to several thousand pages across its
volumes, and nobody knows all of it. Also, some menu items are far slower to
cook than others, and the menu does not tell you which.
PLAIN10.4.3 a worked example#
- Every instruction is a number split into fields. Take RISC-V, which is the
tidiest example, with a fixed 32-bit length.
- The R-type format, used for register-to-register arithmetic, is this.
bits 31..25 24..20 19..15 14..12 11..7 6..0
+---------+------+------+------+------+-------+
| funct7 | rs2 | rs1 |funct3| rd |opcode |
+---------+------+------+------+------+-------+
- For
add x5, x6, x7: opcode 0110011, funct3 000, funct7 0000000, rs1 is
x6, rs2 is x7, rd is x5. The whole word comes out as 0x007302B3.
- Change funct7 to 0100000 and the same opcode becomes
sub. One bit flip
in a field turns add into subtract. That is what a field is for.
- An immediate is a constant baked into the instruction itself, rather
than read from a register.
addi x5, x6, 100 carries the 100 in 12 bits.
- Now addressing modes: the different ways an instruction names where its
data is. Here they are with worked values, using x86-64 notation.
| Mode |
Example |
Where the data is |
| Immediate |
mov rax, 42 |
In the instruction |
| Register |
mov rax, rbx |
In RBX |
| Absolute |
mov rax, [0x600100] |
At that fixed address |
| Reg indirect |
mov rax, [rbx] |
At the address in RBX |
| Base+disp |
mov rax, [rbx+8] |
At RBX plus 8 |
| Indexed |
mov rax, [rbx+rcx*4] |
At RBX plus 4 times RCX |
| PC-relative |
lea rax, [rip+0x20] |
0x20 past the next instr |
- Work one through. If RBX holds 0x1000 and RCX holds 3, then
mov rax, [rbx+rcx*4] reads the 8 bytes at 0x1000 plus 12, which is
0x100C. That single instruction indexes an array of 4-byte elements.
- That scale factor of 1, 2, 4 or 8 exists precisely because arrays of
bytes, shorts, ints and longs are so common.
PLAIN10.4.4 what is really happening inside#
- The decoder is a big lookup. It takes the instruction bits and produces
control signals, plus register numbers, plus a constant.
- With fixed-length instructions, the decoder knows where every field is
before it has read anything. It can slice all fields in parallel.
- With variable-length instructions it cannot. It must work out how long the
first instruction is before it knows where the second begins.
- On x86-64 the length depends on prefixes, on the opcode, on the ModRM byte,
on the SIB byte and on the displacement size. Length decoding is a serial
chain, and it is genuinely hard.
- Intel and AMD solve it by brute force: try to decode at many byte offsets
at once and throw away the wrong answers, plus cache the decoded results so
the work is not repeated. Section 10.6.
- That is a real, permanent cost of x86-64. It costs transistors and power
that an ARM64 decoder simply does not spend.
- It is also, in 2026, a survivable cost. It is a fixed overhead at the front
of a core that is otherwise dominated by caches and execution units.
TECHNICAL10.4.5 the engineer’s version#
- The RISC idea began at IBM with John Cocke’s 801 project, which ran from
1975 onward and was not published widely at the time.
- David Patterson’s group at Berkeley built RISC-I in 1981 and coined the
term. John Hennessy’s group at Stanford built MIPS from 1981.
- Hennessy and Patterson shared the 2017 ACM Turing Award for this work.
- The original argument: compilers do not use complex instructions, so spend
the transistors on registers, pipelines and caches instead.
- What is the real difference today. Honest answer: at the level of the
execution core, almost none. Both x86-64 and ARM64 cores decode into
internal micro-operations, rename registers, execute out of order and
retire in order. The differences that survive are these.
- Decode complexity. Fixed 32-bit versus 1 to 15 bytes.
- Memory operands in arithmetic instructions, allowed on x86-64, not on
ARM64.
- Memory ordering model, strong on x86-64, weak on ARM64. Section 10.10.
- Legacy baggage: x86-64 must still support 16-bit real mode at power-on.
- Where experts disagree: some argue variable-length decode caps how wide an
x86 front end can practically get, others point to Intel and AMD shipping
8-wide decode in 2024 and say the ceiling keeps moving. Both sides have a
point and the argument is not settled.
| Feature |
x86-64 |
ARM64 |
RISC-V |
| Year |
2000 spec |
2011 |
2010 start |
| Designer |
AMD |
Arm Ltd |
UC Berkeley |
| Licence |
Cross-licence |
Paid licence |
Open, BSD terms |
| Registers |
16 integer |
31 integer |
32 integer |
| Instr length |
1 to 15 bytes |
4 bytes fixed |
4 or 2 bytes |
| Typical use |
PC, server |
Phone, Mac |
Embedded, chips |
- Two footnotes to that table. RISC-V’s embedded variant has 16 registers
rather than 32, and its 2-byte instruction length comes from the optional
C compressed extension, not the base set.
- Dates to keep straight. AMD published the x86-64 specification in 2000 and
shipped Opteron in April 2003. Intel shipped its compatible EM64T in the
Nocona Xeon in June 2004. Arm announced ARMv8-A with AArch64 in
October 2011. RISC-V began at Berkeley in 2010 under Krste Asanovic and
David Patterson, its unprivileged ISA was ratified as version 20191213, and
RISC-V International moved to Switzerland, with the rename completed in
March 2020.
- Tools:
gcc -march=native -Q --help=target lists the ISA extensions the
compiler will use, and on Linux the flags line in /proc/cpuinfo lists
what the chip actually supports.
WORDS10.4.6 remember these#
- ISA — the list of orders and how they are written — architectural contract
defining instructions, registers, memory model and exceptions.
- Opcode — the part that says what to do — the operation field of an
instruction word.
- Operand — the part that says what to do it to — register number, memory
address or constant.
- Immediate — a number written into the order itself — constant encoded in
the instruction, not fetched.
- Addressing mode — the recipe for finding the data — the rule turning
instruction fields into an effective address.
- RISC — few simple orders, all the same size — reduced instruction set
computer, load-store, fixed length.
- CISC — many orders, many sizes — complex instruction set computer with
memory operands and variable length.
- Microarchitecture — how one chip actually implements the promise — the
internal design, invisible to software.
10.5 Registers in real CPUs#
PLAIN10.5.1 in simple words#
- A register is a tiny box inside the CPU that holds one number.
- There are very few of them. A modern CPU has a few dozen that programs can
name, not thousands.
- They are the only storage the arithmetic unit can reach in the same tick of
the clock. Everything else is further away.
- Some registers are general: you can put anything in them.
- Some have a fixed job. One always points at the next instruction. One
always points at the top of the stack. One holds yes or no answers about
the last calculation.
- Almost all real work happens in registers. Data is pulled in from memory,
chewed on in registers, and pushed back out.
- A good compiler spends most of its effort deciding which values live in
registers and which have to spill out to memory.
PLAIN10.5.2 a picture in your head#
- Think of a carpenter at a bench.
- The registers are the two or three tools actually in their hands.
- The L1 cache is the tool tray at the edge of the bench, an arm’s reach away.
- Main memory is the tool cupboard on the other side of the workshop.
- Storage is the hardware shop down the road.
- Every step out costs time. Hands are instant. The tray takes a second. The
cupboard takes a minute. The shop takes a day.
- So the carpenter arranges work to keep what is needed in their hands.
That is exactly what a compiler’s register allocator does.
Where this comparison breaks: hands hold whole tools, and registers hold only
fixed-size numbers, typically 64 bits. Also a carpenter can put a tool down
anywhere, while a register value must be explicitly written somewhere or it is
simply overwritten and lost.
PLAIN10.5.3 a worked example#
- On x86-64 the general purpose registers have odd names, because they grew
from 1978 by accident rather than by plan.
- The 8086 of 1978 had eight 16-bit registers, and each name meant something.
| Name |
Stood for |
Old special job |
| AX |
Accumulator |
Results of maths |
| BX |
Base |
A base address |
| CX |
Counter |
Loop counts |
| DX |
Data |
Second half of results |
| SI |
Source index |
Source of a copy |
| DI |
Destination index |
Target of a copy |
| BP |
Base pointer |
Bottom of a stack frame |
| SP |
Stack pointer |
Top of the stack |
- In 1985 the 80386 widened them to 32 bits and put an E in front: EAX, EBX,
and so on. E stands for extended.
- In 2003 x86-64 widened them to 64 bits and put an R in front: RAX, RBX,
and so on. It also added eight plain-numbered ones, R8 to R15.
- So RAX, EAX, AX, AH and AL are all the same physical register, seen through
windows of 64, 32, 16, 8 and 8 bits.
- ARM64 refused all of that. It has 31 registers with no story attached:
X0 to X30 for the 64-bit view, W0 to W30 for the low 32 bits.
- There is no X31. That slot means either the constant zero or the stack
pointer, depending on the instruction. Reading the zero register always
gives 0, and writing to it throws the value away.
PLAIN10.5.4 what is really happening inside#
- Why are registers so fast. Four reasons, all physical.
- They are physically next to the arithmetic unit, often microns away. Signal
travel time is close to nothing.
- They are addressed by a tiny number, three to five bits. That decode is a
few gates, not a lookup in a table of tags like a cache.
- They are built from fast, wide SRAM cells with many ports, so several
registers can be read and written in the same cycle.
- There are very few of them, so the array is small, so its wires are short,
so it settles quickly.
- The cost of all that is area and power per bit. A register file bit costs
far more silicon than a cache bit, which is why you cannot have thousands.
- There is one more thing hiding here. The names in the program are not the
real boxes. A modern CPU has hundreds of real registers and shuffles which
physical box currently holds RAX. That is register renaming, section 10.8.
TECHNICAL10.5.5 the engineer’s version#
- x86-64 architectural integer state: 16 general purpose registers of 64
bits, RIP as the instruction pointer, RFLAGS as the status register.
- Writing a 32-bit sub-register zero-extends into the full 64 bits. Writing
an 8-bit or 16-bit sub-register does not, and leaves the top bits alone.
That asymmetry is a standard, written in the architecture manual, and it
causes real partial-register stalls if you get it wrong.
- RFLAGS bits worth knowing: CF is bit 0 carry, PF bit 2 parity, ZF bit 6
zero, SF bit 7 sign, IF bit 9 interrupt enable, DF bit 10 direction,
OF bit 11 overflow.
- ARM64 architectural integer state: X0 to X30, plus SP, plus PC which is not
a general register and cannot be written directly. Condition flags live in
PSTATE as N, Z, C, V.
- By convention, not by hardware rule, ARM64 uses X30 as the link register
holding the return address, and X29 as the frame pointer.
- Calling conventions are conventions, set by an ABI document, not by the
chip. The System V ABI used on Linux and macOS passes integer arguments in
RDI, RSI, RDX, RCX, R8, R9 and returns in RAX. Microsoft’s x64 ABI uses
RCX, RDX, R8, R9. ARM64’s AAPCS64 passes in X0 to X7 and returns in X0.
- Physically, the architectural names map onto a much larger physical
register file. AMD Zen 5 has a floating point physical register file of
384 entries, doubled from 192 in Zen 4, and an integer file of a couple of
hundred entries.
| Storage |
Typical latency |
Typical size |
| Register |
Under 1 cycle |
16 x 64 bits |
| L1 data cache |
4 to 5 cycles |
48 KB |
| L2 cache |
14 cycles |
1 MB |
| L3 cache |
40 to 50 cycles |
32 MB per die |
| DDR5 main memory |
70 to 90 ns |
32 GB |
- Those latency figures are for AMD Zen 5 on the Ryzen 9000 series, 2024.
At 5.7 GHz one cycle is 0.175 nanoseconds, so an L1 hit is about 0.7 ns
and a memory access is over 400 times slower.
- Tools:
gdb command info registers prints them live. On Linux,
perf stat -e ld_blocks.no_sr and similar counters expose partial
register and store-forwarding stalls on Intel parts.
WORDS10.5.6 remember these#
- Register — a tiny box for one number inside the CPU — architecturally named
fast storage, typically 64 bits wide.
- General purpose register — one you can use for anything — an integer
register with no fixed hardware role.
- Stack pointer — marks the top of the working pile — RSP on x86-64, SP on
ARM64.
- Flags register — a strip of yes or no answers — RFLAGS on x86-64, the NZCV
bits of PSTATE on ARM64.
- Zero register — a tap that always gives zero — XZR and WZR on ARM64, no
equivalent on x86-64.
- Spill — running out of hands and putting something down — the compiler
storing a value to the stack because registers ran out.
- ABI — the agreement about who passes what where — application binary
interface, a convention, not part of the ISA.
10.6 Microcode#
PLAIN10.6.1 in simple words#
- Some instructions are simple, and the hardware can do them directly.
- Some are not. “Copy this whole block of memory to there” is one order for
the programmer but hundreds of steps for the hardware.
- So inside the CPU there is a second, smaller, more private instruction set.
- A complicated outside instruction is turned into a short program of these
small private steps. Those steps are called micro-operations.
- The little program that produces them is the microcode.
- This is why one x86 instruction can take one clock cycle or one hundred.
Some are a single step, and some are a whole routine.
- The important consequence is this: some of the behaviour of a CPU is
stored, not wired. Stored things can be changed later.
- That is how a firmware update can change how a chip behaves without anyone
touching the silicon.
PLAIN10.6.2 a picture in your head#
- Picture a restaurant kitchen again. The menu is the instruction set.
- Some menu items are one action: “pour a glass of water”.
- Some are not: “make a three-course meal for six”. The chef has a card file
of standard sub-recipes and works through them in order.
- The card file is the microcode. It sits in the kitchen, not on the menu.
- Customers never see the cards. They only see the menu, which never changes.
- If a sub-recipe turns out to be wrong, the owner can replace those cards
overnight. The menu still says the same words the next morning.
Where this comparison breaks: the chef can improvise, and the microcode
sequencer cannot. Also, most modern instructions skip the card file entirely
and are handled by hardwired fast decoders, because going through microcode
is slow. Microcode is the exception path, not the normal path.
PLAIN10.6.3 a worked example#
- Take the x86-64 instruction
add [rax], rbx, which means “add RBX to the
number in memory at the address in RAX”.
- To a programmer that is one instruction, four bytes.
- Inside, it becomes roughly three micro-operations.
1. load t0 <- MEM[rax] (read 8 bytes)
2. add t0 <- t0 + rbx (do the arithmetic)
3. store MEM[rax] <- t0 (write 8 bytes back)
t0 is a temporary that has no name in the instruction set. Programs
cannot see it. It exists only for the duration of the instruction.
- Now take
rep movsb, which copies a whole block of bytes. That is a
microcoded loop. It may issue thousands of micro-operations from one
instruction, and the exact number depends on the block length.
- Compare with ARM64. The same job needs three separate instructions written
out by the compiler, one load, one add, one store. The work is identical.
The difference is who wrote the three steps down: the microcode, or the
compiler.
PLAIN10.6.4 what is really happening inside#
- The front end of an x86 core has two paths.
- The fast path: several simple hardware decoders, each turning one
instruction into one to four micro-operations directly.
- The slow path: the microcode sequencer, a small read-only memory holding
canned sequences, used for anything complicated.
- Decoding x86 is expensive, so cores keep a micro-op cache. Once an
instruction has been decoded, the resulting micro-operations are stored.
- The next time round a loop, the front end reads finished micro-operations
straight out of that cache and skips decoding entirely.
- That saves a lot of power, and it also delivers more operations per cycle
than the decoders can.
- A microcode update is a signed blob of data. Firmware loads it into a small
patch memory very early in boot. It is not stored on the chip permanently.
- It disappears at every power-off and must be loaded again next boot, by the
motherboard firmware or by the operating system.
TECHNICAL10.6.5 the engineer’s version#
- Maurice Wilkes proposed microprogramming in 1951, at the Manchester
University Computer Inaugural Conference. EDSAC 2, running from 1958, was
the first microprogrammed machine.
- IBM made it famous with System/360 in 1964: one ISA implemented by many
physically different machines, each with its own microcode. That is the
clearest early proof that an ISA is a contract, not a design.
- On Intel cores, simple decoders handle one-to-one and one-to-four cases.
Anything longer goes to the MSROM, the microcode sequencer ROM.
- Micro-op cache sizes, all implementation details rather than architecture.
| Core |
Year |
Micro-op cache |
| Sandy Bridge |
2011 |
1,536 micro-ops |
| Skylake |
2015 |
1,536 micro-ops |
| Golden Cove |
2021 |
About 4,000 micro-ops |
| AMD Zen 4 |
2022 |
About 6,750 ops |
| AMD Zen 5 |
2024 |
12 ops per cycle out |
- Micro-op fusion and macro-op fusion muddy instruction counting. A compare
followed by a branch is commonly fused into one internal operation, so
counting retired instructions is not the same as counting work.
- Microcode update mechanics. On Intel, software writes the physical address
of the update to model-specific register 0x79 and the load happens. On AMD
the equivalent is MSR 0xC0010020. Updates are encrypted and signed. A CPU
will reject an unsigned patch.
- Limits, and this matters. Microcode cannot add execution units, cannot
change cache sizes, and cannot rewrite the hardwired fast decoders. It
patches sequences and it can flip configuration bits. The number of patch
slots is finite.
- Spectre and Meltdown were disclosed on 3 January 2018.
CVE-2017-5753 is Spectre variant 1, bounds check bypass. CVE-2017-5715 is
Spectre variant 2, branch target injection. CVE-2017-5754 is Meltdown.
- Intel’s microcode updates added entirely new architectural controls:
IA32_SPEC_CTRL exposing IBRS and STIBP, and IA32_PRED_CMD exposing IBPB.
A firmware update added new registers to a shipped ISA. That is unusual.
- IBRS restricts indirect branch prediction across privilege levels.
- IBPB flushes predictor state at a chosen barrier point.
- STIBP stops one hardware thread steering the other thread’s predictor.
- Google published retpoline on 4 January 2018, a purely software fix for
variant 2 that replaces indirect jumps with a return trampoline the
predictor cannot usefully steer.
- Meltdown was not fixed by microcode at all. It was fixed in software by
kernel page-table isolation, which unmaps kernel memory while user code
runs, based on the 2017 KAISER work from TU Graz.
- The rollout went badly. Intel’s January 2018 microcode caused unexpected
reboots, and Microsoft shipped an update on 29 January 2018 that disabled
it. Measured performance drops of 2 to 14 percent were reported on
eighth-generation Core platforms, depending heavily on workload.
- Observe it:
grep microcode /proc/cpuinfo on Linux gives the loaded
revision, dmesg | grep -i microcode shows the load at boot, and
iucode_tool -l lists available Intel updates.
The honest version: saying “the CPU runs microcode” suggests every instruction
goes through an interpreter. It does not. On a modern x86 core the large
majority of instructions never touch the microcode sequencer, and on ARM64
cores the sequencer is far smaller still. Microcode is the escape hatch for
complicated and rare cases, plus a patching mechanism.
WORDS10.6.6 remember these#
- Micro-operation — one small private step — the internal RISC-like operation
an instruction is broken into, often written uop.
- Microcode — the card file of stored sub-recipes — ROM sequences that expand
complex instructions into micro-operations.
- Micro-op cache — remembering the decode so you need not redo it — a cache
of decoded micro-operations, also called the op cache or DSB.
- Microcode update — an overnight replacement of some cards — a signed,
volatile patch loaded by firmware at every boot.
- IBRS, IBPB, STIBP — three new switches added by a patch — speculation
controls introduced by 2018 microcode for Spectre variant 2.
- KPTI — hiding the kernel’s map from user code — kernel page-table
isolation, the software fix for Meltdown.
10.7 Pipelining#
PLAIN10.7.1 in simple words#
- A simple CPU wastes almost all of itself, almost all of the time.
- While it is fetching an instruction, the arithmetic unit sits idle. While
it is doing arithmetic, the fetch hardware sits idle.
- Pipelining fixes that. Break the work into stages, and keep every stage
busy on a different instruction at the same time.
- It does not make any one instruction faster. Each instruction still takes
the same number of stages to get through.
- It makes instructions come out more often. That is throughput, not latency.
- If the pipeline has five stages and nothing goes wrong, you finish one
instruction every clock cycle instead of one every five.
- Every CPU made since the 1980s is pipelined. It is not optional.
PLAIN10.7.2 a picture in your head#
- You have four loads of washing. Each load needs washing, drying, folding.
- Each of the three jobs takes 30 minutes.
- The naive way: wash, dry and fold load 1 completely, then start load 2.
That is 90 minutes each, 360 minutes in total.
- The pipelined way: as soon as load 1 leaves the washer, load 2 goes in.
- Now the timeline looks like this, in 30-minute slots.
slot 1 2 3 4 5 6
wash L1 L2 L3 L4
dry L1 L2 L3 L4
fold L1 L2 L3 L4
- Total: 6 slots, 180 minutes, not 360. Twice as fast for four loads.
- With 100 loads it approaches three times as fast, the number of stages.
- Notice load 1 still took 90 minutes. Nothing got faster. Things just
overlapped.
Where this comparison breaks: laundry loads are independent, and instructions
are not. Load 2 might need the result of load 1. Worse, sometimes you do not
know which load comes next until load 1 finishes. Those two problems are data
hazards and control hazards, and they are the whole difficulty.
PLAIN10.7.3 a worked example#
- The classic teaching pipeline has five stages, from the Berkeley and
Stanford RISC designs of the early 1980s.
| Stage |
Short name |
What it does |
| 1 |
IF |
Fetch instruction |
| 2 |
ID |
Decode, read registers |
| 3 |
EX |
Do arithmetic or address |
| 4 |
MEM |
Access data memory |
| 5 |
WB |
Write result to register |
- Now run five instructions and watch the pipeline fill and drain.
cycle 1 2 3 4 5 6 7 8 9
instr 1 IF ID EX MEM WB
instr 2 IF ID EX MEM WB
instr 3 IF ID EX MEM WB
instr 4 IF ID EX MEM WB
instr 5 IF ID EX MEM WB
- Five instructions finish in 9 cycles, not 25. The first takes 5 cycles to
appear, then one appears every cycle.
- The formula for n instructions in a k-stage pipeline is k plus n minus 1
cycles, against n times k without pipelining.
- For n equals 5 and k equals 5: 9 cycles against 25. Speedup 2.78.
- For n equals 1,000 and k equals 5: 1,004 cycles against 5,000. Speedup
4.98, which is almost the full 5.
- There is a second, bigger win. Each stage now does one fifth of the work,
so each stage settles in one fifth of the time, so the clock can run
roughly five times faster too. Pipelining is why clocks got fast at all.
PLAIN10.7.4 what is really happening inside#
- Between every pair of stages there is a row of flip-flops, from Chapter 6,
holding that stage’s result until the next clock edge. These are pipeline
registers, and they are the physical dividers.
- Three things can go wrong. They are called hazards.
- Structural hazard: two instructions want the same piece of hardware in
the same cycle. For example, one instruction fetching while another reads
data, when there is only one memory port. Fixed by splitting instruction
and data caches, which is the modified Harvard idea from section 10.2.
- Data hazard: an instruction needs a result that is not written yet.
add r1, r2, r3 ; result of r1 written in stage WB
sub r4, r1, r5 ; needs r1 in stage EX, two cycles earlier
- The fix is forwarding, also called bypassing. Extra wires take the
answer straight from the output of the arithmetic unit back into its own
input, before it has been written anywhere.
- Forwarding cures most data hazards but not all. If the producer is a load
from memory, the value does not exist until the MEM stage, so the consumer
must wait one cycle. That wasted cycle is a stall, and the empty slot
pushed through the pipeline is a bubble.
- Control hazard: a branch. Until the branch is resolved, the fetch unit
does not know which instruction comes next.
- In a five-stage pipeline you lose two or three cycles per branch if you
just wait. Since roughly one instruction in five or six is a branch, that
is a large loss, and it is why branch prediction exists.
TECHNICAL10.7.5 the engineer’s version#
- The IBM 7030, known as Stretch and delivered in 1961, was the first
heavily pipelined machine, and it already fetched past unresolved branches.
- Pipeline depth by generation, all measured in stages of the integer
pipeline, which is the usual quoted figure.
| Core |
Year |
Stages |
| Classic MIPS R2000 |
1985 |
5 |
| Intel Pentium |
1993 |
5 |
| Pentium Pro |
1995 |
12 to 14 |
| Pentium 4 Willamette |
2000 |
20 |
| Pentium 4 Prescott |
2004 |
31 |
| Intel Core 2 |
2006 |
14 |
- The cautionary tale is Intel NetBurst. Willamette shipped in November 2000
with a 20-stage pipeline. Prescott followed in February 2004 with 31
stages. The design goal was very high clock speed, and Intel spoke publicly
about reaching 10 GHz.
- It did not work, for three reasons that all reinforce each other.
- Deeper pipelines make every branch misprediction more expensive,
because more work must be thrown away. Prescott’s penalty was well over
30 cycles.
- Every extra stage adds pipeline registers, and those flip-flops burn
power and add setup and hold overhead that eats the timing gain.
- Leakage current at 90 nm rose faster than expected, so the promised
clock speeds never arrived. Intel stopped at 3.8 GHz in November 2004.
- Intel cancelled the successors, Tejas and Jayhawk, in 2004. They were
reported to target between 40 and 50 stages. Top NetBurst parts had a
thermal design power of 115 W, which was extreme for the time.
- Intel abandoned NetBurst and shipped the Core microarchitecture in July
2006, which went back to a 14-stage pipeline at lower clocks and was
substantially faster in real work.
- The lesson, stated properly: pipeline depth trades clock frequency against
misprediction cost and power. There is an optimum, and it is well short of
30 stages for general-purpose code. Modern cores sit at roughly 14 to 20
stages from fetch to retire.
- Measurement:
perf stat -e cycles,instructions,stalled-cycles-frontend, stalled-cycles-backend on Linux separates front-end stalls, which are
usually fetch and branch problems, from back-end stalls, which are usually
memory and dependency problems.
WORDS10.7.6 remember these#
- Pipelining — overlapping the stages of different instructions — splitting
the datapath into stages separated by pipeline registers.
- Throughput — how often results come out — instructions completed per unit
time.
- Latency — how long one thing takes end to end — cycles from issue to
completion for a single instruction.
- Hazard — a reason the overlap cannot happen — structural, data or control
conflict preventing the next stage from proceeding.
- Forwarding — passing the answer straight back — bypass paths routing an
ALU result to a dependent instruction before write-back.
- Stall — waiting — holding earlier stages while a dependency resolves.
- Bubble — an empty slot moving down the pipe — an injected no-operation
filling a stalled stage.
- Pipeline depth — how many stages there are — the count of stages from
fetch to retire, a trade-off with clock speed.
10.8 Going wider and smarter#
PLAIN10.8.1 in simple words#
- Pipelining gets you to about one instruction per cycle. Then what.
- You build more of everything. Two adders, three adders, six adders. Fetch
several instructions at once. Finish several at once.
- That is superscalar: more than one instruction completed per cycle.
- But instructions often wait for each other. If instruction 2 needs the
answer from instruction 1, doubling the hardware helps nothing.
- So the CPU looks further ahead. If instruction 5 is ready and instruction 2
is not, it runs instruction 5 first. That is out-of-order execution.
- It still has to pretend everything happened in order, in case something
goes wrong. So results are held back and released in the original order.
- And when it reaches a branch and does not yet know the answer, it guesses,
and carries on as if the guess were right. That is speculation.
- If the guess was right, the work is kept. If wrong, it is all thrown away
and the CPU restarts down the other road.
- Modern CPUs guess correctly the great majority of the time, which is the
only reason this works at all.
PLAIN10.8.2 a picture in your head#
- Picture a busy kitchen with six cooks instead of one.
- Orders arrive on a rail in strict order. A dispatcher reads them.
- Order 2 needs a sauce that order 1 is still making, so the dispatcher skips
it and gives order 5, which needs nothing, to a free cook.
- Finished dishes go to a shelf, not to the customer. The shelf is served
strictly in the original order, so the customers never notice the reshuffle.
- That shelf is the reorder buffer.
- Now the head waiter sees a customer approaching the till and guesses they
will order the usual. The kitchen starts making it before they speak.
- Nine times out of ten the waiter is right and the food is already there.
One time in ten the food is binned.
Where this comparison breaks: binned food in a real kitchen is wasted money,
and binned speculative work in a CPU is only wasted time and energy. But it is
not perfectly invisible. The traces speculation leaves in the caches are
exactly what Spectre reads, which is why guessing turned out to be a security
problem in 2018.
PLAIN10.8.3 a worked example#
- Look at why renaming is needed. Take this sequence.
1: mul rax, rbx ; slow, takes several cycles
2: mov rcx, rax ; needs the result of 1
3: mov rax, 5 ; writes rax again, needs nothing
4: add rdx, rax ; needs the new rax
- Instruction 3 does not depend on anything. It could run immediately.
- But it writes RAX, and instruction 2 still needs the old RAX. If 3 ran
early, it would destroy the value 2 is waiting for.
- This is a false dependency. The two instructions do not share data, only a
name. It is a collision of labels, not of information.
- The fix: give them different physical boxes. Say RAX currently lives in
physical register P17. Instruction 3 is told to write P42 instead, and from
then on RAX means P42.
- Now instruction 2 reads P17 and instruction 3 writes P42. They are
independent, and both can run at once.
- That relabelling is register renaming, and modern cores do it for every
single instruction, every cycle.
- Now count the win. Suppose the multiply takes 4 cycles. Without renaming
the sequence takes about 7 cycles. With renaming, instructions 3 and 4 run
in parallel with the multiply and the sequence takes about 5.
PLAIN10.8.4 what is really happening inside#
- Here is the shape of a modern core’s back end, in order.
- Fetch and predict. The branch predictor supplies a stream of addresses; the
fetcher pulls bytes from the L1 instruction cache.
- Decode into micro-operations, or read them from the micro-op cache.
- Rename: map each architectural register name onto a free physical register,
using a register alias table.
- Allocate: give each operation a slot in the reorder buffer, which keeps
program order, and a slot in a scheduler.
- Schedule and issue: reservation stations, or a unified scheduler, hold
operations until every input is ready, then fire them at a free unit.
- Execute: the arithmetic units, address generation units, load and store
units, and vector units all work in parallel.
- Write back the result into the physical register, and mark the reorder
buffer slot as complete.
- Retire, in strict program order. Only at retirement do results become
architecturally real. If an exception happened, everything after it is
discarded and never becomes visible.
- That last rule is what makes exceptions precise: the software sees a
clean, ordered machine even though the hardware ran a mess.
TECHNICAL10.8.5 the engineer’s version#
- History. The CDC 6600 of 1964, designed by Seymour Cray, had ten
functional units and a scoreboard to track dependencies. Robert Tomasulo’s
1967 algorithm for the IBM System/360 Model 91 added reservation stations
and register renaming, and it is still the basis of every design today.
- The Intel Pentium of March 1993 was the first superscalar x86, 2-wide with
its U and V pipes. The Pentium Pro of November 1995 was the first
out-of-order x86.
- Modern structure sizes, all implementation details, not architecture.
| Structure |
Zen 5, 2024 |
Golden Cove, 2021 |
| Decode width |
8, as two of 4 |
6 |
| Micro-op cache out |
12 per cycle |
8 per cycle |
| Reorder buffer |
448 entries |
512 entries |
| Integer ALUs |
6 |
5 |
- Branch prediction, in order of invention.
- Static: always predict not taken, or the backward-taken and
forward-not-taken heuristic, which suits loops. No storage needed.
- One-bit: remember the last outcome per branch. A loop of N iterations
mispredicts twice, at entry and exit.
- Two-bit saturating counter: James E. Smith, 1981. Four states, from
strongly not taken to strongly taken. It takes two wrong outcomes to
change the prediction, so a loop mispredicts once, not twice.
- Two-level adaptive: Tse-Yu Yeh and Yale Patt, 1991. Index a table of
counters using a history register of recent branch outcomes.
- gshare: Scott McFarling, 1993. Combine the branch address with the
global history by XOR, which uses the table far better.
- Perceptron: Daniel Jimenez and Calvin Lin, 2001. A simple neural
predictor, able to use very long histories.
- TAGE: Andre Seznec and Pierre Michaud, 2006. Several tagged tables
indexed with geometrically increasing history lengths, so short and
long correlations are both captured. TAGE-SC-L, with a statistical
corrector and a loop predictor, won the Championship Branch Prediction
contests and is the basis of what ships today.
- Real accuracy, in three honest lines.
- On many workloads modern predictors get above 99 percent of conditional
branches right.
- On hard integer code the figure is lower. The 2019 paper “Branch
Prediction Is Not A Solved Problem” measured about 95 percent average
accuracy for an 8 KB TAGE-SC-L across SPEC CPU 2017 integer benchmarks.
- That same paper argues the remaining misses are worth an 18.5 percent
instructions-per-cycle opportunity at current core sizes, rising to
55.3 percent for a core four times wider.
- Real cost of one miss. Measured on Intel Skylake: 16.5 cycles average when
the correct path hits in the micro-op cache, 19 to 20 cycles when it does
not. At 5 GHz that is roughly 3.3 to 4 nanoseconds. On a core that can
retire 6 instructions per cycle, a single misprediction throws away on the
order of 100 instruction slots.
- Two-bit counter state machine, worth memorizing.
taken -> taken -> taken ->
[00]--------->[01]--------->[10]--------->[11]
strong NT weak NT weak T strong T
<-------- <-------- <--------
not taken not taken not taken
predict NT predict NT predict T predict T
- Measurement:
perf stat -e branches,branch-misses ./program gives the
real misprediction rate for your code. A rate above about 2 percent on
hot code usually means a data-dependent branch that should be turned into
branchless arithmetic or a conditional move.
WORDS10.8.6 remember these#
- Superscalar — more than one instruction finished per cycle — multiple
issue, with duplicated execution units.
- Out-of-order execution — run whatever is ready — dynamic scheduling with
in-order retirement.
- Register renaming — giving the same name different boxes — mapping
architectural registers to a larger physical register file.
- Reorder buffer — the shelf that restores order — a circular buffer holding
results until in-order retirement.
- Reservation station — a waiting bay for an operation — a scheduler entry
holding a micro-op until its operands are ready.
- Speculation — acting on a guess — executing past an unresolved branch and
discarding on a miss.
- Branch predictor — the thing that guesses — hardware predicting direction
and target, today TAGE-style.
- Precise exception — software sees a tidy machine — the guarantee that on a
fault, all earlier instructions completed and none later did.
10.9 SIMD and accelerators#
PLAIN10.9.1 in simple words#
- Very often a program does the same sum to a long list of numbers.
- Brighten every pixel. Add every element of two arrays. Scale every audio
sample.
- Doing them one at a time wastes the machine, because the operation is
identical each time. Only the data changes.
- So CPUs got wide registers that hold several numbers side by side, and
instructions that do the same sum to all of them at once.
- This is SIMD: single instruction, multiple data.
- A 256-bit register holds eight 32-bit numbers. One add instruction adds all
eight pairs in one go. Eight times the work, one instruction.
- That is not the same as having eight cores. It is one core doing one
instruction that happens to be eight-wide.
- Later, chips added units for specific jobs: encryption, and now matrix
multiply for neural networks.
PLAIN10.9.2 a picture in your head#
- A normal instruction is a single hole punch: one hole per press.
- A SIMD instruction is a row of eight punches on one bar. One press, eight
holes, all in line.
- The catch is obvious. The paper must be lined up. If your eight numbers are
scattered around memory, you spend more time gathering them into a row than
you save by punching them together.
- Also, the bar punches all eight or none. If you only want six of them, you
need a mask that blocks two of the punches.
Where this comparison breaks: the punch bar is fixed at eight, but Arm’s SVE
deliberately does not fix the width. The same program runs on hardware with
128-bit or 512-bit vectors without recompiling. That is a genuinely different
design idea, not just a bigger bar.
PLAIN10.9.3 a worked example#
- Add two arrays of eight floating point numbers, using AVX on x86-64.
RDI points to array a, RSI to array b, RDX to the result.
vmovups ymm0, [rdi] ; load 8 floats from a
vmovups ymm1, [rsi] ; load 8 floats from b
vaddps ymm0, ymm0, ymm1 ; 8 adds in one instruction
vmovups [rdx], ymm0 ; store 8 results
- Four instructions do 8 additions plus 24 element moves. The scalar version
would need a loop of 8 iterations with about 4 instructions each.
- Same idea in ARM64 NEON, which is 128 bits wide, so four floats at a time.
ld1 {v0.4s}, [x0] ; load 4 floats
ld1 {v1.4s}, [x1] ; load 4 floats
fadd v0.4s, v0.4s, v1.4s ; 4 adds at once
st1 {v0.4s}, [x2] ; store 4 results
- Read
v0.4s as “register v0 treated as 4 single-precision values”.
Change it to v0.2d and the same register is 2 doubles. Change it to
v0.16b and it is 16 bytes. The bits do not move. Only the interpretation
changes, which is the numbers-and-meaning idea from Chapter 7.
- With AVX-512, ZMM registers are 512 bits, so the same loop handles 16
floats per instruction.
- Real speedups are usually below the theoretical multiple. Memory bandwidth,
alignment and loop overhead all take a cut. Two to six times is typical for
an eight-wide operation on real code.
PLAIN10.9.4 what is really happening inside#
- Physically, a SIMD unit is several copies of the same arithmetic circuit,
fed from one wide register and controlled by one decoded instruction.
- There is only one instruction fetch, one decode, one schedule. That saving
is a large part of why SIMD is efficient in energy, not just in time.
- Wide units are hot. Running 512-bit operations flat out draws so much
current that some Intel chips reduced their clock frequency while doing it,
a well-documented behaviour on Skylake-based Xeon parts from 2017.
- Then there are fixed-function accelerators. Instead of a general unit, put
an entire algorithm in silicon.
- AES encryption is the classic case. One instruction performs one complete
round of the cipher, something that takes dozens of ordinary instructions.
- Now the same idea has arrived for neural networks. The core operation there
is multiplying matrices, so chips now include matrix units and separate
neural processing units on the same die.
- Those units are not general. They do one shape of arithmetic extremely
efficiently and are useless for anything else.
TECHNICAL10.9.5 the engineer’s version#
- The term SIMD comes from Michael Flynn’s 1966 taxonomy of parallel
machines.
- The x86 vector history, with widths and dates.
| Extension |
Year |
Width |
| MMX |
1997 |
64 bits, integer only |
| SSE |
1999 |
128 bits, single float |
| SSE2 |
2000 |
128 bits, double, integer |
| AVX |
2011 |
256 bits |
| AVX2 |
2013 |
256 bits, integer, FMA |
| AVX-512 |
2016 to 2017 |
512 bits, 32 registers |
- MMX arrived with the Pentium MMX in January 1997 and reused the x87
floating point registers, so you could not mix the two.
- SSE arrived with the Pentium III in February 1999 and finally gave
separate XMM registers. AVX arrived with Sandy Bridge in January 2011 and
introduced the three-operand VEX encoding, so the destination need not
also be a source.
- AVX-512 first shipped in Knights Landing in 2016 and in Xeon Skylake-SP
during 2017. Consumer support has been inconsistent: present on Rocket Lake,
disabled on Alder Lake, and supported on AMD Zen 4 from 2022 and Zen 5
from 2024, with Zen 5 desktop parts having a full 512-bit wide data path.
Intel announced AVX10 in July 2023 to tidy this up.
- Arm’s line runs in parallel.
- Advanced SIMD, marketed as NEON, is part of ARMv7-A and mandatory in
ARMv8-A, with 32 registers of 128 bits.
- SVE was announced at Hot Chips in August 2016. It is vector-length
agnostic, permitting 128 to 2048 bits in 128-bit steps.
- Fujitsu’s A64FX, used in the Fugaku supercomputer from 2020, implements
512-bit SVE. SVE2 arrived with Armv9-A, announced in March 2021.
- AES-NI arrived with Intel Westmere in January 2010. Six instructions:
AESENC, AESENCLAST, AESDEC, AESDECLAST, AESIMC and AESKEYGENASSIST. One
AESENC performs a complete AES round. Beyond speed, the crucial property
is that it is constant time and uses no lookup tables, which removes the
cache-timing side channels that plagued table-based software AES. Armv8
has the equivalent AESE and AESD instructions.
- Matrix and neural units on the same die.
- Intel Advanced Matrix Extensions, AMX, shipped in Sapphire Rapids Xeon
in January 2023: eight tile registers of 1 KB each with a TMUL unit.
- Arm’s Scalable Matrix Extension, SME, is part of Armv9 and appears in
recent Apple silicon.
- Separate NPUs: Intel Meteor Lake in December 2023 was the first Core
part with one; Lunar Lake in September 2024 raised it to 48 TOPS.
Microsoft’s Copilot+ PC badge requires at least 40 TOPS.
- In phones, the Snapdragon 8 Elite Gen 5, announced 24 September 2025,
has a Hexagon NPU that Qualcomm states is 37 percent faster than the
previous generation. Apple’s A19 Pro, from September 2025, has a
16-core Neural Engine.
- Separate what is what, as the honesty rules require.
- Established fact: the silicon exists, it multiplies matrices at low
precision far more efficiently than the general CPU cores, and it is
shipping in ordinary phones and laptops today.
- Active research: how to split a model across CPU, GPU and NPU, which
number formats to use, and how to keep memory bandwidth from becoming
the limit.
- Marketing claim: TOPS figures. A TOPS number depends entirely on the
number format assumed, is usually a peak that no real workload reaches,
and is not comparable between vendors. Treat it as a rough class, not a
measurement.
- Chapters 46 onwards return to this: what those matrix units actually
compute, and why a neural network reduces to matrix multiply.
- Tools: on Linux
cat /proc/cpuinfo lists avx2, avx512f, aes and so
on in the flags line. On macOS, sysctl hw.optional lists the Arm
features present.
WORDS10.9.6 remember these#
- SIMD — one order, many numbers — single instruction, multiple data, a lane
parallel execution model.
- Vector register — a wide box holding several numbers — XMM at 128, YMM at
256, ZMM at 512 bits on x86-64.
- Lane — one slot in the row — one element position within a vector register.
- Mask register — a stencil saying which lanes count — k0 to k7 on AVX-512,
predicate registers on SVE.
- Vector-length agnostic — the same code on any width — the SVE model where
the hardware vector length is discovered at run time.
- AES-NI — encryption built into the metal — x86 instructions performing AES
rounds directly, constant time.
- NPU — a chunk of silicon for neural maths — neural processing unit, a
fixed-function matrix and activation engine.
- TOPS — a marketing speed number — tera-operations per second, precision
dependent and not comparable across vendors.
10.10 Many cores#
PLAIN10.10.1 in simple words#
- Until about 2005, each new chip simply ran at a higher clock speed.
- Then that stopped. Clocks have barely moved since. A fast desktop chip in
2004 ran at 3.8 GHz, and a fast desktop chip in 2025 boosts to 5.7 GHz.
That is not much in twenty years.
- The reason is heat. Pushing the clock higher needs more voltage, and the
power a chip burns rises with the square of the voltage. The heat became
impossible to remove.
- So instead of one faster CPU, chipmakers put several CPUs on one piece of
silicon. Each one is a core.
- Two cores can do two things at once, genuinely, in parallel.
- That only helps if the software can be split into pieces that run at the
same time. Many programs cannot be split much, and for those, more cores
do very little.
- Cores also have to agree about memory. If two cores both hold a copy of the
same number and one changes it, the other must find out.
- Keeping them in agreement is called cache coherence, and it is one of
the hardest parts of building a modern chip.
PLAIN10.10.2 a picture in your head#
- One cook working faster has a limit: hands only move so quickly, and past
a point the kitchen catches fire.
- So you hire eight cooks. Now eight dishes can be made at once.
- But if the recipe says “wait for the stock to reduce for an hour”, eight
cooks do not make the stock reduce in less than an hour. That step is
stubbornly serial.
- Worse, the cooks share one fridge. If two cooks both take out the butter,
and one of them salts it, the other is holding stale butter.
- So the kitchen needs a rule: before you change something, shout, and
everyone else throws their copy away.
- Shouting takes time, and the more cooks there are, the more shouting.
Where this comparison breaks: cooks can talk to each other in any order, but
cores must agree on a strict order of memory events, or programs break in ways
that only show up once in a million runs. That is memory ordering, and it is
much stricter and much more subtle than shouting.
PLAIN10.10.3 a worked example#
- Amdahl’s law, from Gene Amdahl in 1967, tells you the ceiling.
- Say 95 percent of your program can run in parallel and 5 percent cannot.
- The formula for speedup with n cores is 1 divided by
((1 minus p) plus p divided by n), where p is the parallel fraction.
- With p equal to 0.95, work it out.
| Cores |
Calculation |
Speedup |
| 2 |
1 / (0.05 + 0.475) |
1.90 |
| 4 |
1 / (0.05 + 0.2375) |
3.48 |
| 8 |
1 / (0.05 + 0.11875) |
5.93 |
| 16 |
1 / (0.05 + 0.059375) |
9.14 |
| 64 |
1 / (0.05 + 0.0148) |
15.4 |
| Infinite |
1 / 0.05 |
20.0 |
- Read the last line again. Even with infinite cores, a program that is 5
percent serial can never go more than 20 times faster.
- At 16 cores you already get 9.14 of a possible 20. Doubling to 32 gets you
to 12.6. The returns collapse quickly.
- If only 50 percent is parallel, the ceiling is 2 times, no matter what.
- This single calculation explains why buying more cores often does nothing.
PLAIN10.10.4 what is really happening inside#
- Take two cores, each with its own L1 cache, sharing memory. Both want the
variable X at address A. Follow the standard four-state rule set, MESI.
- Each cached line is in one of four states: Modified (I changed it and
nobody else has it), Exclusive (I have the only copy and it is clean),
Shared (others may have it too, clean), Invalid (I have nothing).
| Step |
Action |
Core 0 / Core 1 |
| 0 |
Nothing cached |
I / I |
| 1 |
Core 0 reads A |
E / I |
| 2 |
Core 0 writes A |
M / I |
| 3 |
Core 1 reads A |
S / S |
| 4 |
Core 1 writes A |
I / M |
| 5 |
Core 0 reads A |
S / S |
- Step by step. At step 1 core 0 misses, fetches the line, and since no other
core has it, it takes it Exclusive.
- At step 2 core 0 writes. Because it already had it Exclusive, no message is
needed at all. It flips silently to Modified. Memory is now out of date.
- At step 3 core 1 asks for the line. Core 0 sees the request, notices it
holds a Modified copy, and supplies the fresh data. Both settle at Shared.
- At step 4 core 1 wants to write. It must first broadcast a request for
ownership. Core 0 drops its copy to Invalid. Core 1 becomes Modified.
- At step 5 core 0 wants to read again, and the whole dance repeats the other
way round.
- Each of those transfers costs tens of cycles. Two cores fighting over one
variable in a tight loop is one of the slowest things a modern CPU can do.
TECHNICAL10.10.5 the engineer’s version#
- The physics. Dynamic power is approximately P equals alpha times C times V
squared times f. Alpha is switching activity, C capacitance, V supply
voltage, f frequency.
- Dennard scaling, from Robert Dennard’s 1974 IBM paper, said that as
transistors shrink you can lower V and C in step, so power per square
millimetre stays constant while frequency rises. That held for thirty years.
- It broke down around 2005 to 2006. Threshold voltage could not keep falling
without leakage current exploding, so supply voltage stalled near 1 volt,
so power density started rising with every shrink.
- Intel cancelled its 4 GHz Pentium 4 in October 2004 and topped out at
3.8 GHz in November 2004. IBM’s POWER4 in 2001 was the first mainstream
dual-core chip; AMD’s Athlon 64 X2 and Intel’s Pentium D both arrived in
May 2005. That is the exact moment the industry turned.
- Simultaneous multithreading, stated honestly. SMT duplicates the
architectural state, meaning registers, program counter and flags, so one
core presents two logical processors to the operating system. It does not
duplicate execution units, caches or TLBs. Those are shared.
- The gain comes from filling issue slots that one thread leaves empty
while it waits for memory. Typical real throughput gain is 10 to 30
percent, not 100 percent.
- It can be negative. Two threads with large working sets thrash the
shared L1 and L2 and both run slower than one would alone.
- Intel shipped Hyper-Threading on Xeon in February 2002 and on the
3.06 GHz Pentium 4 in November 2002.
- It has a security cost. Shared resources leak timing information, which
produced attacks including PortSmash in 2018 and the MDS family in
2019. OpenBSD disabled SMT by default in 2018.
- Intel dropped SMT entirely on Arrow Lake in October 2024. The Core Ultra
9 285K has 24 cores and exactly 24 threads.
- Cache coherence. MESI comes from Mark Papamarcos and Janak Patel, 1984,
and is often called the Illinois protocol. Real chips extend it: MOESI adds
Owned, used by AMD, and MESIF adds Forward, used by Intel. Large chips use
a directory rather than broadcast snooping, because broadcast does not
scale past a few dozen cores.
- Memory ordering is a separate problem from coherence. Coherence is about
one address. Ordering is about the visible order of accesses to different
addresses.
- x86-64 uses Total Store Order. Loads are not reordered with loads,
stores are not reordered with stores, but a store followed by a load to
a different address may appear reordered because of the store buffer.
- ARM64 is weakly ordered. Almost any reordering is allowed unless you
write a barrier.
- Barriers: MFENCE, LFENCE and SFENCE on x86-64; DMB, DSB and ISB on
ARM64. In portable code, use the C11 or C++11 atomics with an explicit
memory order rather than inline assembly.
- This is why lock-free code that works on an Intel laptop can fail on an
Apple silicon Mac. The bug was always there. x86-64’s stronger model
was hiding it.
- False sharing. Two threads write two different variables that happen to sit
in the same 64-byte cache line. There is no logical sharing at all, but the
line ping-pongs between cores at tens of cycles per bounce.
struct bad { long a; long b; }; /* same line */
struct good {
long a; char pad[56]; /* 8 + 56 = 64 */
long b;
};
- In C++,
alignas(64) or std::hardware_destructive_interference_size
expresses this properly. On Linux, perf c2c record and perf c2c report
find false sharing directly by address.
- Amdahl’s law was presented at the 1967 AFIPS Spring Joint Computer
Conference. John Gustafson’s 1988 rebuttal points out that in practice
people grow the problem to fit the machine, and for a fixed time budget
rather than a fixed problem size the scaling looks much better. Both are
correct; they answer different questions.
- Tools:
nproc and lscpu show cores and threads, taskset pins a
process to specific cores, and numactl --hardware shows memory locality
on multi-socket machines.
WORDS10.10.6 remember these#
- Core — one complete CPU on the die — an independent instruction stream with
its own registers, L1 and L2.
- Power wall — the point where heat stopped the clock rising — the end of
frequency scaling around 2005 due to power density.
- Dennard scaling — shrinking used to be free — constant power density under
scaling, broken since roughly 2006.
- SMT — one core pretending to be two — simultaneous multithreading,
duplicated architectural state over shared execution resources.
- Cache coherence — everyone agrees what a given address holds — MESI and its
relatives, enforced by snooping or a directory.
- Memory ordering — the agreed order of unrelated accesses — TSO on x86-64,
weak on ARM64, controlled by barriers.
- False sharing — accidental fighting over one line — unrelated variables in
the same 64-byte cache line bouncing between cores.
- Amdahl’s law — the serial part sets the ceiling — speedup equals
1 / ((1 - p) + p/n).
10.11 Caches from the CPU side#
PLAIN10.11.1 in simple words#
- Main memory is desperately slow compared to the CPU. Fetching from it takes
the time of hundreds of instructions.
- So the CPU keeps small, fast copies of recently used memory close by. Those
are caches.
- There are usually three levels. L1 is tiny and instant, L2 is bigger and
slower, L3 is much bigger and slower again.
- Nothing is copied one byte at a time. Memory is moved in blocks of 64
bytes, called a cache line.
- So if you read one byte, you get its 63 neighbours for free. That is why
walking through an array in order is enormously faster than jumping about.
- Caches work because of two habits real programs have. If you used something
recently you will probably use it again soon, and if you used something you
will probably use the thing next to it.
- Those two habits have names: temporal locality and spatial locality. Almost
all of computer performance rests on them.
PLAIN10.11.2 a picture in your head#
- Picture a library with a huge basement and a small desk.
- You cannot work in the basement. You fetch books up to the desk.
- Fetching one book takes twenty minutes, so you never fetch one book. You
fetch the whole shelf section it sits in. That section is the cache line.
- Your desk holds four books. The table behind you holds fifty. The room next
door holds a thousand. Those are L1, L2 and L3.
- When your desk is full and you need a new book, you must put one back. Which
one you choose is the replacement policy.
- If you always ask for books that are near each other, you almost never go
to the basement. If you ask for random books, you go every time.
Where this comparison breaks: you decide what to fetch, and the CPU mostly
does not. The hardware guesses your next request and fetches it before you
ask. That guessing is prefetching, and when it works you never see the
basement at all.
PLAIN10.11.3 a worked example#
- Real figures for AMD Zen 5, the Ryzen 9000 desktop series from 2024.
| Level |
Size |
Latency |
Time at 5.7 GHz |
| L1 instruction |
32 KB per core |
- |
- |
| L1 data |
48 KB per core |
4 cycles |
0.70 ns |
| L2 |
1 MB per core |
14 cycles |
2.46 ns |
| L3 |
32 MB per die |
about 50 cycles |
8.8 ns |
| DDR5-6000 |
32 GB |
- |
about 75 ns |
The L1 and L2 cycle counts are AMD’s published figures. The L3 figure is
approximate: AMD quoted 50 cycles for Zen 4, and independent measurements of
Zen 5 land close to it but vary with the part and the memory clock.
- Read the last two rows together. Going to memory instead of L3 costs about
nine times longer. Going to memory instead of L1 costs over a hundred times
longer.
- Now measure it yourself. Walk a large array in order, then in random order.
/* sequential: hardware prefetch works, ~1 to 2 ns per element */
for (i = 0; i < n; i++) sum += a[i];
/* random: every access is a cache miss and a TLB risk */
for (i = 0; i < n; i++) sum += a[idx[i]]; /* idx shuffled */
- On a current desktop, with an array well over the L3 size, the sequential
loop typically runs at roughly 1 to 2 nanoseconds per element and the
random loop at roughly 70 to 120 nanoseconds per element. Those are
approximate and machine dependent, but the ratio, somewhere around 50 to
100 times, is consistent everywhere.
- Same instructions. Same number of additions. Same array. The only
difference is the order of the addresses.
- This is the single most important performance fact in the chapter.
PLAIN10.11.4 what is really happening inside#
- When the core wants an address, the cache must answer “do I have it” in one
or two cycles. It cannot search. It must go straight to one place.
- So the address is cut into three fields.
- The offset: which byte inside the 64-byte line.
- The index: which set of the cache to look in.
- The tag: the rest of the address, stored alongside the data to prove
which line this actually is.
- Direct-mapped means one place per index. Fast and simple, but two hot
addresses that share an index knock each other out forever.
- Fully associative means a line may sit anywhere. No conflicts, but you
must compare every tag, which is impossibly expensive at any size.
- Set-associative is the compromise, and it is what everyone uses. Each
index selects a small set of, say, 8 or 12 slots, and only those tags are
compared. You get most of the conflict resistance for a small cost.
- When a set is full, something must go. The replacement policy picks the
victim, usually an approximation of least recently used.
- On a write, two choices. Write-through sends every write onward
immediately, which is simple but floods the bus. Write-back marks the
line dirty and only writes it out when it is evicted. Every modern data
cache is write-back.
TECHNICAL10.11.5 the engineer’s version#
- Work the address split by hand for Zen 5’s L1 data cache: 48 KB, 12-way
set associative, 64-byte lines.
- Lines total: 48 x 1024 / 64 = 768.
- Sets: 768 / 12 = 64.
- Offset bits: log2(64) = 6.
- Index bits: log2(64) = 6.
- Tag bits: everything above bit 11.
- Take the address 0x12345678 and split it.
0x12345678 = 0001 0010 0011 0100 0101 0110 0111 1000
|<--------- tag ------->|index | off |
tag = 0x12345 (bits 31..12)
index = 011001b = set 25 (bits 11..6)
offset= 111000b = byte 56 (bits 5..0)
- Now a direct-mapped counter-example: a 32 KB direct-mapped cache with 64-
byte lines has 512 lines, so 9 index bits. Any two addresses exactly 32 KB
apart share an index. A loop touching two arrays that happen to be 32 KB
apart will miss on every single access. That is a conflict miss, and it is
why associativity exists.
- The three kinds of miss, called the three Cs after Mark Hill’s 1987 thesis:
compulsory (first ever touch), capacity (working set exceeds the cache) and
conflict (fits, but the sets collide).
- Replacement policies in shipping hardware: true LRU up to about 4 ways;
tree-based pseudo-LRU beyond that; RRIP and its dynamic variant from a 2010
ISCA paper for last-level caches, which resist thrashing far better;
pure random in some Arm L2 designs, chosen for its tiny cost.
- Write policy detail: write-back with write-allocate is standard for data
caches. A dirty bit per line tracks whether a write-back is needed. Stores
to memory marked write-combining, typically video memory, bypass the cache
through separate combining buffers.
- Hardware prefetchers, per level and multiple per level. Next-line, stride
detectors that spot a constant step, stream prefetchers that follow several
sequences at once, and region-based prefetchers. They can be counter
productive on pointer-chasing code, and they can be disabled through
model-specific registers on both Intel and AMD parts.
- Cache line size is 64 bytes on x86-64 and on most Arm cores. Apple’s M
series uses 128 bytes, which is a real portability trap for anyone padding
structures by hand.
- Inclusion policy is an implementation detail, not a standard. Intel’s
ring-based designs historically used an inclusive L3; AMD’s L3 is a victim
cache, holding only lines evicted from L2, which is why AMD’s L3 is quoted
per core complex die rather than per chip.
- Tools:
lscpu -C prints every level with size, ways and line size;
getconf LEVEL1_DCACHE_LINESIZE prints the line size;
perf stat -e cache-references,cache-misses,LLC-load-misses measures the
real miss rate; valgrind --tool=cachegrind simulates it per line of
source code.
WORDS10.11.6 remember these#
- Cache line — the block memory moves in — 64 bytes on x86-64 and most Arm,
128 on Apple silicon.
- Tag, index, offset — proof, shelf, and position on the shelf — the three
fields an address is split into for lookup.
- Set-associative — a few slots per index — N-way lookup comparing N tags in
parallel.
- Conflict miss — two hot addresses on the same shelf — a miss that would not
happen in a fully associative cache of the same size.
- Write-back — write later, when evicted — a dirty bit marks lines needing a
write-out, as opposed to write-through.
- Prefetching — fetching before you ask — hardware or software issuing loads
ahead of demand based on detected patterns.
- Locality — you reuse things, and their neighbours — temporal and spatial
locality, the reason caches work at all.
10.12 Interrupts, exceptions and privilege#
PLAIN10.12.1 in simple words#
- The loop of fetch, decode, execute never stops on its own. Something must
be able to break into it.
- Two kinds of thing break in.
- An interrupt comes from outside: a key was pressed, a network packet
arrived, a timer expired. It has nothing to do with the current instruction.
- An exception comes from inside: a divide by zero, an instruction the
chip does not know, a memory address that is not mapped.
- Either way the CPU stops, remembers exactly where it was, jumps to a fixed
handler routine, runs it, and then goes back.
- It also changes mode. Handlers run with more power than ordinary programs.
- That mode change is the whole basis of operating system security. Your
program cannot touch the disk directly. It must ask, and asking means
deliberately triggering a controlled jump into the more powerful mode.
PLAIN10.12.2 a picture in your head#
- Picture a receptionist working through a pile of forms.
- The phone rings. They put a bookmark in the pile, note exactly where they
were, answer the phone, deal with it, and return to the exact same form.
- The phone number is not decided on the spot. There is a printed list on the
wall: “for fire, call this room; for deliveries, call that room”. That list
is the interrupt vector table.
- Some rooms need a key the receptionist does not have. To get in, they must
go through a specific door with a guard, who checks them and hands over the
key on the way in and takes it back on the way out.
- That guarded door is a system call.
Where this comparison breaks: the receptionist chooses when to look up. A CPU
can be interrupted between any two instructions, whether it wants to be or
not, and the hardware, not the software, decides when.
PLAIN10.12.3 a worked example#
- Print thirteen characters to the screen. On Linux, x86-64.
mov rax, 1 ; 1 = the write system call
mov rdi, 1 ; file 1 = standard output
lea rsi, [msg] ; address of the text
mov rdx, 13 ; how many bytes
syscall ; cross into the kernel
- The same thing on Linux, ARM64.
mov x8, #64 ; 64 = the write system call
mov x0, #1 ; file 1 = standard output
adr x1, msg ; address of the text
mov x2, #13 ; how many bytes
svc #0 ; cross into the kernel
- Notice the shape is identical. Put a number saying which service, put the
arguments in agreed registers, execute one special instruction.
- Notice the register names differ, the call numbers differ, and the
instruction differs. All three are conventions of that operating system on
that architecture, not laws of the machine.
- The
syscall and svc instructions do almost nothing themselves. They
save the return address, raise the privilege level, and jump to one fixed
address the kernel installed at boot. The kernel does the rest.
PLAIN10.12.4 what is really happening inside#
- Between instructions, the core checks whether an interrupt is pending. If
interrupts are enabled and one is pending, it acts.
- It finishes the instructions already in flight and discards the speculative
ones, so the machine has a clean, precise state.
- It looks up the vector number in a table of handler addresses whose
location is held in a special register.
- It pushes enough state to come back: at minimum the return address and the
flags, and on x86 also the stack and code segment selectors and, for some
faults, an error code.
- It raises the privilege level and jumps to the handler.
- The handler immediately saves everything else it will use, because the
hardware only saved the minimum.
- When finished, a return-from-interrupt instruction restores the saved state
and drops the privilege level in one atomic step.
- All of this costs real time. A modern system call is on the order of tens
to a few hundred nanoseconds, and the Meltdown mitigations of 2018 made it
noticeably more expensive by forcing a page-table switch on every crossing.
TECHNICAL10.12.5 the engineer’s version#
- x86-64 uses the Interrupt Descriptor Table, located by the IDTR register.
It has 256 entries. Vectors 0 to 31 are architecturally defined exceptions;
32 to 255 are available for devices and software.
| Vector |
Name |
Cause |
| 0 |
#DE |
Divide error |
| 6 |
#UD |
Invalid opcode |
| 13 |
#GP |
General protection fault |
| 14 |
#PF |
Page fault |
- Exceptions are classified as faults, which re-run the instruction after the
handler, traps, which continue after it, and aborts, which do not return.
A page fault is a fault; that is exactly how demand paging works.
- ARM64 uses a vector table located by VBAR_EL1, with 16 entries of 128
bytes each.
- The 16 entries are four exception types, which are synchronous, IRQ,
FIQ and SError, crossed with four sources.
- The four sources are the current exception level using SP0, the current
level using SPx, a lower level in AArch64, and a lower level in AArch32.
- State is saved into ELR_EL1 for the return address, SPSR_EL1 for the
saved processor state, and ESR_EL1 for the syndrome describing why.
- Privilege levels. x86-64 defines rings 0 to 3. In practice only ring 0, the
kernel, and ring 3, user code, are used; ring 1 and 2 are historical.
Virtualization adds VMX root mode below ring 0, informally called ring -1,
and System Management Mode sits outside all of it.
- ARM64 defines exception levels instead: EL0 for applications, EL1 for the
kernel, EL2 for a hypervisor, EL3 for the secure monitor. Higher number
means more privilege, the opposite direction to x86 rings.
- SYSCALL on x86-64 was introduced by AMD and is deliberately minimal.
- It saves RIP into RCX and RFLAGS into R11, then masks RFLAGS using
IA32_FMASK.
- It loads the code and stack selectors from IA32_STAR and jumps to the
address held in IA32_LSTAR.
- It performs no memory reads and no descriptor table lookup. That is
precisely why it is fast compared with the old
int 0x80 gate.
- Because SYSCALL destroys RCX, the Linux x86-64 system call ABI passes its
fourth argument in R10 rather than RCX. The number goes in RAX, arguments
in RDI, RSI, RDX, R10, R8, R9, and the result comes back in RAX. On ARM64
the number goes in X8, arguments in X0 to X5, result in X0.
- Kernel page-table isolation, the Meltdown fix, unmaps almost all kernel
memory while user code runs, so each crossing writes CR3 and disturbs the
TLB. On processors without PCID tagging the cost is severe. This is a
concrete case where a security fix has a permanent, measurable performance
price.
- The vDSO exists to avoid the crossing entirely for cheap, common calls.
Linux maps a small shared page into every process so that
clock_gettime
and similar can read a kernel-updated timestamp without a system call.
- Tools:
strace ./program traces every system call and its arguments,
cat /proc/interrupts shows interrupt counts per CPU per device, and
perf trace gives timing per call.
WORDS10.12.6 remember these#
- Interrupt — something outside wants attention — an asynchronous event from
a device, delivered between instructions.
- Exception — the current instruction went wrong — a synchronous fault, trap
or abort caused by execution itself.
- Vector table — the printed list of who to call — IDT on x86-64, the table
at VBAR_EL1 on ARM64.
- Context switch — putting a bookmark in and picking up another book —
saving and restoring register state to change what is running.
- Privilege ring — how much the running code is allowed to do — rings 0 to 3
on x86-64, exception levels EL0 to EL3 on ARM64.
- System call — knocking on the kernel’s door on purpose — SYSCALL on
x86-64, SVC on ARM64, with the number in a register.
- Page fault — the address had no memory behind it — a fault that lets the
kernel map a page and re-run the instruction.
10.13 Reading a real spec sheet#
PLAIN10.13.1 in simple words#
- A CPU page is a wall of numbers. Almost all of them are simple once you
know what each one is measuring.
- Cores means how many complete CPUs are on the chip. Threads means how many
programs the operating system can schedule at once, which may be the same
number or twice it.
- Base clock is what the chip guarantees under a heavy load on all cores.
Boost clock is the best it will ever do, briefly, on one core, when cool.
- Cache numbers tell you how much fast memory sits between the cores and main
memory, at each of three levels.
- TDP is a cooling target in watts. It is not the power the chip draws.
- Process node, such as “3 nm”, is a marketing name for a manufacturing
generation, not a measurement of anything on the chip.
- Socket says which motherboard it physically fits.
- Memory channels and PCIe lanes say how much data can get in and out.
PLAIN10.13.2 a picture in your head#
- Compare it to reading a car’s specification.
- Cores are cylinders. Threads are how many drivers the dashboard pretends
there are.
- Base clock is the speed you can hold all day up a hill. Boost clock is what
the speedometer reaches downhill with a tailwind, for thirty seconds.
- Cache is the fuel already in the lines. TDP is the radiator’s rating.
- PCIe lanes are the number of doors for luggage.
Where this comparison breaks: a car’s top speed is a property of the car, but
a CPU’s boost clock is a property of the car, the road, the weather and the
driver together. Cooling, motherboard power delivery and even the individual
silicon sample all change it.
PLAIN10.13.3 a worked example#
- Take a current desktop chip: the AMD Ryzen 9 9950X3D, launched on
12 March 2025. Every number decoded.
| Item |
Value |
What it means |
| Cores / threads |
16 / 32 |
16 real cores, SMT on |
| Base clock |
4.3 GHz |
Guaranteed all-core floor |
| Boost clock |
5.7 GHz |
Best case, one core, cool |
| L1 cache |
80 KB per core |
32 KB code, 48 KB data |
| L2 cache |
1 MB per core |
16 MB in total |
| L3 cache |
128 MB |
96 MB stacked, 32 MB plain |
| TDP |
170 W |
Cooling design target |
| Socket |
AM5 |
LGA 1718 mainboard fit |
| Memory |
Dual channel DDR5 |
Two 64-bit paths |
| PCIe |
5.0, 24 usable lanes |
Graphics and storage |
- The 128 MB of L3 is not one pool. This chip has two eight-core dies. One
has a 64 MB cache die stacked on top of it, giving that die 96 MB. The
other has the normal 32 MB.
- So threads on the wrong die do not get the big cache. That is why the
operating system scheduler has to be told which die is which.
- Now the phone side: the Qualcomm Snapdragon 8 Elite Gen 5, announced on
24 September 2025.
| Item |
Value |
What it means |
| Prime cores |
2 at up to 4.6 GHz |
Third-generation Oryon |
| Perf cores |
6 at up to 3.63 GHz |
Same design, lower clock |
| Threads |
8 |
No SMT, one per core |
| Process |
3 nm class |
TSMC generation name |
| GPU |
Adreno at 1.2 GHz |
Sliced architecture |
| NPU |
Hexagon |
Qualcomm claims +37% |
| Memory |
LPDDR5X |
On-package, no slots |
| Modem |
Snapdragon X85 |
Up to 12.5 Gbps down |
- Notice what is missing from the phone sheet: no TDP, no socket, no memory
channels, no PCIe lanes. A phone chip is soldered, its memory is stacked on
the package, and its power budget is a thermal envelope of a few watts, not
a published number.
PLAIN10.13.4 what is really happening inside#
- TDP deserves a paragraph of its own, because it is widely misread.
- TDP is the heat, in watts, that the cooler must be able to remove for the
chip to hold its rated behaviour. It is a design target for the cooling
system.
- AMD’s socket AM5 parts will draw up to 1.35 times the TDP in package power.
So a 170 W part draws up to about 230 W.
- Intel now publishes two numbers instead: base power and maximum turbo
power. On the Core Ultra 9 285K those are 125 W and 250 W.
- Boost clock is a ceiling, not a promise. It requires a light load, a good
core, headroom in the power budget, and cool silicon. All-core sustained
clocks under a heavy load are typically several hundred megahertz lower.
- Cores are no longer identical. Intel’s desktop parts mix performance cores
and efficient cores with different clocks and different capabilities. Phone
chips have done this since Arm’s big.LITTLE arrangement in 2011.
- “Cores plus threads” on a mixed chip is genuinely confusing. The Core Ultra
9 285K has 8 performance cores plus 16 efficient cores, which is 24 cores,
and because SMT was removed it has exactly 24 threads.
TECHNICAL10.13.5 the engineer’s version#
- The two current desktop parts side by side, from vendor specifications.
| Spec |
9950X3D |
Core Ultra 9 285K |
| Launched |
March 2025 |
Q4 2024 |
| Cores |
16 uniform |
8 P plus 16 E |
| Threads |
32 |
24 |
| Base clock |
4.3 GHz |
3.7 GHz P, 3.2 GHz E |
| Max turbo |
5.7 GHz |
5.7 GHz |
| L2 total |
16 MB |
40 MB |
| L3 |
128 MB |
36 MB |
| Power |
170 W TDP |
125 W base, 250 W max |
| Socket |
AM5 |
FCLGA1851 |
| Memory |
DDR5, 2 channels |
DDR5-6400, 2 channels |
| PCIe |
5.0, 24 usable |
5.0 and 4.0, 24 lanes |
- Process nodes: the 9950X3D uses TSMC N4P for its core dies with a separate
6 nm class I/O die; the 285K uses a TSMC N3B compute tile in a multi-tile
package. In both cases the number is a process generation name. Chapter 3
covered why no dimension on the chip actually measures 3 or 4 nanometres.
- The mobile parts for comparison, from vendor material.
| Spec |
Snapdragon 8E Gen 5 |
Apple A19 Pro |
| Announced |
24 Sept 2025 |
9 Sept 2025 |
| CPU |
2 at 4.6, 6 at 3.63 |
2 at 4.26, 4 at 2.6 |
| Process |
3 nm class |
TSMC N3P |
| Memory |
LPDDR5X on package |
12 GB LPDDR5X |
| Bandwidth |
not published |
about 76.8 GB/s |
| NPU |
Hexagon |
16-core Neural Engine |
- Note that Apple publishes core counts and little else; Qualcomm publishes
clocks; neither publishes sustained power. Comparing them properly needs
measurement, not spec sheets.
- Memory bandwidth is worth computing rather than reading. Dual-channel
DDR5-6000 gives 2 channels times 8 bytes times 6000 million transfers per
second, which is 96 GB/s peak. Real achieved bandwidth is typically 70 to
85 percent of that.
- PCIe bandwidth likewise: PCIe 5.0 runs at 32 gigatransfers per second per
lane, and with 128b/130b encoding that is about 3.94 GB/s per lane per
direction, so a 16-lane graphics slot is about 63 GB/s each way.
- Tools:
lscpu, lscpu -C, dmidecode -t processor for socket and
package data, sudo lspci -vv for actual negotiated PCIe link width and
speed, and turbostat for live clock, power and temperature.
WORDS10.13.6 remember these#
- Base clock — the speed it can always hold — guaranteed frequency at rated
power with all cores loaded.
- Boost clock — the best it ever does — opportunistic maximum for a light
load with thermal and power headroom.
- TDP — how much heat the cooler must remove — thermal design power, a
cooling target, not consumption.
- PPT, PL1, PL2 — the real power limits — package power tracking on AMD,
base and turbo power limits on Intel.
- Socket — the physical fit — LGA 1718 for AM5, FCLGA1851 for Arrow Lake.
- Memory channel — one independent 64-bit path to DRAM — two channels is
standard on desktop, more on workstation and server.
- PCIe lane — one serial link pair for expansion — PCIe 5.0 gives about
3.94 GB/s per lane per direction.
- System on chip — everything in one package — CPU, GPU, NPU, modem, memory
controller and I/O on one piece of silicon.
10.14 How fast is a CPU really#
PLAIN10.14.1 in simple words#
- Speed is not gigahertz. Gigahertz is how many times per second the clock
ticks, and that is only half the story.
- The other half is how much work the chip gets done per tick.
- A chip at 3 GHz that does two instructions per tick beats a chip at 4 GHz
that does one.
- Work per tick is called instructions per cycle, or IPC.
- Time taken equals the number of instructions, times cycles per instruction,
divided by the clock speed. Those three numbers are the whole of it.
- A compiler that emits fewer instructions helps. A wider chip that runs more
per cycle helps. A higher clock helps. All three multiply together.
- So you cannot compare two different chips by clock speed alone, and you
certainly cannot compare two different instruction sets that way.
PLAIN10.14.2 a picture in your head#
- Think of a factory line. The clock is how often the conveyor advances.
- IPC is how many items each advance actually delivers.
- A line that advances four times a second but usually delivers nothing is
worse than one that advances three times a second and always delivers two.
- Turbo is the line running fast for a short burst while everything is still
cool, then slowing down once the machines heat up.
- Thermal throttling is the safety cut-out that slows the line before
anything melts.
Where this comparison breaks: a factory delivers nothing when starved of
parts, and so does a CPU, but the starvation is invisible. A core waiting on
memory still ticks its clock and still reports full frequency. It just retires
almost no instructions. That is why frequency graphs look healthy while
programs run slowly.
PLAIN10.14.3 a worked example#
- Take a program of exactly 1,000,000,000 instructions.
- CPU A: 4.0 GHz, IPC 1.0. Time = 1e9 / (4e9 x 1.0) = 0.250 seconds.
- CPU B: 3.0 GHz, IPC 1.8. Time = 1e9 / (3e9 x 1.8) = 0.185 seconds.
- CPU B is 26 percent faster while being 25 percent slower in gigahertz.
- This is not a made-up case. It is exactly what happened in 2006. The
Pentium 4 670 ran at 3.8 GHz. The Core 2 Duo E6600 ran at 2.4 GHz and beat
it comfortably per core, because its IPC was far higher.
- Now add the third term. If a better compiler removes 20 percent of the
instructions, CPU A takes 0.200 seconds without any hardware change.
- Real IPC values worth carrying around: pointer-chasing code that misses
cache constantly runs at 0.1 to 0.3; ordinary integer application code runs
at roughly 1 to 2.5 on a modern wide core; tight vectorized numeric loops
can exceed 4.
PLAIN10.14.4 what is really happening inside#
- Turbo works like a budget. The chip watches its power draw, its current
draw and its temperature, thousands of times a second.
- While all three are under their limits, it raises the clock. When any one
hits its limit, it lowers it.
- So the same chip in a small case with a weak cooler is genuinely a slower
chip than in a big case with a good one. The number on the box does not
change; the achieved clock does.
- Not all cores are equal even within one chip. Manufacturing variation means
some cores tolerate higher clocks, and the firmware knows which, and sends
single-threaded work there.
- Thermal throttling is the last-resort mechanism. If temperature reaches the
limit, typically around 95 to 100 degrees Celsius, the clock drops sharply
until it recovers.
- This is why a benchmark run for 10 seconds and a benchmark run for 10
minutes give different answers, and why laptop reviews measure both.
TECHNICAL10.14.5 the engineer’s version#
- The performance equation, often called the iron law of processor
performance, is:
CPU time = instruction count x cycles per instruction x clock period.
- IPC is the reciprocal of CPI. Each of the three terms is owned by a
different party: the compiler and program own instruction count, the
microarchitecture owns CPI, and the process and power budget own the clock.
- Measuring it, on Linux.
perf stat -e cycles,instructions,branch-misses ./program
# prints, among other lines:
# 1,842,113,904 cycles
# 3,120,884,551 instructions # 1.69 insn per cycle
- That “insn per cycle” figure counts retired instructions, meaning the ones
that actually completed. Speculative work that was thrown away is not
counted, which is the correct definition.
- IPC is not comparable across instruction sets. One x86-64 instruction may
do a load, an arithmetic operation and an addressing calculation, while the
ARM64 equivalent needs two or three instructions. Comparing IPC between
them measures encoding density, not speed.
- IPC is also not comparable across programs. A memory-bound program has low
IPC because it is waiting, and no amount of core width fixes it.
- Benchmark categories, and what each is honest about.
- SPEC CPU 2017: real compiled applications, with separate integer and
floating point suites and separate rate and speed variants. Rate
measures throughput with many copies; speed measures one job’s time.
It is the closest thing to a standard, and results must be submitted
with full disclosure of compiler flags.
- Geekbench 6 and Cinebench 2024: quick, widely quoted, and dominated by
short bursts, so they favour high boost clocks over sustained ability.
- Microbenchmarks: measure one instruction’s latency or throughput. Useful
for engineers, useless as a purchase guide.
- Your own workload, timed: always the best answer if you can get it.
- Turbo control is exposed as real, documented limits. Intel defines PL1, the
long-term power limit, PL2, the short-term limit, and tau, the time
constant over which the average is taken. AMD defines PPT for package
power, TDC for sustained current and EDC for peak current, with Precision
Boost 2 adjusting clocks continuously against all three plus temperature.
- Where experts disagree: whether single-number benchmark scores are useful
at all. One camp says a composite score is the only practical way for a
buyer to compare; the other says composites hide the fact that different
workloads rank chips in different orders. Both are right in their own
context, and the safe habit is to look at the sub-scores.
- Tools:
turbostat on Intel and zenmonitor or ryzen_monitor on AMD
show live per-core clock, package power and temperature. s-tui shows
throttling under load. stress-ng generates the load.
WORDS10.14.6 remember these#
- IPC — how much gets done per tick — instructions retired per cycle,
measured by hardware performance counters.
- CPI — the same thing upside down — cycles per instruction, the reciprocal
of IPC.
- Iron law — the only performance formula you need — time equals instruction
count times CPI times clock period.
- Retired instruction — one that really counted — an instruction that
committed architectural state, excluding discarded speculation.
- Turbo — a temporary speed rise — opportunistic frequency increase within
power, current and thermal limits.
- Thermal throttling — slowing down to survive — automatic clock reduction
when a temperature limit is reached.
- SPEC CPU 2017 — the serious benchmark — a suite of real applications with
mandatory disclosure of build settings.
10.98 Common wrong ideas#
- Wrong: a CPU understands your program. Right: it matches bit patterns to
control signals. Nothing is understood at any point.
- Wrong: a higher clock speed means a faster chip. Right: time equals
instruction count times cycles per instruction divided by clock speed, and
the middle term differs by more than 2 times between designs.
- Wrong: instructions and data are stored differently. Right: they are the
same bytes in the same memory. Only permission bits and where the program
counter points make the difference.
- Wrong: more cores means proportionally more speed. Right: Amdahl’s law
caps you at 1 divided by the serial fraction, so 5 percent serial code can
never go more than 20 times faster.
- Wrong: hyper-threading doubles performance. Right: it duplicates
architectural state only, and the typical real throughput gain is 10 to 30
percent, sometimes negative.
- Wrong: RISC chips are simple and CISC chips are complex. Right: both decode
into internal micro-operations and both run out of order. The real
surviving differences are decode complexity, memory operands and the
memory ordering model.
- Wrong: a deeper pipeline is always faster. Right: the Pentium 4 went from
20 to 31 stages, was cancelled at 3.8 GHz, and was beaten by a 14-stage
design in 2006.
- Wrong: microcode is what runs your program. Right: most instructions never
reach the microcode sequencer. It handles complicated cases and provides a
patching mechanism.
- Wrong: cache makes memory faster. Right: cache makes repeated and nearby
access faster. Truly random access over a large array gets almost no
benefit at all.
- Wrong: TDP is how much power the chip uses. Right: TDP is a cooling design
target. An AMD 170 W part draws up to about 230 W at the package.
10.99 Chapter summary in 20 lines#
- A CPU is one loop repeated forever: fetch, decode, execute, repeat.
- Its parts are a control unit, an ALU, a register file, a program counter,
an instruction register, caches, interconnect and a memory controller.
- The stored-program idea puts instructions and data in the same memory,
which is why one machine runs any program.
- That same idea permits buffer overflows, self-modifying code and JIT
compilation, and it is why the NX permission bit had to be invented.
- Real CPUs are modified Harvard: one memory underneath, split L1 instruction
and data caches on top.
- An instruction is a number split into fields: opcode, operands, immediates.
Addressing modes are the rules that turn those fields into an address.
- An ISA is a contract, not a design. x86-64 dates from 2000, ARM64 from
2011, RISC-V began at Berkeley in 2010 and is open.
- Registers are the fastest storage that exists because they are tiny, near,
many-ported and addressed by three to five bits.
- Microcode turns one complex instruction into several micro-operations, and
because it is stored rather than wired, it can be patched at boot.
- That patching mechanism carried the 2018 Spectre mitigations, which added
new control registers to a shipped architecture by firmware update.
- Pipelining overlaps stages so one instruction completes per cycle instead
of one every five, and it is why clock speeds could rise at all.
- Hazards are structural, data and control. Forwarding fixes most data
hazards; branch prediction fixes most control hazards.
- Very deep pipelines lose more on every mispredict than they gain in clock.
The 31-stage Pentium 4 Prescott is the proof.
- Superscalar, out-of-order execution, register renaming and the reorder
buffer extract parallelism while still showing software a tidy, ordered
machine.
- Modern TAGE-style branch predictors are right the great majority of the
time, and each miss still costs about 16 to 20 cycles.
- SIMD does one instruction to many numbers at once, from MMX in 1997 to
AVX-512 and Arm SVE. Fixed-function units such as AES-NI and NPUs go
further and put whole algorithms in silicon.
- Clocks stopped rising around 2005 when Dennard scaling ended, so chips
went multicore, and Amdahl’s law then set the ceiling.
- Cache coherence keeps cores agreeing about one address; memory ordering
and barriers govern the visible order across different addresses.
- Caches work only because programs reuse data and touch neighbours.
Sequential access can be 50 to 100 times faster than random access.
- Judge a CPU by instruction count times cycles per instruction times clock
period, measured on your own workload, not by the number on the box.