21 Sep 2026
Performance Profiling 101
Performance Measurement Counter Events
MPKI: Misses per kilo instruction
STLB:L Second Translation Lookaside Buffer
Notes:
STLB metric being speculative refers to attempting to use the TLB before for an instruction that isn't guaranteed to retired
| Metric | Name | Description | Formula |
|---|---|---|---|
| L1MPKI | L1 cache true misses per kilo instruction | For retired demand loads | 1000 * MEM_LOAD_RETIRED.L1_MISS_PS / INST_RETIRED.ANY |
| L2MPKI | L2 cache true misses per kilo instruction | For retired demand loads | 1000 * MEM_LOAD_RETIRED.L2_MISS_PS / INST_RETIRED.ANY |
| L3MPKI | L3 cache true misses per kilo instruction | For retired demand loads | 1000 * MEM_LOAD_RETIRED.L3_MISS_PS / INST_RETIRED.ANY |
| Branch Mispr. Ratio | Ratio of all branches which mispredict | — | BR_MISP_RETIRED.ALL_BRANCHES / BR_INST_RETIRED.ALL_BRANCHES |
| STLB MPKI | 2nd Level TLB code speculative misses per kilo instruction | Misses of any page size that complete the page walk<br>STLB MPKI = STLB MPKI Load + STLB MPKI Store | 1000 * ITLB_MISSES.WALK_COMPLETED / INST_RETIRED.ANY |
| STLB MPKI Load | 2nd Level TLB data load speculative misses per kilo instruction | — | 1000 * DTLB_LD_MISSES.WALK_COMPLETED / INST_RETIRED.ANY |
| STLB MPKI Store | STLB data store speculative misses per kilo instruction | — | 1000 * DTLB_ST_MISSES.WALK_COMPLETED / INST_RETIRED.ANY |
| Load Miss Real Latency* | Average latency for L1 D-cache miss demand load operations | In core cycles | L1D_PEND_MISS.PENDING / MEM_LD_COMPLETED.L1_MISS_ANY |
| ILP | Instruction-level parallelism per core | Average number of µops executed when there is execution | UOPS_EXECUTED.THREAD / UOPS_EXECUTED.CORE_CYCLES_GE1<br>Divide by 2 if SMT is enabled |
| MLP | Memory-level parallelism per thread | MLP = Total cycles with ≥1 outstanding miss (weighted by count) / Total cycles with ≥1 outstanding mis.<br><br>In laymans terms, average number of outstanding L2/LLC misses per cycle, sampled over intervals where at least one miss is pending. | L1D_PEND_MISS.PENDING / L1D_PEND_MISS.PENDING_CYCLES |
| DRAM BW Use GB/sec* | Average external memory bandwidth use | For reads and writes. | (64 * (UNC_M_CAS_COUNT.RD + UNC_M_CAS_COUNT.WR) / 1GB) / Time |
| IpCall | Instructions per near call | This just refers to the average number of instructions before a call is made. So its a instruction per call frequency. | INST_RETIRED.ANY / BR_INST_RETIRED.NEAR_CALL |
| IpBranch | Instructions per branch | See above. | INST_RETIRED.ANY / BR_INST_RETIRED.ALL_BRANCHES |
| IpLoad | Instructions per load | See above. | INST_RETIRED.ANY / MEM_INST_RETIRED.ALL_LOADS_PS |
| IpStore | Instructions per store | See above. | INST_RETIRED.ANY / MEM_INST_RETIRED.ALL_STORES_PS |
| IpMispredict | Instructions per non-speculative branch misprediction | See above. | INST_RETIRED.ANY / BR_MISP_RETIRED.ALL_BRANCHES |
| IpFLOP | Instructions per FP operation | Lol<br>FLOP is floating point operation but unless you live under a rock you probably have heard of this horrifying term | |
| IpArith | Instructions per FP arithmetic instruction | <br>IpArith (all FP arithmetic instructions)<br>├── Scalar<br>│ ├── IpArith Scalar SP<br>│ └── IpArith Scalar DP<br>└── Vector (SIMD/packed)<br>├── AVX128 (128-bit vectors, AVX-encoded: e.g., VADDPS/VADDPD with xmm registers)<br>├── AVX256 (256-bit vectors, AVX-encoded: e.g., VADDPS/VADDPD with ymm registers)<br>└── (on AVX-512-capable systems) AVX512 (512-bit vectors, zmm registers)<br> |
|
| IpArith Scalar SP | Instructions per FP arithmetic scalar single-precision instruction | — | INST_RETIRED.ANY / FP_ARITH_INST.SCALAR_SINGLE |
| IpArith Scalar DP | Instructions per FP arithmetic scalar double-precision instruction | — | INST_RETIRED.ANY / FP_ARITH_INST.SCALAR_DOUBLE |
| Ip Arith AVX128 | Instructions per arithmetic AVX/SSE 128-bit instruction | AVX stands for advanced vector selections. | INST_RETIRED.ANY / (FP_ARITH_INST.128B_PACKED_DOUBLE + FP_ARITH_INST.128B_PACKED_SINGLE) |
| Ip Arith AVX256 | Instructions per arithmetic AVX 256-bit instruction | — | INST_RETIRED.ANY / (FP_ARITH_INST.256B_PACKED_DOUBLE + FP_ARITH_INST.256B_PACKED_SINGLE) |
| Ip SWPF | Instructions per software prefetch instruction | Of any type<br>Apparently too much software prefetch is actually a bad thing. | INST_RETIRED.ANY / SW_PREFETCH_ACCESS.T0:u0xF |
Caveats
- ILP and MLP metrics do not represent theoretical maximums for an application; rather they measure the actual ILP and MLP of an application on a given machine. On an ideal machine with infinite resources, these numbers would be higher.
- All metrics besides “DRAM BW Use” and “Load Miss Real Latency” are fractions; we can apply fairly straightforward reasoning to each of them to tell whether a specific metric is high or low. But to make sense of “DRAM BW Use” and “Load Miss Real Latency” metrics, we need to put them in context. For the former, we would like to know if a program saturates the memory bandwidth or not. The latter gives you an idea of the average cost of a cache miss, which is useless by itself unless you know the latencies of each component in the cache hierarchy.
MLC
$ sudo ./mlc --idle_latency -c0 -L -b10m
Intel(R) Memory Latency Checker - v3.10
Command line parameters: --idle_latency -c0 -L -b10m
Using buffer size of 10.000MiB
Each iteration took 31.1 base frequency clocks ( 12.5 ns)
idle-latency for idle simulation, load-latency to simulate there being other hungry processes, c0 to pin the measurement process to logical core 0. if you read the manual for this system, this is actually a Performance Core
$ sudo ./mlc --max_bandwidth -k0-15 -Y -L -u -b18m
Measuring Maximum Memory Bandwidths for the system
Bandwidths are in MB/sec (1 MB/sec = 1,000,000 Bytes/sec)
Using all the threads from each core if Hyper-threading is enabled
Using traffic with the following read-write ratios
ALL Reads
:
349670.42
On most Intel chips (the ones MLC targets), the L3 cache is inclusive of L1 and L2. That means: whatever is sitting in L1 or L2 is also required to have a copy sitting in L3 at the same time. L3 isn't a separate 18 MB pool that's short-changed by however much lives "up" in L1/L2 — it's a superset. If a cache line is in L1, it's guaranteed to also be in L3.
So wehn you choose -b18m since L3 capacity is 18mb then you'd effectively be measuring the L3 cache latency
(-u ensured when you're saying all threads in total use 18mb. if excluded, you'd measure 18mb * 8)
Practice
Here's what i identified as abnormal
Here's what I should have identified as well, highlighted in green
Also take care to note that these readings are actually averages that may change over the course of the program's lifecycle. E.g. imagine IPC is shown to be 2, which is actually the average of 2 different average performance modes where in one you have 1 IPC and in the other 3 IPC.
Cool stuff
If you go with a P/E core design, you can have the P cores L2 cache be in-core and E cores L2 cache be shared between multiple cores.