1 /*
2 * Copyright (c) 2024 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /*
8 * @file measure time for various LIFO operations
9 *
10 * This file contains the tests that measures the times for the following
11 * LIFO operations from both kernel threads and user threads:
12 * 1. Immediately adding a data item to a LIFO
13 * 2. Immediately removing a data item from a LIFO
14 * 3. Immediately adding a data item to a LIFO with allocation
15 * 4. Immediately removing a data item from a LIFO with allocation
16 * 5. Blocking on removing a data item from a LIFO
17 * 6. Waking (and context switching to) a thread blocked on a LIFO via
18 * k_lifo_put().
19 * 7. Waking (and context switching to) a thread blocked on a LIFO via
20 * k_lifo_alloc_put().
21 */
22
23 #include <zephyr/kernel.h>
24 #include <zephyr/timing/timing.h>
25 #include "utils.h"
26 #include "timing_sc.h"
27
28 #define STACK_SIZE (512 + CONFIG_TEST_EXTRA_STACK_SIZE)
29
30 static K_LIFO_DEFINE(lifo);
31
32 BENCH_BMEM uintptr_t lifo_data[5];
33
lifo_put_get_thread_entry(void * p1,void * p2,void * p3)34 static void lifo_put_get_thread_entry(void *p1, void *p2, void *p3)
35 {
36 uint32_t num_iterations = (uint32_t)(uintptr_t)p1;
37 uint32_t options = (uint32_t)(uintptr_t)p2;
38 timing_t start;
39 timing_t mid;
40 timing_t finish;
41 uint64_t put_sum = 0ULL;
42 uint64_t get_sum = 0ULL;
43 uintptr_t *data;
44
45 if ((options & K_USER) == 0) {
46 for (uint32_t i = 0; i < num_iterations; i++) {
47 start = timing_timestamp_get();
48
49 k_lifo_put(&lifo, lifo_data);
50
51 mid = timing_timestamp_get();
52
53 data = k_lifo_get(&lifo, K_NO_WAIT);
54
55 finish = timing_timestamp_get();
56
57 put_sum += timing_cycles_get(&start, &mid);
58 get_sum += timing_cycles_get(&mid, &finish);
59 }
60
61 timestamp.cycles = put_sum;
62 k_sem_take(&pause_sem, K_FOREVER);
63
64 timestamp.cycles = get_sum;
65 k_sem_take(&pause_sem, K_FOREVER);
66
67 put_sum = 0ULL;
68 get_sum = 0ULL;
69 }
70
71 for (uint32_t i = 0; i < num_iterations; i++) {
72 start = timing_timestamp_get();
73
74 k_lifo_alloc_put(&lifo, lifo_data);
75
76 mid = timing_timestamp_get();
77
78 data = k_lifo_get(&lifo, K_NO_WAIT);
79
80 finish = timing_timestamp_get();
81
82 put_sum += timing_cycles_get(&start, &mid);
83 get_sum += timing_cycles_get(&mid, &finish);
84 }
85
86 timestamp.cycles = put_sum;
87 k_sem_take(&pause_sem, K_FOREVER);
88
89 timestamp.cycles = get_sum;
90 }
91
lifo_ops(uint32_t num_iterations,uint32_t options)92 int lifo_ops(uint32_t num_iterations, uint32_t options)
93 {
94 int priority;
95 uint64_t cycles;
96 char tag[50];
97 char description[120];
98
99 priority = k_thread_priority_get(k_current_get());
100
101 timing_start();
102
103 k_thread_create(&start_thread, start_stack,
104 K_THREAD_STACK_SIZEOF(start_stack),
105 lifo_put_get_thread_entry,
106 (void *)(uintptr_t)num_iterations,
107 (void *)(uintptr_t)options, NULL,
108 priority - 1, options, K_FOREVER);
109
110 k_thread_access_grant(&start_thread, &pause_sem, &lifo);
111
112 k_thread_start(&start_thread);
113
114 if ((options & K_USER) == 0) {
115 snprintf(tag, sizeof(tag),
116 "lifo.put.immediate.%s",
117 options & K_USER ? "user" : "kernel");
118 snprintf(description, sizeof(description),
119 "%-40s - Add data to LIFO (no ctx switch)", tag);
120
121 cycles = timestamp.cycles;
122 cycles -= timestamp_overhead_adjustment(options, options);
123 PRINT_STATS_AVG(description, (uint32_t)cycles,
124 num_iterations, false, "");
125 k_sem_give(&pause_sem);
126
127 snprintf(tag, sizeof(tag),
128 "lifo.get.immediate.%s",
129 options & K_USER ? "user" : "kernel");
130 snprintf(description, sizeof(description),
131 "%-40s - Get data from LIFO (no ctx switch)", tag);
132 cycles = timestamp.cycles;
133 cycles -= timestamp_overhead_adjustment(options, options);
134 PRINT_STATS_AVG(description, (uint32_t)cycles,
135 num_iterations, false, "");
136 k_sem_give(&pause_sem);
137 }
138
139 snprintf(tag, sizeof(tag),
140 "lifo.put.alloc.immediate.%s",
141 options & K_USER ? "user" : "kernel");
142 snprintf(description, sizeof(description),
143 "%-40s - Allocate to add data to LIFO (no ctx switch)", tag);
144
145 cycles = timestamp.cycles;
146 PRINT_STATS_AVG(description, (uint32_t)cycles,
147 num_iterations, false, "");
148 k_sem_give(&pause_sem);
149
150 snprintf(tag, sizeof(tag),
151 "lifo.get.free.immediate.%s",
152 options & K_USER ? "user" : "kernel");
153 snprintf(description, sizeof(description),
154 "%-40s - Free when getting data from LIFO (no ctx switch)", tag);
155 cycles = timestamp.cycles;
156 PRINT_STATS_AVG(description, (uint32_t)cycles,
157 num_iterations, false, "");
158
159 k_thread_join(&start_thread, K_FOREVER);
160
161 timing_stop();
162
163 return 0;
164 }
165
alt_thread_entry(void * p1,void * p2,void * p3)166 static void alt_thread_entry(void *p1, void *p2, void *p3)
167 {
168 uint32_t num_iterations = (uint32_t)(uintptr_t)p1;
169 uint32_t options = (uint32_t)(uintptr_t)p2;
170 timing_t start;
171 timing_t mid;
172 timing_t finish;
173 uint64_t sum[4] = {0ULL, 0ULL, 0ULL, 0ULL};
174 uintptr_t *data;
175 uint32_t i;
176
177 if ((options & K_USER) == 0) {
178
179 /* Used with k_lifo_put() */
180
181 for (i = 0; i < num_iterations; i++) {
182
183 /* 1. Block waiting for data on LIFO */
184
185 start = timing_timestamp_get();
186
187 data = k_lifo_get(&lifo, K_FOREVER);
188
189 /* 3. Data obtained. */
190
191 finish = timing_timestamp_get();
192
193 mid = timestamp.sample;
194
195 sum[0] += timing_cycles_get(&start, &mid);
196 sum[1] += timing_cycles_get(&mid, &finish);
197 }
198 }
199
200 /* Used with k_lifo_alloc_put() */
201
202 for (i = 0; i < num_iterations; i++) {
203
204 /* 4. Block waiting for data on LIFO */
205
206 start = timing_timestamp_get();
207
208 data = k_lifo_get(&lifo, K_FOREVER);
209
210 /* 6. Data obtained */
211
212 finish = timing_timestamp_get();
213
214 mid = timestamp.sample;
215
216 sum[2] += timing_cycles_get(&start, &mid);
217 sum[3] += timing_cycles_get(&mid, &finish);
218 }
219
220 if ((options & K_USER) == 0) {
221 timestamp.cycles = sum[0];
222 k_sem_take(&pause_sem, K_FOREVER);
223 timestamp.cycles = sum[1];
224 k_sem_take(&pause_sem, K_FOREVER);
225 }
226
227 timestamp.cycles = sum[2];
228 k_sem_take(&pause_sem, K_FOREVER);
229 timestamp.cycles = sum[3];
230 }
231
start_thread_entry(void * p1,void * p2,void * p3)232 static void start_thread_entry(void *p1, void *p2, void *p3)
233 {
234 uint32_t num_iterations = (uint32_t)(uintptr_t)p1;
235 uint32_t options = (uint32_t)(uintptr_t)p2;
236 uint32_t i;
237
238 k_thread_start(&alt_thread);
239
240 if ((options & K_USER) == 0) {
241 for (i = 0; i < num_iterations; i++) {
242
243 /* 2. Add data thereby waking alt thread */
244
245 timestamp.sample = timing_timestamp_get();
246
247 k_lifo_put(&lifo, lifo_data);
248
249 }
250 }
251
252 for (i = 0; i < num_iterations; i++) {
253
254 /* 5. Add data thereby waking alt thread */
255
256 timestamp.sample = timing_timestamp_get();
257
258 k_lifo_alloc_put(&lifo, lifo_data);
259
260 }
261
262 k_thread_join(&alt_thread, K_FOREVER);
263 }
264
lifo_blocking_ops(uint32_t num_iterations,uint32_t start_options,uint32_t alt_options)265 int lifo_blocking_ops(uint32_t num_iterations, uint32_t start_options,
266 uint32_t alt_options)
267 {
268 int priority;
269 uint64_t cycles;
270 char tag[50];
271 char description[120];
272
273 priority = k_thread_priority_get(k_current_get());
274
275 timing_start();
276
277 k_thread_create(&start_thread, start_stack,
278 K_THREAD_STACK_SIZEOF(start_stack),
279 start_thread_entry,
280 (void *)(uintptr_t)num_iterations,
281 (void *)(uintptr_t)(start_options | alt_options), NULL,
282 priority - 1, start_options, K_FOREVER);
283
284 k_thread_create(&alt_thread, alt_stack,
285 K_THREAD_STACK_SIZEOF(alt_stack),
286 alt_thread_entry,
287 (void *)(uintptr_t)num_iterations,
288 (void *)(uintptr_t)(start_options | alt_options), NULL,
289 priority - 2, alt_options, K_FOREVER);
290
291 k_thread_access_grant(&start_thread, &alt_thread, &pause_sem, &lifo);
292 k_thread_access_grant(&alt_thread, &pause_sem, &lifo);
293
294 k_thread_start(&start_thread);
295
296 if (((start_options | alt_options) & K_USER) == 0) {
297 snprintf(tag, sizeof(tag),
298 "lifo.get.blocking.%s_to_%s",
299 alt_options & K_USER ? "u" : "k",
300 start_options & K_USER ? "u" : "k");
301 snprintf(description, sizeof(description),
302 "%-40s - Get data from LIFO (w/ ctx switch)", tag);
303
304 cycles = timestamp.cycles;
305 PRINT_STATS_AVG(description, (uint32_t)cycles,
306 num_iterations, false, "");
307 k_sem_give(&pause_sem);
308
309 snprintf(tag, sizeof(tag),
310 "lifo.put.wake+ctx.%s_to_%s",
311 start_options & K_USER ? "u" : "k",
312 alt_options & K_USER ? "u" : "k");
313 snprintf(description, sizeof(description),
314 "%-40s - Add data to LIFO (w/ ctx switch)", tag);
315 cycles = timestamp.cycles;
316 PRINT_STATS_AVG(description, (uint32_t)cycles,
317 num_iterations, false, "");
318 k_sem_give(&pause_sem);
319 }
320
321 snprintf(tag, sizeof(tag),
322 "lifo.get.free.blocking.%s_to_%s",
323 alt_options & K_USER ? "u" : "k",
324 start_options & K_USER ? "u" : "k");
325 snprintf(description, sizeof(description),
326 "%-40s - Free when getting data from LIFO (w/ ctx switch)", tag);
327
328 cycles = timestamp.cycles;
329 PRINT_STATS_AVG(description, (uint32_t)cycles,
330 num_iterations, false, "");
331 k_sem_give(&pause_sem);
332
333 snprintf(tag, sizeof(tag),
334 "lifo.put.alloc.wake+ctx.%s_to_%s",
335 start_options & K_USER ? "u" : "k",
336 alt_options & K_USER ? "u" : "k");
337 snprintf(description, sizeof(description),
338 "%-40s - Allocate to add data to LIFO (w/ ctx siwtch)", tag);
339 cycles = timestamp.cycles;
340 PRINT_STATS_AVG(description, (uint32_t)cycles,
341 num_iterations, false, "");
342
343 k_thread_join(&start_thread, K_FOREVER);
344
345 timing_stop();
346
347 return 0;
348 }
349