Performance Engineering

a hardware performance counter (PMU)

/ PMU: pee-em-YOO /

Suppose you want to know not just THAT a loop is slow, but WHY — is it waiting on memory, mispredicting branches, or just doing a lot of work? Ordinary timing cannot answer that. But the CPU itself keeps tiny tally counters of low-level events as it runs, and you can read them. Those counters are the hardware performance counters, and the unit on the chip that manages them is the Performance Monitoring Unit, or PMU.

Concretely, the PMU has a small set of physical counter registers. You program each one to count a specific microarchitectural event — for example total cycles, retired instructions, cache misses at a given level, branch mispredictions, or TLB misses — and then the hardware increments that register every time the event happens, with essentially no slowdown to your program. Software reads them at the start and end of a region and subtracts. Because there are only a few physical counters (often 4 to 8), tools that want many events at once MULTIPLEX them: they count event A for a while, switch to event B, and scale the results, which is accurate over a long run but adds a little noise. On Linux the standard interface is the perf_event_open() syscall, which the perf tool and libraries like PAPI sit on top of; the same counters can also drive a sampling profiler by firing an interrupt every N events (for example, sample once per million cache misses to see where the misses come from).

It matters because counters turn 'this is slow' into a diagnosis. Seeing a high cache-miss rate points you at memory layout; a high branch-misprediction rate points you at unpredictable control flow; a low instructions-per-cycle with few misses points you at a dependency or port bottleneck. The honest caveats: counter event names and meanings differ between CPU models and vendors, so a recipe from one chip may not transfer; reading them usually needs elevated permissions; and a raw count is only useful next to a baseline — 'one million cache misses' means nothing until you know it is one per loop iteration versus one per thousand.

$ perf stat ./myprog 12,400,103,221 cycles 8,201,556,090 instructions # 0.66 insn per cycle (IPC) 512,003,118 cache-misses # one miss every ~16 instructions -> memory-bound

perf stat reads the PMU: cycles, retired instructions, derived IPC, and cache misses — together a quick diagnosis of a memory-bound loop.

Counter results are NOT perfectly deterministic: 'skid' means the recorded instruction can be a few past the one that truly caused the event, multiplexing scales estimates, and identical-looking events differ across CPUs — treat counters as strong evidence, not exact ground truth.

Also called
performance monitoring unithardware counterperf event效能監測單元硬體計數器