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