1 Using TopDown metrics in user space
2 -----------------------------------
4 Intel CPUs (since Sandy Bridge and Silvermont) support a TopDown
5 methodology to break down CPU pipeline execution into 4 bottlenecks:
6 frontend bound, backend bound, bad speculation, retiring.
8 For more details on Topdown see [1][5]
10 Traditionally this was implemented by events in generic counters
11 and specific formulas to compute the bottlenecks.
13 perf stat --topdown implements this.
15 Full Top Down includes more levels that can break down the
16 bottlenecks further. This is not directly implemented in perf,
17 but available in other tools that can run on top of perf,
18 such as toplev[2] or vtune[3]
20 New Topdown features in Ice Lake
21 ===============================
23 With Ice Lake CPUs the TopDown metrics are directly available as
24 fixed counters and do not require generic counters. This allows
25 to collect TopDown always in addition to other events.
27 % perf stat -a --topdown -I1000
28 # time retiring bad speculation frontend bound backend bound
29 1.001281330 23.0% 15.3% 29.6% 32.1%
30 2.003009005 5.0% 6.8% 46.6% 41.6%
31 3.004646182 6.7% 6.7% 46.0% 40.6%
32 4.006326375 5.0% 6.4% 47.6% 41.0%
33 5.007991804 5.1% 6.3% 46.3% 42.3%
34 6.009626773 6.2% 7.1% 47.3% 39.3%
35 7.011296356 4.7% 6.7% 46.2% 42.4%
36 8.012951831 4.7% 6.7% 47.5% 41.1%
39 This also enables measuring TopDown per thread/process instead
42 Using TopDown through RDPMC in applications on Ice Lake
43 ======================================================
45 For more fine grained measurements it can be useful to
46 access the new directly from user space. This is more complicated,
47 but drastically lowers overhead.
49 On Ice Lake, there is a new fixed counter 3: SLOTS, which reports
50 "pipeline SLOTS" (cycles multiplied by core issue width) and a
51 metric register that reports slots ratios for the different bottleneck
54 The metrics counter is CPU model specific and is not available on older
60 Library functions to do the functionality described below
61 is also available in libjevents [4]
63 The application opens a group with fixed counter 3 (SLOTS) and any
64 metric event, and allow user programs to read the performance counters.
66 Fixed counter 3 is mapped to a pseudo event event=0x00, umask=04,
67 so the perf_event_attr structure should be initialized with
68 { .config = 0x0400, .type = PERF_TYPE_RAW }
69 The metric events are mapped to the pseudo event event=0x00, umask=0x8X.
70 For example, the perf_event_attr structure can be initialized with
71 { .config = 0x8000, .type = PERF_TYPE_RAW } for Retiring metric event
72 The Fixed counter 3 must be the leader of the group.
74 #include <linux/perf_event.h>
76 #include <sys/syscall.h>
79 /* Provide own perf_event_open stub because glibc doesn't */
81 int perf_event_open(struct perf_event_attr *attr, pid_t pid,
82 int cpu, int group_fd, unsigned long flags)
84 return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
87 /* Open slots counter file descriptor for current task. */
88 struct perf_event_attr slots = {
89 .type = PERF_TYPE_RAW,
90 .size = sizeof(struct perf_event_attr),
95 int slots_fd = perf_event_open(&slots, 0, -1, -1, 0);
99 /* Memory mapping the fd permits _rdpmc calls from userspace */
100 void *slots_p = mmap(0, getpagesize(), PROT_READ, MAP_SHARED, slots_fd, 0);
105 * Open metrics event file descriptor for current task.
106 * Set slots event as the leader of the group.
108 struct perf_event_attr metrics = {
109 .type = PERF_TYPE_RAW,
110 .size = sizeof(struct perf_event_attr),
115 int metrics_fd = perf_event_open(&metrics, 0, -1, slots_fd, 0);
119 /* Memory mapping the fd permits _rdpmc calls from userspace */
120 void *metrics_p = mmap(0, getpagesize(), PROT_READ, MAP_SHARED, metrics_fd, 0);
124 Note: the file descriptors returned by the perf_event_open calls must be memory
125 mapped to permit calls to the _rdpmd instruction. Permission may also be granted
126 by writing the /sys/devices/cpu/rdpmc sysfs node.
128 The RDPMC instruction (or _rdpmc compiler intrinsic) can now be used
129 to read slots and the topdown metrics at different points of the program:
132 #include <x86intrin.h>
134 #define RDPMC_FIXED (1 << 30) /* return fixed counters */
135 #define RDPMC_METRIC (1 << 29) /* return metric counters */
137 #define FIXED_COUNTER_SLOTS 3
138 #define METRIC_COUNTER_TOPDOWN_L1_L2 0
140 static inline uint64_t read_slots(void)
142 return _rdpmc(RDPMC_FIXED | FIXED_COUNTER_SLOTS);
145 static inline uint64_t read_metrics(void)
147 return _rdpmc(RDPMC_METRIC | METRIC_COUNTER_TOPDOWN_L1_L2);
150 Then the program can be instrumented to read these metrics at different
153 It's not a good idea to do this with too short code regions,
154 as the parallelism and overlap in the CPU program execution will
155 cause too much measurement inaccuracy. For example instrumenting
156 individual basic blocks is definitely too fine grained.
158 _rdpmc calls should not be mixed with reading the metrics and slots counters
159 through system calls, as the kernel will reset these counters after each system
162 Decoding metrics values
163 =======================
165 The value reported by read_metrics() contains four 8 bit fields
166 that represent a scaled ratio that represent the Level 1 bottleneck.
167 All four fields add up to 0xff (= 100%)
169 The binary ratios in the metric value can be converted to float ratios:
171 #define GET_METRIC(m, i) (((m) >> (i*8)) & 0xff)
173 /* L1 Topdown metric events */
174 #define TOPDOWN_RETIRING(val) ((float)GET_METRIC(val, 0) / 0xff)
175 #define TOPDOWN_BAD_SPEC(val) ((float)GET_METRIC(val, 1) / 0xff)
176 #define TOPDOWN_FE_BOUND(val) ((float)GET_METRIC(val, 2) / 0xff)
177 #define TOPDOWN_BE_BOUND(val) ((float)GET_METRIC(val, 3) / 0xff)
180 * L2 Topdown metric events.
181 * Available on Sapphire Rapids and later platforms.
183 #define TOPDOWN_HEAVY_OPS(val) ((float)GET_METRIC(val, 4) / 0xff)
184 #define TOPDOWN_BR_MISPREDICT(val) ((float)GET_METRIC(val, 5) / 0xff)
185 #define TOPDOWN_FETCH_LAT(val) ((float)GET_METRIC(val, 6) / 0xff)
186 #define TOPDOWN_MEM_BOUND(val) ((float)GET_METRIC(val, 7) / 0xff)
188 and then converted to percent for printing.
190 The ratios in the metric accumulate for the time when the counter
191 is enabled. For measuring programs it is often useful to measure
192 specific sections. For this it is needed to deltas on metrics.
194 This can be done by scaling the metrics with the slots counter
195 read at the same time.
197 Then it's possible to take deltas of these slots counts
198 measured at different points, and determine the metrics
199 for that time period.
201 slots_a = read_slots();
202 metric_a = read_metrics();
204 ... larger code region ...
206 slots_b = read_slots()
207 metric_b = read_metrics()
209 # compute scaled metrics for measurement a
210 retiring_slots_a = GET_METRIC(metric_a, 0) * slots_a
211 bad_spec_slots_a = GET_METRIC(metric_a, 1) * slots_a
212 fe_bound_slots_a = GET_METRIC(metric_a, 2) * slots_a
213 be_bound_slots_a = GET_METRIC(metric_a, 3) * slots_a
215 # compute delta scaled metrics between b and a
216 retiring_slots = GET_METRIC(metric_b, 0) * slots_b - retiring_slots_a
217 bad_spec_slots = GET_METRIC(metric_b, 1) * slots_b - bad_spec_slots_a
218 fe_bound_slots = GET_METRIC(metric_b, 2) * slots_b - fe_bound_slots_a
219 be_bound_slots = GET_METRIC(metric_b, 3) * slots_b - be_bound_slots_a
221 Later the individual ratios of L1 metric events for the measurement period can
222 be recreated from these counts.
224 slots_delta = slots_b - slots_a
225 retiring_ratio = (float)retiring_slots / slots_delta
226 bad_spec_ratio = (float)bad_spec_slots / slots_delta
227 fe_bound_ratio = (float)fe_bound_slots / slots_delta
228 be_bound_ratio = (float)be_bound_slots / slota_delta
230 printf("Retiring %.2f%% Bad Speculation %.2f%% FE Bound %.2f%% BE Bound %.2f%%\n",
231 retiring_ratio * 100.,
232 bad_spec_ratio * 100.,
233 fe_bound_ratio * 100.,
234 be_bound_ratio * 100.);
236 The individual ratios of L2 metric events for the measurement period can be
237 recreated from L1 and L2 metric counters. (Available on Sapphire Rapids and
240 # compute scaled metrics for measurement a
241 heavy_ops_slots_a = GET_METRIC(metric_a, 4) * slots_a
242 br_mispredict_slots_a = GET_METRIC(metric_a, 5) * slots_a
243 fetch_lat_slots_a = GET_METRIC(metric_a, 6) * slots_a
244 mem_bound_slots_a = GET_METRIC(metric_a, 7) * slots_a
246 # compute delta scaled metrics between b and a
247 heavy_ops_slots = GET_METRIC(metric_b, 4) * slots_b - heavy_ops_slots_a
248 br_mispredict_slots = GET_METRIC(metric_b, 5) * slots_b - br_mispredict_slots_a
249 fetch_lat_slots = GET_METRIC(metric_b, 6) * slots_b - fetch_lat_slots_a
250 mem_bound_slots = GET_METRIC(metric_b, 7) * slots_b - mem_bound_slots_a
252 slots_delta = slots_b - slots_a
253 heavy_ops_ratio = (float)heavy_ops_slots / slots_delta
254 light_ops_ratio = retiring_ratio - heavy_ops_ratio;
256 br_mispredict_ratio = (float)br_mispredict_slots / slots_delta
257 machine_clears_ratio = bad_spec_ratio - br_mispredict_ratio;
259 fetch_lat_ratio = (float)fetch_lat_slots / slots_delta
260 fetch_bw_ratio = fe_bound_ratio - fetch_lat_ratio;
262 mem_bound_ratio = (float)mem_bound_slots / slota_delta
263 core_bound_ratio = be_bound_ratio - mem_bound_ratio;
265 printf("Heavy Operations %.2f%% Light Operations %.2f%% "
266 "Branch Mispredict %.2f%% Machine Clears %.2f%% "
267 "Fetch Latency %.2f%% Fetch Bandwidth %.2f%% "
268 "Mem Bound %.2f%% Core Bound %.2f%%\n",
269 heavy_ops_ratio * 100.,
270 light_ops_ratio * 100.,
271 br_mispredict_ratio * 100.,
272 machine_clears_ratio * 100.,
273 fetch_lat_ratio * 100.,
274 fetch_bw_ratio * 100.,
275 mem_bound_ratio * 100.,
276 core_bound_ratio * 100.);
278 Resetting metrics counters
279 ==========================
281 Since the individual metrics are only 8bit they lose precision for
282 short regions over time because the number of cycles covered by each
283 fraction bit shrinks. So the counters need to be reset regularly.
285 When using the kernel perf API the kernel resets on every read.
286 So as long as the reading is at reasonable intervals (every few
287 seconds) the precision is good.
289 When using perf stat it is recommended to always use the -I option,
290 with no longer interval than a few seconds
292 perf stat -I 1000 --topdown ...
294 For user programs using RDPMC directly the counter can
295 be reset explicitly using ioctl:
297 ioctl(perf_fd, PERF_EVENT_IOC_RESET, 0);
299 This "opens" a new measurement period.
301 A program using RDPMC for TopDown should schedule such a reset
302 regularly, as in every few seconds.
307 Four pseudo TopDown metric events are exposed for the end-users,
308 topdown-retiring, topdown-bad-spec, topdown-fe-bound and topdown-be-bound.
309 They can be used to collect the TopDown value under the following
311 - All the TopDown metric events must be in a group with the SLOTS event.
312 - The SLOTS event must be the leader of the group.
313 - The PERF_FORMAT_GROUP flag must be applied for each TopDown metric
316 The SLOTS event and the TopDown metric events can be counting members of
317 a sampling read group. Since the SLOTS event must be the leader of a TopDown
318 group, the second event of the group is the sampling event.
319 For example, perf record -e '{slots, $sampling_event, topdown-retiring}:S'
321 Extension on Sapphire Rapids Server
322 ===================================
323 The metrics counter is extended to support TMA method level 2 metrics.
324 The lower half of the register is the TMA level 1 metrics (legacy).
325 The upper half is also divided into four 8-bit fields for the new level 2
326 metrics. Four more TopDown metric events are exposed for the end-users,
327 topdown-heavy-ops, topdown-br-mispredict, topdown-fetch-lat and
330 Each of the new level 2 metrics in the upper half is a subset of the
331 corresponding level 1 metric in the lower half. Software can deduce the
332 other four level 2 metrics by subtracting corresponding metrics as below.
334 Light_Operations = Retiring - Heavy_Operations
335 Machine_Clears = Bad_Speculation - Branch_Mispredicts
336 Fetch_Bandwidth = Frontend_Bound - Fetch_Latency
337 Core_Bound = Backend_Bound - Memory_Bound
340 [1] https://software.intel.com/en-us/top-down-microarchitecture-analysis-method-win
341 [2] https://github.com/andikleen/pmu-tools/wiki/toplev-manual
342 [3] https://software.intel.com/en-us/intel-vtune-amplifier-xe
343 [4] https://github.com/andikleen/pmu-tools/tree/master/jevents
344 [5] https://sites.google.com/site/analysismethods/yasin-pubs