source: mainline/uspace/app/rcubench/rcubench.c@ f74ec77

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since f74ec77 was f74ec77, checked in by Adam Hraska <adam.hraska+hos@…>, 13 years ago

Fixed rcubench stack corrupting print_res().

  • Property mode set to 100644
File size: 8.2 KB
Line 
1/*
2 * Copyright (c) 2012 Adam Hraska
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup test
30 * @{
31 */
32
33/**
34 * @file rcubench.c
35 */
36
37#include <stdio.h>
38#include <stdlib.h>
39#include <stdint.h>
40#include <mem.h>
41#include <errno.h>
42#include <thread.h>
43#include <assert.h>
44#include <async.h>
45#include <fibril.h>
46#include <fibril_synch.h>
47#include <compiler/barrier.h>
48#include <futex.h>
49
50#include <rcu.h>
51
52
53/* Results are printed to this file in addition to stdout. */
54static FILE *results_fd = NULL;
55
56typedef struct bench {
57 const char *name;
58 void (*func)(struct bench *);
59 size_t iters;
60 size_t nthreads;
61 size_t array_size;
62 size_t *array;
63 futex_t done_threads;
64
65 futex_t bench_fut;
66} bench_t;
67
68
69/* Combats compiler optimizations. */
70static volatile size_t dummy = 0;
71
72static size_t sum_array(size_t *array, size_t len)
73{
74 size_t sum = 0;
75
76 for (size_t k = 0; k < len; ++k)
77 sum += array[k];
78
79 return sum;
80}
81
82
83static void kernel_futex_bench(bench_t *bench)
84{
85 futex_t * const fut = &bench->bench_fut;
86 const size_t iters = bench->iters;
87 size_t sum = 0;
88
89 for (size_t i = 0; i < iters; ++i) {
90 /* Do some work with the futex locked to encourage contention. */
91 futex_down(fut);
92 sum += sum_array(bench->array, bench->array_size);
93 futex_up(fut);
94
95 /*
96 * Do half as much work to give other threads a chance to acquire
97 * the futex.
98 */
99 sum += sum_array(bench->array, bench->array_size / 2);
100 }
101
102 /*
103 * Writing to a global volatile variable separated with a cc-barrier
104 * should discourage the compiler from optimizing away sum_array()s.
105 */
106 compiler_barrier();
107 dummy = sum;
108}
109
110static void libc_futex_lock_bench(bench_t *bench)
111{
112 const size_t iters = bench->iters;
113 futex_t loc_fut = FUTEX_INITIALIZER;
114
115 for (size_t i = 0; i < iters; ++i) {
116 futex_lock(&loc_fut);
117 /* no-op */
118 compiler_barrier();
119 futex_unlock(&loc_fut);
120 }
121}
122
123static void libc_futex_sema_bench(bench_t *bench)
124{
125 const size_t iters = bench->iters;
126 futex_t loc_fut = FUTEX_INITIALIZER;
127
128 for (size_t i = 0; i < iters; ++i) {
129 futex_down(&loc_fut);
130 /* no-op */
131 compiler_barrier();
132 futex_up(&loc_fut);
133 }
134}
135
136static void thread_func(void *arg)
137{
138 bench_t *bench = (bench_t*)arg;
139
140 bench->func(bench);
141
142 /* Signal another thread completed. */
143 futex_up(&bench->done_threads);
144}
145
146static void run_threads_and_wait(bench_t *bench)
147{
148 assert(1 <= bench->nthreads);
149
150 if (2 <= bench->nthreads) {
151 printf("Creating %zu additional threads...\n", bench->nthreads - 1);
152 }
153
154 /* Create and run the first nthreads - 1 threads.*/
155 for (size_t k = 1; k < bench->nthreads; ++k) {
156 thread_id_t tid;
157 /* Also sets up a fibril for the thread. */
158 int ret = thread_create(thread_func, bench, "rcubench-t", &tid);
159 if (ret != EOK) {
160 printf("Error: Failed to create benchmark thread.\n");
161 abort();
162 }
163 thread_detach(tid);
164 }
165
166 /*
167 * Run the last thread in place so that we create multiple threads
168 * only when needed. Otherwise libc would immediately upgrade
169 * single-threaded futexes to proper multithreaded futexes
170 */
171 thread_func(bench);
172
173 printf("Waiting for remaining threads to complete.\n");
174
175 /* Wait for threads to complete. */
176 for (size_t k = 0; k < bench->nthreads; ++k) {
177 futex_down(&bench->done_threads);
178 }
179}
180
181static const char *results_txt = "/tmp/rcu-bench-results.txt";
182
183static bool open_results(void)
184{
185 results_fd = fopen(results_txt, "a");
186 return NULL != results_fd;
187}
188
189static void close_results(void)
190{
191 if (results_fd) {
192 fclose(results_fd);
193 }
194}
195
196static void print_res(const char *fmt, ... )
197{
198 va_list args;
199
200 va_start(args, fmt);
201 vfprintf(results_fd, fmt, args);
202 va_end(args);
203
204 va_start(args, fmt);
205 vprintf(fmt, args);
206 va_end(args);
207}
208
209static void print_usage(void)
210{
211 printf("rcubench [test-name] [k-iterations] [n-threads] {work-size}\n");
212 printf("Available tests: \n");
213 printf(" ke-futex .. threads down/up a shared futex and do some work when\n");
214 printf(" in critical section; do a little less work outside CS.\n");
215 printf(" lock .. threads lock/unlock separate futexes.\n");
216 printf(" sema .. threads down/up separate futexes.\n");
217 printf("eg:\n");
218 printf(" rcubench ke-futex 100000 3 4\n");
219 printf(" rcubench lock 100000 2 ..runs futex_lock/unlock in a loop\n");
220 printf(" rcubench sema 100000 2 ..runs futex_down/up in a loop\n");
221 printf("Results are stored in %s\n", results_txt);
222}
223
224static bool parse_cmd_line(int argc, char **argv, bench_t *bench,
225 const char **err)
226{
227 if (argc < 4) {
228 *err = "Not enough parameters";
229 return false;
230 }
231
232 futex_initialize(&bench->bench_fut, 1);
233
234 if (0 == str_cmp(argv[1], "ke-futex")) {
235 bench->func = kernel_futex_bench;
236 } else if (0 == str_cmp(argv[1], "lock")) {
237 bench->func = libc_futex_lock_bench;
238 } else if (0 == str_cmp(argv[1], "sema")) {
239 bench->func = libc_futex_sema_bench;
240 } else {
241 *err = "Unknown test name";
242 return false;
243 }
244
245 bench->name = argv[1];
246
247 /* Determine iteration count. */
248 uint32_t iter_cnt = 0;
249 int ret = str_uint32_t(argv[2], NULL, 0, true, &iter_cnt);
250
251 if (ret == EOK && 1 <= iter_cnt) {
252 bench->iters = iter_cnt;
253 } else {
254 *err = "Err: Invalid number of iterations";
255 return false;
256 }
257
258 /* Determine thread count. */
259 uint32_t thread_cnt = 0;
260 ret = str_uint32_t(argv[3], NULL, 0, true, &thread_cnt);
261
262 if (ret == EOK && 1 <= thread_cnt && thread_cnt <= 64) {
263 bench->nthreads = thread_cnt;
264 } else {
265 *err = "Err: Invalid number of threads";
266 return false;
267 }
268
269 /* Set work array size. */
270 if (argc > 4) {
271 uint32_t work_size = 0;
272 ret = str_uint32_t(argv[4], NULL, 0, true, &work_size);
273
274 if (ret == EOK && work_size <= 10000) {
275 bench->array_size = work_size;
276 } else {
277 *err = "Err: Work size too large";
278 return false;
279 }
280 } else {
281 bench->array_size = 0;
282 }
283
284 /* Allocate work array. */
285 if (0 < bench->array_size) {
286 bench->array = malloc(bench->array_size * sizeof(size_t));
287 if (!bench->array) {
288 *err = "Err: Failed to allocate work array";
289 return false;
290 }
291 } else {
292 bench->array = NULL;
293 }
294
295 return true;
296}
297
298int main(int argc, char **argv)
299{
300 const char *err = "(error)";
301 bench_t bench;
302
303 futex_initialize(&bench.done_threads, 0);
304
305 if (!parse_cmd_line(argc, argv, &bench, &err)) {
306 printf("%s\n", err);
307 print_usage();
308 return -1;
309 }
310
311 open_results();
312
313 print_res("Running '%s' futex bench in '%zu' threads with '%zu' iterations.\n",
314 bench.name, bench.nthreads, bench.iters);
315
316 struct timeval start, end;
317 getuptime(&start);
318
319 run_threads_and_wait(&bench);
320
321 getuptime(&end);
322 int64_t duration = tv_sub(&end, &start);
323
324 uint64_t secs = (uint64_t)duration / 1000 / 1000;
325 uint64_t total_iters = (uint64_t)bench.iters * bench.nthreads;
326 uint64_t iters_per_sec = 0;
327
328 if (0 < duration) {
329 iters_per_sec = total_iters * 1000 * 1000 / duration;
330 }
331
332 print_res("Completed %" PRIu64 " iterations in %" PRId64 " usecs (%" PRIu64
333 " secs); %" PRIu64 " iters/sec\n",
334 total_iters, duration, secs, iters_per_sec);
335
336 close_results();
337
338 return 0;
339}
340
341
342/**
343 * @}
344 */
Note: See TracBrowser for help on using the repository browser.