Table of Contents
perf Metrics
Key performance metrics derived from hardware counters reveal what the CPU is doing and where it's spending time.
Instructions per cycle (IPC) is the most important metric—it tells you how efficiently the CPU is using its execution pipeline. Calculate it as instructions / cycles.
IPC = 2.5 → CPU is executing 2.5 instructions per cycle (good utilization) IPC = 1.0 → CPU is executing 1 instruction per cycle (moderate stall) IPC = 0.5 → CPU is executing 0.5 instructions per cycle (significant stall)
Modern superscalar CPUs can retire 3-4 instructions per cycle at theoretical peak (depends on architecture). An IPC above 2 is good; below 1 almost always indicates memory stalls (CPU is waiting for DRAM).
Cache miss rate shows the fraction of cache accesses that missed and had to fetch from memory. Calculate as cache-misses / cache-references:
1% miss rate → excellent (cache is working well) 5% miss rate → acceptable (some misses but mostly cache hits) 30% miss rate → poor (CPU is waiting for memory frequently)
High cache miss rates combined with low IPC confirms memory-bound execution. Typical optimizations: improve data locality, prefetch strategically, or increase compute per loaded cacheline.
Branch misprediction rate is branch-misses / branches. Modern CPUs have sophisticated branch predictors but they can still miss:
2% miss rate → typical for well-predicted code 10% miss rate → irregular branch patterns, consider restructuring
Data-dependent branches are hard to predict. Unpredictable branches cause pipeline flushes and stalls. Optimizations: branch on frequently-taken paths, use conditional moves, or restructure to avoid data-dependent branches.
Stalled cycles (frontend vs backend) reveal where the CPU is waiting:
stalled-cycles-frontend CPU waiting for instruction fetch (rare) stalled-cycles-backend CPU waiting for execution (memory, data hazards)
High backend stalls with low IPC usually means memory waits. High frontend stalls indicate instruction fetch bottlenecks (usually from branch mispredictions or instruction cache misses).
Cycles per instruction (CPI) is the inverse of IPC—sometimes more intuitive to reason about:
CPI = cycles / instructions = 1 / IPC
CPI of 2 means it takes 2 cycles per instruction on average (equivalent to IPC of 0.5).
System utilization shows CPU efficiency. For a single-threaded program:
task-clock (ms) / wall-clock time (ms) = CPUs utilized
If your program runs for 4 seconds and task-clock is 4000ms, that's 1 CPU utilized (100%). If it's 1000ms, that's 0.25 CPUs (25%)—the program is not keeping the CPU busy (waiting on I/O or synchronization).
Quick profiling checklist: 1. Is IPC low (< 1)? Memory bound—optimize memory access. 2. Is cache miss rate high (> 10%)? Memory access pattern issue—improve locality. 3. Is branch misprediction rate high (> 5%)? Branch prediction issue—restructure loops. 4. Is CPU utilization low (< 75%)? I/O bound or synchronization—check for waits.
