1 /*
2 * Copyright (c) 2024 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /*
8 * @file measure time for various event operations
9 *
10 * This file contains the tests that measure the times for manipulating
11 * event objects from both kernel and user threads:
12 * 1. Immediately posting and setting events
13 * 2. Immediately receiving any or all events.
14 * 3. Blocking to receive either any or all events.
15 * 4. Waking (and switching to) a thread waiting for any or all events.
16 */
17
18 #include <zephyr/kernel.h>
19 #include <zephyr/timing/timing.h>
20 #include "utils.h"
21 #include "timing_sc.h"
22
23 #define BENCH_EVENT_SET 0x1234
24 #define ALL_EVENTS 0xFFFFFFFF
25
26 static K_EVENT_DEFINE(event_set);
27
event_ops_entry(void * p1,void * p2,void * p3)28 static void event_ops_entry(void *p1, void *p2, void *p3)
29 {
30 uint32_t num_iterations = (uint32_t)(uintptr_t)p1;
31 timing_t start;
32 timing_t finish;
33 uint32_t i;
34
35 /* 2. Benchmark k_event_post() with no waiters */
36
37 k_event_clear(&event_set, ALL_EVENTS);
38
39 start = timing_timestamp_get();
40 for (i = 0; i < num_iterations; i++) {
41 k_event_post(&event_set, BENCH_EVENT_SET);
42 }
43 finish = timing_timestamp_get();
44
45 timestamp.cycles = timing_cycles_get(&start, &finish);
46
47 /* 3. Pause to allow main thread to print results */
48
49 k_sem_take(&pause_sem, K_FOREVER);
50
51 /* 5. Benchmark k_event_set() with no waiters */
52
53 start = timing_timestamp_get();
54 for (i = 0; i < num_iterations; i++) {
55 k_event_set(&event_set, BENCH_EVENT_SET);
56 }
57 finish = timing_timestamp_get();
58
59 timestamp.cycles = timing_cycles_get(&start, &finish);
60
61 /* 6. Pause to allow main thread to print results */
62
63 k_sem_take(&pause_sem, K_FOREVER);
64
65 /* 8. Benchmark k_event_wait() (events have already been set) */
66
67 start = timing_timestamp_get();
68 for (i = 0; i < num_iterations; i++) {
69 k_event_wait(&event_set, BENCH_EVENT_SET, false, K_FOREVER);
70 }
71 finish = timing_timestamp_get();
72
73 timestamp.cycles = timing_cycles_get(&start, &finish);
74
75 /* 9. Pause to allow main thread to print results */
76
77 k_sem_take(&pause_sem, K_FOREVER);
78
79 /* 11. Benchmark k_event_wait_all() (events have already been set) */
80
81 start = timing_timestamp_get();
82 for (i = 0; i < num_iterations; i++) {
83 k_event_wait_all(&event_set, BENCH_EVENT_SET, false, K_FOREVER);
84 }
85 finish = timing_timestamp_get();
86
87 timestamp.cycles = timing_cycles_get(&start, &finish);
88
89 /* 12. Thread finishes */
90 }
91
start_thread_entry(void * p1,void * p2,void * p3)92 static void start_thread_entry(void *p1, void *p2, void *p3)
93 {
94 uint32_t num_iterations = (uint32_t)(uintptr_t)p1;
95 uint32_t i;
96
97 k_thread_start(&alt_thread);
98
99 for (i = 0; i < num_iterations; i++) {
100
101 /* 2. Set the events to wake alt_thread */
102
103 timestamp.sample = timing_timestamp_get();
104 k_event_set(&event_set, BENCH_EVENT_SET);
105 }
106
107 for (i = 0; i < num_iterations; i++) {
108
109 /* 5. Post the events to wake alt_thread */
110
111 timestamp.sample = timing_timestamp_get();
112 k_event_post(&event_set, BENCH_EVENT_SET);
113 }
114
115 k_thread_join(&alt_thread, K_FOREVER);
116 }
117
alt_thread_entry(void * p1,void * p2,void * p3)118 static void alt_thread_entry(void *p1, void *p2, void *p3)
119 {
120 uint32_t num_iterations = (uint32_t)(uintptr_t)p1;
121 uint32_t i;
122 timing_t start;
123 timing_t mid;
124 timing_t finish;
125 uint64_t sum[4] = {0ULL, 0ULL, 0ULL, 0ULL};
126
127 for (i = 0; i < num_iterations; i++) {
128
129 /* 1. Wait for any of the events */
130
131 start = timing_timestamp_get();
132 k_event_wait(&event_set, BENCH_EVENT_SET, true, K_FOREVER);
133
134 /* 3. Record the final timestamp */
135
136 finish = timing_timestamp_get();
137 mid = timestamp.sample;
138
139 sum[0] += timing_cycles_get(&start, &mid);
140 sum[1] += timing_cycles_get(&mid, &finish);
141 }
142
143 for (i = 0; i < num_iterations; i++) {
144
145 /* 4. Wait for all of the events */
146
147 start = timing_timestamp_get();
148 k_event_wait_all(&event_set, BENCH_EVENT_SET, true, K_FOREVER);
149
150 /* 6. Record the final timestamp */
151
152 finish = timing_timestamp_get();
153 mid = timestamp.sample;
154
155 sum[2] += timing_cycles_get(&start, &mid);
156 sum[3] += timing_cycles_get(&mid, &finish);
157 }
158
159 /* Let the main thread print the results */
160
161 timestamp.cycles = sum[0];
162 k_sem_take(&pause_sem, K_FOREVER);
163
164 timestamp.cycles = sum[1];
165 k_sem_take(&pause_sem, K_FOREVER);
166
167 timestamp.cycles = sum[2];
168 k_sem_take(&pause_sem, K_FOREVER);
169
170 timestamp.cycles = sum[3];
171 }
172
event_ops(uint32_t num_iterations,uint32_t options)173 int event_ops(uint32_t num_iterations, uint32_t options)
174 {
175 int priority;
176 char tag[50];
177 char description[120];
178 uint64_t cycles;
179
180 priority = k_thread_priority_get(k_current_get());
181
182 timing_start();
183
184 k_thread_create(&start_thread, start_stack,
185 K_THREAD_STACK_SIZEOF(start_stack),
186 event_ops_entry,
187 (void *)(uintptr_t)num_iterations,
188 NULL, NULL,
189 priority - 1, options, K_FOREVER);
190
191 k_thread_access_grant(&start_thread, &event_set, &pause_sem);
192
193 /* 1. Start test thread */
194
195 k_thread_start(&start_thread);
196
197 /* 4. Benchmark thread has paused */
198
199 snprintf(tag, sizeof(tag), "events.post.immediate.%s",
200 (options & K_USER) ? "user" : "kernel");
201 snprintf(description, sizeof(description),
202 "%-40s - Post events (nothing wakes)", tag);
203
204 cycles = timestamp.cycles;
205 PRINT_STATS_AVG(description, (uint32_t)cycles,
206 num_iterations, false, "");
207
208 k_sem_give(&pause_sem);
209
210 /* 7. Benchmark thread has paused */
211
212 snprintf(tag, sizeof(tag), "events.set.immediate.%s",
213 (options & K_USER) ? "user" : "kernel");
214 snprintf(description, sizeof(description),
215 "%-40s - Set events (nothing wakes)", tag);
216 cycles = timestamp.cycles;
217 PRINT_STATS_AVG(description, (uint32_t)cycles,
218 num_iterations, false, "");
219
220 k_sem_give(&pause_sem);
221
222 /* 10. Benchmark thread has paused */
223
224 snprintf(tag, sizeof(tag), "events.wait.immediate.%s",
225 (options & K_USER) ? "user" : "kernel");
226 snprintf(description, sizeof(description),
227 "%-40s - Wait for any events (no ctx switch)", tag);
228 cycles = timestamp.cycles;
229 PRINT_STATS_AVG(description, (uint32_t)cycles,
230 num_iterations, false, "");
231
232 k_sem_give(&pause_sem);
233
234 /* 13. Benchmark thread has finished */
235
236 snprintf(tag, sizeof(tag), "events.wait_all.immediate.%s",
237 (options & K_USER) ? "user" : "kernel");
238 snprintf(description, sizeof(description),
239 "%-40s - Wait for all events (no ctx switch)", tag);
240 cycles = timestamp.cycles;
241 PRINT_STATS_AVG(description, (uint32_t)cycles,
242 num_iterations, false, "");
243 k_thread_join(&start_thread, K_FOREVER);
244
245 timing_stop();
246
247 return 0;
248 }
249
event_blocking_ops(uint32_t num_iterations,uint32_t start_options,uint32_t alt_options)250 int event_blocking_ops(uint32_t num_iterations, uint32_t start_options,
251 uint32_t alt_options)
252 {
253 int priority;
254 char tag[50];
255 char description[120];
256 uint64_t cycles;
257
258 priority = k_thread_priority_get(k_current_get());
259
260 timing_start();
261
262 k_thread_create(&start_thread, start_stack,
263 K_THREAD_STACK_SIZEOF(start_stack),
264 start_thread_entry,
265 (void *)(uintptr_t)num_iterations,
266 NULL, NULL,
267 priority - 1, start_options, K_FOREVER);
268
269 k_thread_create(&alt_thread, alt_stack,
270 K_THREAD_STACK_SIZEOF(alt_stack),
271 alt_thread_entry,
272 (void *)(uintptr_t)num_iterations,
273 NULL, NULL,
274 priority - 2, alt_options, K_FOREVER);
275
276 k_thread_access_grant(&start_thread, &alt_thread, &event_set,
277 &pause_sem);
278 k_thread_access_grant(&alt_thread, &event_set, &pause_sem);
279
280 k_thread_start(&start_thread);
281
282 snprintf(tag, sizeof(tag),
283 "events.wait.blocking.%c_to_%c",
284 (alt_options & K_USER) ? 'u' : 'k',
285 (start_options & K_USER) ? 'u' : 'k');
286 snprintf(description, sizeof(description),
287 "%-40s - Wait for any events (w/ ctx switch)", tag);
288 cycles = timestamp.cycles -
289 timestamp_overhead_adjustment(start_options, alt_options);
290 PRINT_STATS_AVG(description, (uint32_t)cycles,
291 num_iterations, false, "");
292 k_sem_give(&pause_sem);
293
294 snprintf(tag, sizeof(tag),
295 "events.set.wake+ctx.%c_to_%c",
296 (start_options & K_USER) ? 'u' : 'k',
297 (alt_options & K_USER) ? 'u' : 'k');
298 snprintf(description, sizeof(description),
299 "%-40s - Set events (w/ ctx switch)", tag);
300 cycles = timestamp.cycles -
301 timestamp_overhead_adjustment(start_options, alt_options);
302 PRINT_STATS_AVG(description, (uint32_t)cycles,
303 num_iterations, false, "");
304 k_sem_give(&pause_sem);
305
306 snprintf(tag, sizeof(tag),
307 "events.wait_all.blocking.%c_to_%c",
308 (alt_options & K_USER) ? 'u' : 'k',
309 (start_options & K_USER) ? 'u' : 'k');
310 snprintf(description, sizeof(description),
311 "%-40s - Wait for all events (w/ ctx switch)", tag);
312 cycles = timestamp.cycles;
313 PRINT_STATS_AVG(description, (uint32_t)cycles,
314 num_iterations, false, "");
315 k_sem_give(&pause_sem);
316
317 snprintf(tag, sizeof(tag),
318 "events.post.wake+ctx.%c_to_%c",
319 (start_options & K_USER) ? 'u' : 'k',
320 (alt_options & K_USER) ? 'u' : 'k');
321 snprintf(description, sizeof(description),
322 "%-40s - Post events (w/ ctx switch)", tag);
323 cycles = timestamp.cycles;
324 PRINT_STATS_AVG(description, (uint32_t)cycles,
325 num_iterations, false, "");
326
327 k_thread_join(&start_thread, K_FOREVER);
328
329 timing_stop();
330
331 return 0;
332 }
333