4 TopDown metrics break apart performance bottlenecks. Starting at level
5 1 it is typical to get metrics on retiring, bad speculation, frontend
6 bound, and backend bound. Higher levels provide more detail in to the
7 level 1 bottlenecks, such as at level 2: core bound, memory bound,
8 heavy operations, light operations, branch mispredicts, machine
9 clears, fetch latency and fetch bandwidth. For more details see [1][2][3].
11 perf stat --topdown implements this using available metrics that vary
14 % perf stat -a --topdown -I1000
15 # time % tma_retiring % tma_backend_bound % tma_frontend_bound % tma_bad_speculation
16 1.001141351 11.5 34.9 46.9 6.7
17 2.006141972 13.4 28.1 50.4 8.1
18 3.010162040 12.9 28.1 51.1 8.0
19 4.014009311 12.5 28.6 51.8 7.2
20 5.017838554 11.8 33.0 48.0 7.2
21 5.704818971 14.0 27.5 51.3 7.3
24 New Topdown features in Intel Ice Lake
25 ======================================
27 With Ice Lake CPUs the TopDown metrics are directly available as
28 fixed counters and do not require generic counters. This allows
29 to collect TopDown always in addition to other events.
31 Using TopDown through RDPMC in applications on Intel Ice Lake
32 =============================================================
34 For more fine grained measurements it can be useful to
35 access the new directly from user space. This is more complicated,
36 but drastically lowers overhead.
38 On Ice Lake, there is a new fixed counter 3: SLOTS, which reports
39 "pipeline SLOTS" (cycles multiplied by core issue width) and a
40 metric register that reports slots ratios for the different bottleneck
43 The metrics counter is CPU model specific and is not available on older
49 Library functions to do the functionality described below
50 is also available in libjevents [4]
52 The application opens a group with fixed counter 3 (SLOTS) and any
53 metric event, and allow user programs to read the performance counters.
55 Fixed counter 3 is mapped to a pseudo event event=0x00, umask=04,
56 so the perf_event_attr structure should be initialized with
57 { .config = 0x0400, .type = PERF_TYPE_RAW }
58 The metric events are mapped to the pseudo event event=0x00, umask=0x8X.
59 For example, the perf_event_attr structure can be initialized with
60 { .config = 0x8000, .type = PERF_TYPE_RAW } for Retiring metric event
61 The Fixed counter 3 must be the leader of the group.
63 #include <linux/perf_event.h>
65 #include <sys/syscall.h>
68 /* Provide own perf_event_open stub because glibc doesn't */
70 int perf_event_open(struct perf_event_attr *attr, pid_t pid,
71 int cpu, int group_fd, unsigned long flags)
73 return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
76 /* Open slots counter file descriptor for current task. */
77 struct perf_event_attr slots = {
78 .type = PERF_TYPE_RAW,
79 .size = sizeof(struct perf_event_attr),
84 int slots_fd = perf_event_open(&slots, 0, -1, -1, 0);
88 /* Memory mapping the fd permits _rdpmc calls from userspace */
89 void *slots_p = mmap(0, getpagesize(), PROT_READ, MAP_SHARED, slots_fd, 0);
94 * Open metrics event file descriptor for current task.
95 * Set slots event as the leader of the group.
97 struct perf_event_attr metrics = {
98 .type = PERF_TYPE_RAW,
99 .size = sizeof(struct perf_event_attr),
104 int metrics_fd = perf_event_open(&metrics, 0, -1, slots_fd, 0);
108 /* Memory mapping the fd permits _rdpmc calls from userspace */
109 void *metrics_p = mmap(0, getpagesize(), PROT_READ, MAP_SHARED, metrics_fd, 0);
113 Note: the file descriptors returned by the perf_event_open calls must be memory
114 mapped to permit calls to the _rdpmd instruction. Permission may also be granted
115 by writing the /sys/devices/cpu/rdpmc sysfs node.
117 The RDPMC instruction (or _rdpmc compiler intrinsic) can now be used
118 to read slots and the topdown metrics at different points of the program:
121 #include <x86intrin.h>
123 #define RDPMC_FIXED (1 << 30) /* return fixed counters */
124 #define RDPMC_METRIC (1 << 29) /* return metric counters */
126 #define FIXED_COUNTER_SLOTS 3
127 #define METRIC_COUNTER_TOPDOWN_L1_L2 0
129 static inline uint64_t read_slots(void)
131 return _rdpmc(RDPMC_FIXED | FIXED_COUNTER_SLOTS);
134 static inline uint64_t read_metrics(void)
136 return _rdpmc(RDPMC_METRIC | METRIC_COUNTER_TOPDOWN_L1_L2);
139 Then the program can be instrumented to read these metrics at different
142 It's not a good idea to do this with too short code regions,
143 as the parallelism and overlap in the CPU program execution will
144 cause too much measurement inaccuracy. For example instrumenting
145 individual basic blocks is definitely too fine grained.
147 _rdpmc calls should not be mixed with reading the metrics and slots counters
148 through system calls, as the kernel will reset these counters after each system
151 Decoding metrics values
152 =======================
154 The value reported by read_metrics() contains four 8 bit fields
155 that represent a scaled ratio that represent the Level 1 bottleneck.
156 All four fields add up to 0xff (= 100%)
158 The binary ratios in the metric value can be converted to float ratios:
160 #define GET_METRIC(m, i) (((m) >> (i*8)) & 0xff)
162 /* L1 Topdown metric events */
163 #define TOPDOWN_RETIRING(val) ((float)GET_METRIC(val, 0) / 0xff)
164 #define TOPDOWN_BAD_SPEC(val) ((float)GET_METRIC(val, 1) / 0xff)
165 #define TOPDOWN_FE_BOUND(val) ((float)GET_METRIC(val, 2) / 0xff)
166 #define TOPDOWN_BE_BOUND(val) ((float)GET_METRIC(val, 3) / 0xff)
169 * L2 Topdown metric events.
170 * Available on Sapphire Rapids and later platforms.
172 #define TOPDOWN_HEAVY_OPS(val) ((float)GET_METRIC(val, 4) / 0xff)
173 #define TOPDOWN_BR_MISPREDICT(val) ((float)GET_METRIC(val, 5) / 0xff)
174 #define TOPDOWN_FETCH_LAT(val) ((float)GET_METRIC(val, 6) / 0xff)
175 #define TOPDOWN_MEM_BOUND(val) ((float)GET_METRIC(val, 7) / 0xff)
177 and then converted to percent for printing.
179 The ratios in the metric accumulate for the time when the counter
180 is enabled. For measuring programs it is often useful to measure
181 specific sections. For this it is needed to deltas on metrics.
183 This can be done by scaling the metrics with the slots counter
184 read at the same time.
186 Then it's possible to take deltas of these slots counts
187 measured at different points, and determine the metrics
188 for that time period.
190 slots_a = read_slots();
191 metric_a = read_metrics();
193 ... larger code region ...
195 slots_b = read_slots()
196 metric_b = read_metrics()
198 # compute scaled metrics for measurement a
199 retiring_slots_a = GET_METRIC(metric_a, 0) * slots_a
200 bad_spec_slots_a = GET_METRIC(metric_a, 1) * slots_a
201 fe_bound_slots_a = GET_METRIC(metric_a, 2) * slots_a
202 be_bound_slots_a = GET_METRIC(metric_a, 3) * slots_a
204 # compute delta scaled metrics between b and a
205 retiring_slots = GET_METRIC(metric_b, 0) * slots_b - retiring_slots_a
206 bad_spec_slots = GET_METRIC(metric_b, 1) * slots_b - bad_spec_slots_a
207 fe_bound_slots = GET_METRIC(metric_b, 2) * slots_b - fe_bound_slots_a
208 be_bound_slots = GET_METRIC(metric_b, 3) * slots_b - be_bound_slots_a
210 Later the individual ratios of L1 metric events for the measurement period can
211 be recreated from these counts.
213 slots_delta = slots_b - slots_a
214 retiring_ratio = (float)retiring_slots / slots_delta
215 bad_spec_ratio = (float)bad_spec_slots / slots_delta
216 fe_bound_ratio = (float)fe_bound_slots / slots_delta
217 be_bound_ratio = (float)be_bound_slots / slota_delta
219 printf("Retiring %.2f%% Bad Speculation %.2f%% FE Bound %.2f%% BE Bound %.2f%%\n",
220 retiring_ratio * 100.,
221 bad_spec_ratio * 100.,
222 fe_bound_ratio * 100.,
223 be_bound_ratio * 100.);
225 The individual ratios of L2 metric events for the measurement period can be
226 recreated from L1 and L2 metric counters. (Available on Sapphire Rapids and
229 # compute scaled metrics for measurement a
230 heavy_ops_slots_a = GET_METRIC(metric_a, 4) * slots_a
231 br_mispredict_slots_a = GET_METRIC(metric_a, 5) * slots_a
232 fetch_lat_slots_a = GET_METRIC(metric_a, 6) * slots_a
233 mem_bound_slots_a = GET_METRIC(metric_a, 7) * slots_a
235 # compute delta scaled metrics between b and a
236 heavy_ops_slots = GET_METRIC(metric_b, 4) * slots_b - heavy_ops_slots_a
237 br_mispredict_slots = GET_METRIC(metric_b, 5) * slots_b - br_mispredict_slots_a
238 fetch_lat_slots = GET_METRIC(metric_b, 6) * slots_b - fetch_lat_slots_a
239 mem_bound_slots = GET_METRIC(metric_b, 7) * slots_b - mem_bound_slots_a
241 slots_delta = slots_b - slots_a
242 heavy_ops_ratio = (float)heavy_ops_slots / slots_delta
243 light_ops_ratio = retiring_ratio - heavy_ops_ratio;
245 br_mispredict_ratio = (float)br_mispredict_slots / slots_delta
246 machine_clears_ratio = bad_spec_ratio - br_mispredict_ratio;
248 fetch_lat_ratio = (float)fetch_lat_slots / slots_delta
249 fetch_bw_ratio = fe_bound_ratio - fetch_lat_ratio;
251 mem_bound_ratio = (float)mem_bound_slots / slota_delta
252 core_bound_ratio = be_bound_ratio - mem_bound_ratio;
254 printf("Heavy Operations %.2f%% Light Operations %.2f%% "
255 "Branch Mispredict %.2f%% Machine Clears %.2f%% "
256 "Fetch Latency %.2f%% Fetch Bandwidth %.2f%% "
257 "Mem Bound %.2f%% Core Bound %.2f%%\n",
258 heavy_ops_ratio * 100.,
259 light_ops_ratio * 100.,
260 br_mispredict_ratio * 100.,
261 machine_clears_ratio * 100.,
262 fetch_lat_ratio * 100.,
263 fetch_bw_ratio * 100.,
264 mem_bound_ratio * 100.,
265 core_bound_ratio * 100.);
267 Resetting metrics counters
268 ==========================
270 Since the individual metrics are only 8bit they lose precision for
271 short regions over time because the number of cycles covered by each
272 fraction bit shrinks. So the counters need to be reset regularly.
274 When using the kernel perf API the kernel resets on every read.
275 So as long as the reading is at reasonable intervals (every few
276 seconds) the precision is good.
278 When using perf stat it is recommended to always use the -I option,
279 with no longer interval than a few seconds
281 perf stat -I 1000 --topdown ...
283 For user programs using RDPMC directly the counter can
284 be reset explicitly using ioctl:
286 ioctl(perf_fd, PERF_EVENT_IOC_RESET, 0);
288 This "opens" a new measurement period.
290 A program using RDPMC for TopDown should schedule such a reset
291 regularly, as in every few seconds.
293 Limits on Intel Ice Lake
294 ========================
296 Four pseudo TopDown metric events are exposed for the end-users,
297 topdown-retiring, topdown-bad-spec, topdown-fe-bound and topdown-be-bound.
298 They can be used to collect the TopDown value under the following
300 - All the TopDown metric events must be in a group with the SLOTS event.
301 - The SLOTS event must be the leader of the group.
302 - The PERF_FORMAT_GROUP flag must be applied for each TopDown metric
305 The SLOTS event and the TopDown metric events can be counting members of
306 a sampling read group. Since the SLOTS event must be the leader of a TopDown
307 group, the second event of the group is the sampling event.
308 For example, perf record -e '{slots, $sampling_event, topdown-retiring}:S'
310 Extension on Intel Sapphire Rapids Server
311 =========================================
312 The metrics counter is extended to support TMA method level 2 metrics.
313 The lower half of the register is the TMA level 1 metrics (legacy).
314 The upper half is also divided into four 8-bit fields for the new level 2
315 metrics. Four more TopDown metric events are exposed for the end-users,
316 topdown-heavy-ops, topdown-br-mispredict, topdown-fetch-lat and
319 Each of the new level 2 metrics in the upper half is a subset of the
320 corresponding level 1 metric in the lower half. Software can deduce the
321 other four level 2 metrics by subtracting corresponding metrics as below.
323 Light_Operations = Retiring - Heavy_Operations
324 Machine_Clears = Bad_Speculation - Branch_Mispredicts
325 Fetch_Bandwidth = Frontend_Bound - Fetch_Latency
326 Core_Bound = Backend_Bound - Memory_Bound
331 TPEBS (Timed PEBS) is one of the new Intel PMU features provided since Granite
332 Rapids microarchitecture. The TPEBS feature adds a 16 bit retire_latency field
333 in the Basic Info group of the PEBS record. It records the Core cycles since the
334 retirement of the previous instruction to the retirement of current instruction.
335 Please refer to Section 8.4.1 of "Intel® Architecture Instruction Set Extensions
336 Programming Reference" for more details about this feature. Because this feature
337 extends PEBS record, sampling with weight option is required to get the
338 retire_latency value.
340 perf record -e event_name -W ...
342 In the most recent release of TMA, the metrics begin to use event retire_latency
343 values in some of the metrics’ formulas on processors that support TPEBS feature.
344 For previous generations that do not support TPEBS, the values are static and
345 predefined per processor family by the hardware architects. Due to the diversity
346 of workloads in execution environments, retire_latency values measured at real
347 time are more accurate. Therefore, new TMA metrics that use TPEBS will provide
348 more accurate performance analysis results.
350 To support TPEBS in TMA metrics, a new modifier :R on event is added. Perf would
351 capture retire_latency value of required events(event with :R in metric formula)
352 with perf record. The retire_latency value would be used in metric calculation.
353 Currently, this feature is supported through perf stat
355 perf stat -M metric_name --record-tpebs ...
359 [1] https://software.intel.com/en-us/top-down-microarchitecture-analysis-method-win
360 [2] https://sites.google.com/site/analysismethods/yasin-pubs
361 [3] https://perf.wiki.kernel.org/index.php/Top-Down_Analysis
362 [4] https://github.com/andikleen/pmu-tools/tree/master/jevents