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