CPU Microarchitecture for Programmers

instruction-level parallelism and execution ports

/ ILP -> eye-el-PEE /

Instruction-level parallelism, or ILP, is the amount of work in a stream of instructions that can be done at the same time because the instructions do not depend on each other. It is not something you turn on — it is a property of the code itself. Two adds that use unrelated inputs have parallelism (they can run together); an add whose input is the previous add's output has none (the second must wait). Modern CPUs are built to find and exploit whatever ILP your code contains, and your code's available ILP often sets its real speed.

The hardware that exploits ILP is the set of execution ports and functional units inside the core. A functional unit is a piece of hardware that performs a class of operation — an integer ALU, a floating-point multiplier, a load unit, a store unit, a branch unit. These units sit behind a handful of execution ports (a modern core might have 8 to 12), and each cycle the scheduler can dispatch one ready instruction to each port. So the core can, for example, do two loads, a store, two integer adds, and a multiply all in the same cycle — IF it has independent instructions ready and the right ports free. The mix of ports is asymmetric: there may be four units that can do a simple add but only one that can do division, so a loop bottlenecked on divides cannot go faster than that single port allows, no matter how much else is idle.

The practical upshot ties this whole field together. To run fast, code must EXPOSE ILP — supply enough independent instructions to keep the ports busy — and avoid two killers: long dependency chains (each instruction waiting on the last, leaving ports idle) and port pressure (too many instructions of one kind competing for too few units of that kind). This is why unrolling a loop with multiple independent accumulators speeds it up (it breaks the dependency chain so several ports work in parallel), and why a loop's true bottleneck is sometimes a single scarce port rather than total instruction count. ILP is the unifying reason superscalar width, out-of-order execution, and the reorder buffer all exist: they are the machinery for harvesting it.

sum = 0; for (i...) sum += a[i]; is one dependency chain — each add waits on the previous sum, so only one add-port is used at a time. Splitting into four accumulators (s0..s3, summed at the end) exposes four independent chains, and a core with enough add ports runs them in parallel for a large speedup.

Breaking one dependency chain into several independent ones is how you feed a superscalar core's ports.

ILP is a property of your code, not a CPU setting — a wide out-of-order core cannot manufacture parallelism that isn't there. A pure dependency chain runs at the same speed on the widest machine; sometimes the real limit is one scarce execution port, not the instruction count.

Also called
ILPfunctional unitsexecution ports指令級平行功能單元