1 /*
2 * Copyright (c) 2012-2015 Wind River Systems, Inc.
3 * Copyright (c) 2023 Intel Corporation.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 /*
9 * @file measure time for sema lock and release
10 *
11 * This file contains the test that measures semaphore give and take time
12 * in the kernel. There is no contention on the semaphore being tested.
13 */
14
15 #include <zephyr/kernel.h>
16 #include <zephyr/timing/timing.h>
17 #include "utils.h"
18 #include "timing_sc.h"
19
20 static struct k_sem sem;
21
alt_thread_entry(void * p1,void * p2,void * p3)22 static void alt_thread_entry(void *p1, void *p2, void *p3)
23 {
24 uint32_t num_iterations = (uint32_t)(uintptr_t)p1;
25 timing_t mid;
26
27 ARG_UNUSED(p2);
28 ARG_UNUSED(p3);
29
30 for (uint32_t i = 0; i < num_iterations; i++) {
31
32 /*
33 * 2. Give the semaphore, thereby forcing a context switch back
34 * to <start_thread>.
35 */
36
37 mid = timing_timestamp_get();
38 k_sem_give(&sem);
39
40 /* 5. Share the <mid> timestamp. */
41
42 timestamp.sample = mid;
43
44 /* 6. Give <sem> so <start_thread> resumes execution */
45
46 k_sem_give(&sem);
47 }
48 }
49
start_thread_entry(void * p1,void * p2,void * p3)50 static void start_thread_entry(void *p1, void *p2, void *p3)
51 {
52 uint32_t num_iterations = (uint32_t)(uintptr_t)p1;
53 timing_t start;
54 timing_t mid;
55 timing_t finish;
56 uint32_t i;
57 uint64_t take_sum = 0ull;
58 uint64_t give_sum = 0ull;
59
60 ARG_UNUSED(p2);
61 ARG_UNUSED(p3);
62
63 k_thread_start(&alt_thread);
64
65 for (i = 0; i < num_iterations; i++) {
66
67 /*
68 * 1. Block on taking the semaphore and force a context switch
69 * to <alt_thread>.
70 */
71
72 start = timing_timestamp_get();
73 k_sem_take(&sem, K_FOREVER);
74
75 /* 3. Get the <finish> timestamp. */
76
77 finish = timing_timestamp_get();
78
79 /*
80 * 4. Let <alt_thread> run so it can share its <mid>
81 * timestamp.
82 */
83
84 k_sem_take(&sem, K_FOREVER);
85
86 /* 7. Retrieve the <mid> timestamp */
87
88 mid = timestamp.sample;
89
90 take_sum += timing_cycles_get(&start, &mid);
91 give_sum += timing_cycles_get(&mid, &finish);
92 }
93
94 k_thread_join(&alt_thread, K_FOREVER);
95
96 /* Share the totals with the main thread */
97
98 timestamp.cycles = take_sum;
99
100 k_sem_take(&sem, K_FOREVER);
101
102 timestamp.cycles = give_sum;
103 }
104
sema_context_switch(uint32_t num_iterations,uint32_t start_options,uint32_t alt_options)105 void sema_context_switch(uint32_t num_iterations,
106 uint32_t start_options, uint32_t alt_options)
107 {
108 uint64_t cycles;
109 char tag[50];
110 char description[120];
111 int priority;
112
113 timing_start();
114
115 priority = k_thread_priority_get(k_current_get());
116
117 k_thread_create(&start_thread, start_stack,
118 K_THREAD_STACK_SIZEOF(start_stack),
119 start_thread_entry,
120 (void *)(uintptr_t)num_iterations, NULL, NULL,
121 priority - 2, start_options, K_FOREVER);
122
123 k_thread_create(&alt_thread, alt_stack,
124 K_THREAD_STACK_SIZEOF(alt_stack),
125 alt_thread_entry,
126 (void *)(uintptr_t)num_iterations, NULL, NULL,
127 priority - 1, alt_options, K_FOREVER);
128
129 k_thread_access_grant(&start_thread, &sem, &alt_thread);
130
131 k_thread_access_grant(&alt_thread, &sem);
132
133 /* Start the test threads */
134
135 k_thread_start(&start_thread);
136
137 /* Retrieve the number of cycles spent taking the semaphore */
138
139 cycles = timestamp.cycles;
140 cycles -= timestamp_overhead_adjustment(start_options, alt_options);
141
142 snprintf(tag, sizeof(tag),
143 "semaphore.take.blocking.%c_to_%c",
144 ((start_options & K_USER) == K_USER) ? 'u' : 'k',
145 ((alt_options & K_USER) == K_USER) ? 'u' : 'k');
146 snprintf(description, sizeof(description),
147 "%-40s - Take a semaphore (context switch)", tag);
148 PRINT_STATS_AVG(description, (uint32_t)cycles,
149 num_iterations, false, "");
150
151 /* Unblock <start_thread> */
152
153 k_sem_give(&sem);
154
155 /* Retrieve the number of cycles spent taking the semaphore */
156
157 cycles = timestamp.cycles;
158 cycles -= timestamp_overhead_adjustment(start_options, alt_options);
159
160 snprintf(tag, sizeof(tag),
161 "semaphore.give.wake+ctx.%c_to_%c",
162 ((alt_options & K_USER) == K_USER) ? 'u' : 'k',
163 ((start_options & K_USER) == K_USER) ? 'u' : 'k');
164 snprintf(description, sizeof(description),
165 "%-40s - Give a semaphore (context switch)", tag);
166 PRINT_STATS_AVG(description, (uint32_t)cycles,
167 num_iterations, false, "");
168
169 k_thread_join(&start_thread, K_FOREVER);
170
171 timing_stop();
172
173 return;
174 }
175
176 /**
177 * This is the entry point for the test that performs uncontested operations
178 * on the semaphore. It gives the semaphore many times, takes the semaphore
179 * many times and then sends the results back to the main thread.
180 */
immediate_give_take(void * p1,void * p2,void * p3)181 static void immediate_give_take(void *p1, void *p2, void *p3)
182 {
183 uint32_t num_iterations = (uint32_t)(uintptr_t)p1;
184 timing_t start;
185 timing_t finish;
186 uint64_t give_cycles;
187 uint64_t take_cycles;
188
189 ARG_UNUSED(p2);
190 ARG_UNUSED(p3);
191
192 /* 1. Give a semaphore. No threads are waiting on it */
193
194 start = timing_timestamp_get();
195
196 for (uint32_t i = 0; i < num_iterations; i++) {
197 k_sem_give(&sem);
198 }
199
200 finish = timing_timestamp_get();
201 give_cycles = timing_cycles_get(&start, &finish);
202
203 /* 2. Take a semaphore--no contention */
204
205 start = timing_timestamp_get();
206
207 for (uint32_t i = 0; i < num_iterations; i++) {
208 k_sem_take(&sem, K_NO_WAIT);
209 }
210
211 finish = timing_timestamp_get();
212 take_cycles = timing_cycles_get(&start, &finish);
213
214 /* 3. Post the number of cycles spent giving the semaphore */
215
216 timestamp.cycles = give_cycles;
217
218 /* 4. Wait for the main thread to retrieve the data */
219
220 k_sem_take(&sem, K_FOREVER);
221
222 /* 7. Post the number of cycles spent taking the semaphore */
223
224 timestamp.cycles = take_cycles;
225 }
226
227
228 /**
229 *
230 * @brief The function tests semaphore test/signal time
231 *
232 * The routine performs unlock the quite amount of semaphores and then
233 * acquires them in order to measure the necessary time.
234 *
235 * @return 0 on success
236 */
sema_test_signal(uint32_t num_iterations,uint32_t options)237 int sema_test_signal(uint32_t num_iterations, uint32_t options)
238 {
239 uint64_t cycles;
240 int priority;
241 char tag[50];
242 char description[120];
243
244 timing_start();
245
246 k_sem_init(&sem, 0, num_iterations);
247
248 priority = k_thread_priority_get(k_current_get());
249
250 k_thread_create(&start_thread, start_stack,
251 K_THREAD_STACK_SIZEOF(start_stack),
252 immediate_give_take,
253 (void *)(uintptr_t)num_iterations, NULL, NULL,
254 priority - 1, options, K_FOREVER);
255
256 k_thread_access_grant(&start_thread, &sem);
257 k_thread_start(&start_thread);
258
259 /* 5. Retrieve the number of cycles spent giving the semaphore */
260
261 cycles = timestamp.cycles;
262
263 snprintf(tag, sizeof(tag),
264 "semaphore.give.immediate.%s",
265 (options & K_USER) == K_USER ? "user" : "kernel");
266 snprintf(description, sizeof(description),
267 "%-40s - Give a semaphore (no waiters)", tag);
268
269 PRINT_STATS_AVG(description, (uint32_t)cycles,
270 num_iterations, false, "");
271
272 /* 6. Unblock <start_thread> */
273
274 k_sem_give(&sem);
275
276 /* 8. Wait for <start_thread> to finish */
277
278 k_thread_join(&start_thread, K_FOREVER);
279
280 /* 9. Retrieve the number of cycles spent taking the semaphore */
281
282 cycles = timestamp.cycles;
283
284 snprintf(tag, sizeof(tag),
285 "semaphore.take.immediate.%s",
286 (options & K_USER) == K_USER ? "user" : "kernel");
287 snprintf(description, sizeof(description),
288 "%-40s - Take a semaphore (no blocking)", tag);
289
290 PRINT_STATS_AVG(description, (uint32_t)cycles,
291 num_iterations, false, "");
292
293 timing_stop();
294
295 return 0;
296 }
297