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