out-of-order execution and register renaming
Imagine a chef handed a list of tasks in order, where task 3 is 'wait for the oven' and tasks 4 and 5 are independent chopping that could be done meanwhile. A foolish chef stands idle at the oven; a smart one does the chopping while waiting, then comes back. Out-of-order execution is the CPU being the smart chef: instead of executing instructions strictly in program order, it executes each one as soon as its inputs are ready, while a stalled instruction (say, one waiting on a DRAM load) is parked aside.
The core problem is that program order creates false dependencies. If instruction A writes register rax and a later, unrelated instruction B also writes rax, B cannot run before A using the same physical register — even though they share no real data, only the name rax. Register renaming dissolves this: the CPU has far more physical registers than the handful of architectural names (rax, rbx, and so on) the instruction set exposes, and it maps each WRITE of an architectural register to a fresh physical register. Now A and B write different physical registers and can run in any order. Combined with a scheduler that watches which operands are ready, the core executes instructions out of program order, draining stalls by running independent work around them. Crucially, results are still RETIRED (made architecturally visible) in original program order, so the program behaves exactly as written.
Why this is the engine of modern performance: a single DRAM miss costs hundreds of cycles, and out-of-order execution lets the CPU keep doing useful, independent work during that wait instead of freezing — it hides memory latency. The window of instructions it can look ahead over is finite (bounded by the reorder buffer), so it cannot hide unlimited latency, and long dependency chains still serialize. But this machinery, invisible in your source, is why the same instructions can run two to ten times faster on a wide out-of-order core than on a simple in-order one.
x = arr[i]; /* misses to DRAM, 300 cycles */ y = a + b; z = c * d; The two arithmetic ops do not depend on x, so an out-of-order core renames their outputs and executes them during the long load, retiring all three in order once x finally arrives.
The classic win: useful arithmetic runs in the shadow of a long memory stall instead of waiting for it.
Out-of-order is a hardware reordering invisible to your program's observable results — but it does NOT relax the memory model. Inter-thread ordering still needs atomics and fences; OoO never makes a data race safe.