1Using TopDown metrics in user space
2-----------------------------------
3
4Intel CPUs (since Sandy Bridge and Silvermont) support a TopDown
5methology to break down CPU pipeline execution into 4 bottlenecks:
6frontend bound, backend bound, bad speculation, retiring.
7
8For more details on Topdown see [1][5]
9
10Traditionally this was implemented by events in generic counters
11and specific formulas to compute the bottlenecks.
12
13perf stat --topdown implements this.
14
15Full Top Down includes more levels that can break down the
16bottlenecks further. This is not directly implemented in perf,
17but available in other tools that can run on top of perf,
18such as toplev[2] or vtune[3]
19
20New Topdown features in Ice Lake
21===============================
22
23With Ice Lake CPUs the TopDown metrics are directly available as
24fixed counters and do not require generic counters. This allows
25to collect TopDown always in addition to other events.
26
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%
37...
38
39This also enables measuring TopDown per thread/process instead
40of only per core.
41
42Using TopDown through RDPMC in applications on Ice Lake
43======================================================
44
45For more fine grained measurements it can be useful to
46access the new  directly from user space. This is more complicated,
47but drastically lowers overhead.
48
49On Ice Lake, there is a new fixed counter 3: SLOTS, which reports
50"pipeline SLOTS" (cycles multiplied by core issue width) and a
51metric register that reports slots ratios for the different bottleneck
52categories.
53
54The metrics counter is CPU model specific and is not available on older
55CPUs.
56
57Example code
58============
59
60Library functions to do the functionality described below
61is also available in libjevents [4]
62
63The application opens a group with fixed counter 3 (SLOTS) and any
64metric event, and allow user programs to read the performance counters.
65
66Fixed counter 3 is mapped to a pseudo event event=0x00, umask=04,
67so the perf_event_attr structure should be initialized with
68{ .config = 0x0400, .type = PERF_TYPE_RAW }
69The metric events are mapped to the pseudo event event=0x00, umask=0x8X.
70For example, the perf_event_attr structure can be initialized with
71{ .config = 0x8000, .type = PERF_TYPE_RAW } for Retiring metric event
72The Fixed counter 3 must be the leader of the group.
73
74#include <linux/perf_event.h>
75#include <sys/syscall.h>
76#include <unistd.h>
77
78/* Provide own perf_event_open stub because glibc doesn't */
79__attribute__((weak))
80int perf_event_open(struct perf_event_attr *attr, pid_t pid,
81		    int cpu, int group_fd, unsigned long flags)
82{
83	return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
84}
85
86/* Open slots counter file descriptor for current task. */
87struct perf_event_attr slots = {
88	.type = PERF_TYPE_RAW,
89	.size = sizeof(struct perf_event_attr),
90	.config = 0x400,
91	.exclude_kernel = 1,
92};
93
94int slots_fd = perf_event_open(&slots, 0, -1, -1, 0);
95if (slots_fd < 0)
96	... error ...
97
98/*
99 * Open metrics event file descriptor for current task.
100 * Set slots event as the leader of the group.
101 */
102struct perf_event_attr metrics = {
103	.type = PERF_TYPE_RAW,
104	.size = sizeof(struct perf_event_attr),
105	.config = 0x8000,
106	.exclude_kernel = 1,
107};
108
109int metrics_fd = perf_event_open(&metrics, 0, -1, slots_fd, 0);
110if (metrics_fd < 0)
111	... error ...
112
113
114The RDPMC instruction (or _rdpmc compiler intrinsic) can now be used
115to read slots and the topdown metrics at different points of the program:
116
117#include <stdint.h>
118#include <x86intrin.h>
119
120#define RDPMC_FIXED	(1 << 30)	/* return fixed counters */
121#define RDPMC_METRIC	(1 << 29)	/* return metric counters */
122
123#define FIXED_COUNTER_SLOTS		3
124#define METRIC_COUNTER_TOPDOWN_L1_L2	0
125
126static inline uint64_t read_slots(void)
127{
128	return _rdpmc(RDPMC_FIXED | FIXED_COUNTER_SLOTS);
129}
130
131static inline uint64_t read_metrics(void)
132{
133	return _rdpmc(RDPMC_METRIC | METRIC_COUNTER_TOPDOWN_L1_L2);
134}
135
136Then the program can be instrumented to read these metrics at different
137points.
138
139It's not a good idea to do this with too short code regions,
140as the parallelism and overlap in the CPU program execution will
141cause too much measurement inaccuracy. For example instrumenting
142individual basic blocks is definitely too fine grained.
143
144Decoding metrics values
145=======================
146
147The value reported by read_metrics() contains four 8 bit fields
148that represent a scaled ratio that represent the Level 1 bottleneck.
149All four fields add up to 0xff (= 100%)
150
151The binary ratios in the metric value can be converted to float ratios:
152
153#define GET_METRIC(m, i) (((m) >> (i*8)) & 0xff)
154
155/* L1 Topdown metric events */
156#define TOPDOWN_RETIRING(val)	((float)GET_METRIC(val, 0) / 0xff)
157#define TOPDOWN_BAD_SPEC(val)	((float)GET_METRIC(val, 1) / 0xff)
158#define TOPDOWN_FE_BOUND(val)	((float)GET_METRIC(val, 2) / 0xff)
159#define TOPDOWN_BE_BOUND(val)	((float)GET_METRIC(val, 3) / 0xff)
160
161/*
162 * L2 Topdown metric events.
163 * Available on Sapphire Rapids and later platforms.
164 */
165#define TOPDOWN_HEAVY_OPS(val)		((float)GET_METRIC(val, 4) / 0xff)
166#define TOPDOWN_BR_MISPREDICT(val)	((float)GET_METRIC(val, 5) / 0xff)
167#define TOPDOWN_FETCH_LAT(val)		((float)GET_METRIC(val, 6) / 0xff)
168#define TOPDOWN_MEM_BOUND(val)		((float)GET_METRIC(val, 7) / 0xff)
169
170and then converted to percent for printing.
171
172The ratios in the metric accumulate for the time when the counter
173is enabled. For measuring programs it is often useful to measure
174specific sections. For this it is needed to deltas on metrics.
175
176This can be done by scaling the metrics with the slots counter
177read at the same time.
178
179Then it's possible to take deltas of these slots counts
180measured at different points, and determine the metrics
181for that time period.
182
183	slots_a = read_slots();
184	metric_a = read_metrics();
185
186	... larger code region ...
187
188	slots_b = read_slots()
189	metric_b = read_metrics()
190
191	# compute scaled metrics for measurement a
192	retiring_slots_a = GET_METRIC(metric_a, 0) * slots_a
193	bad_spec_slots_a = GET_METRIC(metric_a, 1) * slots_a
194	fe_bound_slots_a = GET_METRIC(metric_a, 2) * slots_a
195	be_bound_slots_a = GET_METRIC(metric_a, 3) * slots_a
196
197	# compute delta scaled metrics between b and a
198	retiring_slots = GET_METRIC(metric_b, 0) * slots_b - retiring_slots_a
199	bad_spec_slots = GET_METRIC(metric_b, 1) * slots_b - bad_spec_slots_a
200	fe_bound_slots = GET_METRIC(metric_b, 2) * slots_b - fe_bound_slots_a
201	be_bound_slots = GET_METRIC(metric_b, 3) * slots_b - be_bound_slots_a
202
203Later the individual ratios of L1 metric events for the measurement period can
204be recreated from these counts.
205
206	slots_delta = slots_b - slots_a
207	retiring_ratio = (float)retiring_slots / slots_delta
208	bad_spec_ratio = (float)bad_spec_slots / slots_delta
209	fe_bound_ratio = (float)fe_bound_slots / slots_delta
210	be_bound_ratio = (float)be_bound_slots / slota_delta
211
212	printf("Retiring %.2f%% Bad Speculation %.2f%% FE Bound %.2f%% BE Bound %.2f%%\n",
213		retiring_ratio * 100.,
214		bad_spec_ratio * 100.,
215		fe_bound_ratio * 100.,
216		be_bound_ratio * 100.);
217
218The individual ratios of L2 metric events for the measurement period can be
219recreated from L1 and L2 metric counters. (Available on Sapphire Rapids and
220later platforms)
221
222	# compute scaled metrics for measurement a
223	heavy_ops_slots_a = GET_METRIC(metric_a, 4) * slots_a
224	br_mispredict_slots_a = GET_METRIC(metric_a, 5) * slots_a
225	fetch_lat_slots_a = GET_METRIC(metric_a, 6) * slots_a
226	mem_bound_slots_a = GET_METRIC(metric_a, 7) * slots_a
227
228	# compute delta scaled metrics between b and a
229	heavy_ops_slots = GET_METRIC(metric_b, 4) * slots_b - heavy_ops_slots_a
230	br_mispredict_slots = GET_METRIC(metric_b, 5) * slots_b - br_mispredict_slots_a
231	fetch_lat_slots = GET_METRIC(metric_b, 6) * slots_b - fetch_lat_slots_a
232	mem_bound_slots = GET_METRIC(metric_b, 7) * slots_b - mem_bound_slots_a
233
234	slots_delta = slots_b - slots_a
235	heavy_ops_ratio = (float)heavy_ops_slots / slots_delta
236	light_ops_ratio = retiring_ratio - heavy_ops_ratio;
237
238	br_mispredict_ratio = (float)br_mispredict_slots / slots_delta
239	machine_clears_ratio = bad_spec_ratio - br_mispredict_ratio;
240
241	fetch_lat_ratio = (float)fetch_lat_slots / slots_delta
242	fetch_bw_ratio = fe_bound_ratio - fetch_lat_ratio;
243
244	mem_bound_ratio = (float)mem_bound_slots / slota_delta
245	core_bound_ratio = be_bound_ratio - mem_bound_ratio;
246
247	printf("Heavy Operations %.2f%% Light Operations %.2f%% "
248	       "Branch Mispredict %.2f%% Machine Clears %.2f%% "
249	       "Fetch Latency %.2f%% Fetch Bandwidth %.2f%% "
250	       "Mem Bound %.2f%% Core Bound %.2f%%\n",
251		heavy_ops_ratio * 100.,
252		light_ops_ratio * 100.,
253		br_mispredict_ratio * 100.,
254		machine_clears_ratio * 100.,
255		fetch_lat_ratio * 100.,
256		fetch_bw_ratio * 100.,
257		mem_bound_ratio * 100.,
258		core_bound_ratio * 100.);
259
260Resetting metrics counters
261==========================
262
263Since the individual metrics are only 8bit they lose precision for
264short regions over time because the number of cycles covered by each
265fraction bit shrinks. So the counters need to be reset regularly.
266
267When using the kernel perf API the kernel resets on every read.
268So as long as the reading is at reasonable intervals (every few
269seconds) the precision is good.
270
271When using perf stat it is recommended to always use the -I option,
272with no longer interval than a few seconds
273
274	perf stat -I 1000 --topdown ...
275
276For user programs using RDPMC directly the counter can
277be reset explicitly using ioctl:
278
279	ioctl(perf_fd, PERF_EVENT_IOC_RESET, 0);
280
281This "opens" a new measurement period.
282
283A program using RDPMC for TopDown should schedule such a reset
284regularly, as in every few seconds.
285
286Limits on Ice Lake
287==================
288
289Four pseudo TopDown metric events are exposed for the end-users,
290topdown-retiring, topdown-bad-spec, topdown-fe-bound and topdown-be-bound.
291They can be used to collect the TopDown value under the following
292rules:
293- All the TopDown metric events must be in a group with the SLOTS event.
294- The SLOTS event must be the leader of the group.
295- The PERF_FORMAT_GROUP flag must be applied for each TopDown metric
296  events
297
298The SLOTS event and the TopDown metric events can be counting members of
299a sampling read group. Since the SLOTS event must be the leader of a TopDown
300group, the second event of the group is the sampling event.
301For example, perf record -e '{slots, $sampling_event, topdown-retiring}:S'
302
303Extension on Sapphire Rapids Server
304===================================
305The metrics counter is extended to support TMA method level 2 metrics.
306The lower half of the register is the TMA level 1 metrics (legacy).
307The upper half is also divided into four 8-bit fields for the new level 2
308metrics. Four more TopDown metric events are exposed for the end-users,
309topdown-heavy-ops, topdown-br-mispredict, topdown-fetch-lat and
310topdown-mem-bound.
311
312Each of the new level 2 metrics in the upper half is a subset of the
313corresponding level 1 metric in the lower half. Software can deduce the
314other four level 2 metrics by subtracting corresponding metrics as below.
315
316    Light_Operations = Retiring - Heavy_Operations
317    Machine_Clears = Bad_Speculation - Branch_Mispredicts
318    Fetch_Bandwidth = Frontend_Bound - Fetch_Latency
319    Core_Bound = Backend_Bound - Memory_Bound
320
321
322[1] https://software.intel.com/en-us/top-down-microarchitecture-analysis-method-win
323[2] https://github.com/andikleen/pmu-tools/wiki/toplev-manual
324[3] https://software.intel.com/en-us/intel-vtune-amplifier-xe
325[4] https://github.com/andikleen/pmu-tools/tree/master/jevents
326[5] https://sites.google.com/site/analysismethods/yasin-pubs
327