1 /*
2  * Copyright (c) 2024 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /*
8  * @file measure time for various k_stack operations
9  *
10  * This file contains the tests that measures the times for the following
11  * k_stack operations from both kernel threads and user threads:
12  *  1. Immediately adding a data item to a k_stack
13  *  2. Immediately removing a data item from a k_stack
14  *  3. Blocking on removing a data item from a k_stack
15  *  4. Waking (and context switching to) a thread blocked on a k_stack
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 MAX_ITEMS  16
24 
25 static BENCH_BMEM stack_data_t stack_array[MAX_ITEMS];
26 
27 static struct k_stack stack;
28 
stack_push_pop_thread_entry(void * p1,void * p2,void * p3)29 static void stack_push_pop_thread_entry(void *p1, void *p2, void *p3)
30 {
31 	uint32_t num_iterations = (uint32_t)(uintptr_t)p1;
32 	timing_t start;
33 	timing_t mid;
34 	timing_t finish;
35 	uint64_t put_sum = 0ULL;
36 	uint64_t get_sum = 0ULL;
37 	stack_data_t  data;
38 
39 	for (uint32_t i = 0; i < num_iterations; i++) {
40 		start = timing_timestamp_get();
41 
42 		(void) k_stack_push(&stack, 1234);
43 
44 		mid = timing_timestamp_get();
45 
46 		(void) k_stack_pop(&stack, &data, K_NO_WAIT);
47 
48 		finish = timing_timestamp_get();
49 
50 		put_sum += timing_cycles_get(&start, &mid);
51 		get_sum += timing_cycles_get(&mid, &finish);
52 	}
53 
54 	timestamp.cycles = put_sum;
55 	k_sem_take(&pause_sem, K_FOREVER);
56 
57 	timestamp.cycles = get_sum;
58 }
59 
stack_ops(uint32_t num_iterations,uint32_t options)60 int stack_ops(uint32_t num_iterations, uint32_t options)
61 {
62 	int      priority;
63 	uint64_t cycles;
64 	char     tag[50];
65 	char     description[120];
66 
67 	priority = k_thread_priority_get(k_current_get());
68 
69 	timing_start();
70 
71 	k_stack_init(&stack, stack_array, MAX_ITEMS);
72 
73 	k_thread_create(&start_thread, start_stack,
74 			K_THREAD_STACK_SIZEOF(start_stack),
75 			stack_push_pop_thread_entry,
76 			(void *)(uintptr_t)num_iterations,
77 			NULL, NULL,
78 			priority - 1, options, K_FOREVER);
79 
80 	k_thread_access_grant(&start_thread, &pause_sem, &stack);
81 
82 	k_thread_start(&start_thread);
83 
84 	snprintf(tag, sizeof(tag),
85 		 "stack.push.immediate.%s",
86 		 options & K_USER ? "user" : "kernel");
87 	snprintf(description, sizeof(description),
88 		 "%-40s - Add data to k_stack (no ctx switch)", tag);
89 
90 	cycles = timestamp.cycles;
91 	cycles -= timestamp_overhead_adjustment(options, options);
92 	PRINT_STATS_AVG(description, (uint32_t)cycles,
93 			num_iterations, false, "");
94 	k_sem_give(&pause_sem);
95 
96 	snprintf(tag, sizeof(tag),
97 		 "stack.pop.immediate.%s",
98 		 options & K_USER ? "user" : "kernel");
99 	snprintf(description, sizeof(description),
100 		 "%-40s - Get data from k_stack (no ctx switch)", tag);
101 	cycles = timestamp.cycles;
102 	cycles -= timestamp_overhead_adjustment(options, options);
103 	PRINT_STATS_AVG(description, (uint32_t)cycles,
104 			num_iterations, false, "");
105 
106 	k_thread_join(&start_thread, K_FOREVER);
107 
108 	timing_stop();
109 
110 	return 0;
111 }
112 
alt_thread_entry(void * p1,void * p2,void * p3)113 static void alt_thread_entry(void *p1, void *p2, void *p3)
114 {
115 	uint32_t num_iterations = (uint32_t)(uintptr_t)p1;
116 	timing_t  start;
117 	timing_t  mid;
118 	timing_t  finish;
119 	uint64_t  sum[2] = {0ULL, 0ULL};
120 	uint32_t  i;
121 	stack_data_t data;
122 
123 	for (i = 0; i < num_iterations; i++) {
124 
125 		/* 1. Block waiting for data on k_stack */
126 
127 		start = timing_timestamp_get();
128 
129 		k_stack_pop(&stack, &data, K_FOREVER);
130 
131 		/* 3. Data obtained. */
132 
133 		finish = timing_timestamp_get();
134 
135 		mid = timestamp.sample;
136 
137 		sum[0] += timing_cycles_get(&start, &mid);
138 		sum[1] += timing_cycles_get(&mid, &finish);
139 	}
140 
141 	timestamp.cycles = sum[0];
142 	k_sem_take(&pause_sem, K_FOREVER);
143 	timestamp.cycles = sum[1];
144 }
145 
start_thread_entry(void * p1,void * p2,void * p3)146 static void start_thread_entry(void *p1, void *p2, void *p3)
147 {
148 	uint32_t num_iterations = (uint32_t)(uintptr_t)p1;
149 	uint32_t i;
150 
151 	k_thread_start(&alt_thread);
152 
153 	for (i = 0; i < num_iterations; i++) {
154 
155 		/* 2. Add data thereby waking alt thread */
156 
157 		timestamp.sample = timing_timestamp_get();
158 
159 		k_stack_push(&stack, (stack_data_t)123);
160 	}
161 
162 	k_thread_join(&alt_thread, K_FOREVER);
163 }
164 
stack_blocking_ops(uint32_t num_iterations,uint32_t start_options,uint32_t alt_options)165 int stack_blocking_ops(uint32_t num_iterations, uint32_t start_options,
166 		      uint32_t alt_options)
167 {
168 	int      priority;
169 	uint64_t cycles;
170 	char     tag[50];
171 	char     description[120];
172 
173 	priority = k_thread_priority_get(k_current_get());
174 
175 	timing_start();
176 
177 	k_thread_create(&start_thread, start_stack,
178 			K_THREAD_STACK_SIZEOF(start_stack),
179 			start_thread_entry,
180 			(void *)(uintptr_t)num_iterations,
181 			NULL, NULL,
182 			priority - 1, start_options, K_FOREVER);
183 
184 	k_thread_create(&alt_thread, alt_stack,
185 			K_THREAD_STACK_SIZEOF(alt_stack),
186 			alt_thread_entry,
187 			(void *)(uintptr_t)num_iterations,
188 			NULL, NULL,
189 			priority - 2, alt_options, K_FOREVER);
190 
191 	k_thread_access_grant(&start_thread, &alt_thread, &pause_sem, &stack);
192 	k_thread_access_grant(&alt_thread, &pause_sem, &stack);
193 
194 	k_thread_start(&start_thread);
195 
196 	snprintf(tag, sizeof(tag),
197 		 "stack.pop.blocking.%s_to_%s",
198 		 alt_options & K_USER ? "u" : "k",
199 		 start_options & K_USER ? "u" : "k");
200 	snprintf(description, sizeof(description),
201 		 "%-40s - Get data from k_stack (w/ ctx switch)", tag);
202 
203 	cycles = timestamp.cycles;
204 	PRINT_STATS_AVG(description, (uint32_t)cycles,
205 			num_iterations, false, "");
206 	k_sem_give(&pause_sem);
207 
208 	snprintf(tag, sizeof(tag),
209 		 "stack.push.wake+ctx.%s_to_%s",
210 		 start_options & K_USER ? "u" : "k",
211 		 alt_options & K_USER ? "u" : "k");
212 	snprintf(description, sizeof(description),
213 		 "%-40s - Add data to k_stack (w/ ctx switch)", tag);
214 	cycles = timestamp.cycles;
215 	PRINT_STATS_AVG(description, (uint32_t)cycles,
216 			num_iterations, false, "");
217 
218 	k_thread_join(&start_thread, K_FOREVER);
219 
220 	timing_stop();
221 
222 	return 0;
223 }
224