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

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

Differentiated futexes when used with mutex semantics from those with semaphore semantics.

  • Property mode set to 100644
File size: 7.5 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
52typedef struct bench {
53 enum {
54 T_KERN_FUTEX,
55 T_LIBC_FUTEX
56 } type;
57 size_t iters;
58 size_t nthreads;
59 size_t array_size;
60 size_t *array;
61 futex_t done_threads;
62
63 futex_t ke_bench_fut;
64 fibril_mutex_t libc_bench_mtx;
65} bench_t;
66
67
68/* Combats compiler optimizations. */
69static volatile size_t dummy = 0;
70
71static size_t sum_array(size_t *array, size_t len)
72{
73 size_t sum = 0;
74
75 for (size_t k = 0; k < len; ++k)
76 sum += array[k];
77
78 return sum;
79}
80
81
82static void kernel_futex_bench(bench_t *bench)
83{
84 futex_t * const fut = &bench->ke_bench_fut;
85 const size_t iters = bench->iters;
86 size_t sum = 0;
87
88 for (size_t i = 0; i < iters; ++i) {
89 /* Do some work with the futex locked to encourage contention. */
90 futex_down(fut);
91 sum += sum_array(bench->array, bench->array_size);
92 futex_up(fut);
93
94 /*
95 * Do half as much work to give other threads a chance to acquire
96 * the futex.
97 */
98 sum += sum_array(bench->array, bench->array_size / 2);
99 }
100
101 /*
102 * Writing to a global volatile variable separated with a cc-barrier
103 * should discourage the compiler from optimizing away sum_array()s.
104 */
105 compiler_barrier();
106 dummy = sum;
107}
108
109static void libc_futex_bench(bench_t *bench)
110{
111 fibril_mutex_t * const mtx = &bench->libc_bench_mtx;
112 const size_t iters = bench->iters;
113
114 for (size_t i = 0; i < iters; ++i) {
115 fibril_mutex_lock(mtx);
116 /* no-op */
117 compiler_barrier();
118 fibril_mutex_unlock(mtx);
119 }
120}
121
122
123static void thread_func(void *arg)
124{
125 bench_t *bench = (bench_t*)arg;
126
127 switch (bench->type) {
128 case T_KERN_FUTEX:
129 kernel_futex_bench(bench);
130 break;
131 case T_LIBC_FUTEX:
132 libc_futex_bench(bench);
133 break;
134 default:
135 assert(false);
136 }
137
138 /* Signal another thread completed. */
139 futex_up(&bench->done_threads);
140}
141
142static void run_threads_and_wait(bench_t *bench)
143{
144 assert(1 <= bench->nthreads);
145
146 if (2 <= bench->nthreads) {
147 printf("Creating %zu additional threads...\n", bench->nthreads - 1);
148 }
149
150 /* Create and run the first nthreads - 1 threads.*/
151 for (size_t k = 1; k < bench->nthreads; ++k) {
152 thread_id_t tid;
153 /* Also sets up a fibril for the thread. */
154 int ret = thread_create(thread_func, bench, "rcubench-t", &tid);
155 if (ret != EOK) {
156 printf("Error: Failed to create benchmark thread.\n");
157 abort();
158 }
159 thread_detach(tid);
160 }
161
162 /*
163 * Run the last thread in place so that we create multiple threads
164 * only when needed. Otherwise libc would immediately upgrade
165 * single-threaded futexes to proper multithreaded futexes
166 */
167 thread_func(bench);
168
169 printf("Waiting for remaining threads to complete.\n");
170
171 /* Wait for threads to complete. */
172 for (size_t k = 0; k < bench->nthreads; ++k) {
173 futex_down(&bench->done_threads);
174 }
175}
176
177static void print_usage(void)
178{
179 printf("rcubench [test-name] [k-iterations] [n-threads] {work-size}\n");
180 printf("eg:\n");
181 printf(" rcubench ke 100000 3 4\n");
182 printf(" rcubench libc 100000 2\n");
183 printf(" rcubench def-ke \n");
184 printf(" rcubench def-libc\n");
185}
186
187static bool parse_cmd_line(int argc, char **argv, bench_t *bench,
188 const char **err)
189{
190 if (argc < 2) {
191 *err = "Benchmark name not specified";
192 return false;
193 }
194
195 futex_initialize(&bench->ke_bench_fut, 1);
196 fibril_mutex_initialize(&bench->libc_bench_mtx);
197
198 if (0 == str_cmp(argv[1], "def-ke")) {
199 bench->type = T_KERN_FUTEX;
200 bench->nthreads = 4;
201 bench->iters = 1000 * 1000;
202 bench->array_size = 10;
203 bench->array = malloc(bench->array_size * sizeof(size_t));
204 return NULL != bench->array;
205 } else if (0 == str_cmp(argv[1], "def-libc")) {
206 bench->type = T_LIBC_FUTEX;
207 bench->nthreads = 4;
208 bench->iters = 1000 * 1000;
209 bench->array_size = 0;
210 bench->array = NULL;
211 return true;
212 } else if (0 == str_cmp(argv[1], "ke")) {
213 bench->type = T_KERN_FUTEX;
214 } else if (0 == str_cmp(argv[1], "libc")) {
215 bench->type = T_LIBC_FUTEX;
216 } else {
217 *err = "Unknown test name";
218 return false;
219 }
220
221 if (argc < 4) {
222 *err = "Not enough parameters";
223 return false;
224 }
225
226 uint32_t iter_cnt = 0;
227 int ret = str_uint32_t(argv[2], NULL, 0, true, &iter_cnt);
228
229 if (ret == EOK && 1 <= iter_cnt) {
230 bench->iters = iter_cnt;
231 } else {
232 *err = "Err: Invalid number of iterations";
233 return false;
234 }
235
236 uint32_t thread_cnt = 0;
237 ret = str_uint32_t(argv[3], NULL, 0, true, &thread_cnt);
238
239 if (ret == EOK && 1 <= thread_cnt && thread_cnt <= 64) {
240 bench->nthreads = thread_cnt;
241 } else {
242 *err = "Err: Invalid number of threads";
243 return false;
244 }
245
246 if (argc > 4) {
247 uint32_t work_size = 0;
248 ret = str_uint32_t(argv[4], NULL, 0, true, &work_size);
249
250 if (ret == EOK && work_size <= 10000) {
251 bench->array_size = work_size;
252 } else {
253 *err = "Err: Work size too large";
254 return false;
255 }
256 } else {
257 bench->array_size = 0;
258 }
259
260 if (0 < bench->array_size) {
261 bench->array = malloc(bench->array_size * sizeof(size_t));
262 if (!bench->array) {
263 *err = "Err: Failed to allocate work array";
264 return false;
265 }
266 } else {
267 bench->array = NULL;
268 }
269
270 return true;
271}
272
273int main(int argc, char **argv)
274{
275 const char *err = "(error)";
276 bench_t bench;
277
278 futex_initialize(&bench.done_threads, 0);
279
280 if (!parse_cmd_line(argc, argv, &bench, &err)) {
281 printf("%s\n", err);
282 print_usage();
283 return -1;
284 }
285
286 printf("Running '%s' futex bench in '%zu' threads with '%zu' iterations.\n",
287 bench.type == T_KERN_FUTEX ? "kernel" : "libc",
288 bench.nthreads, bench.iters);
289
290 struct timeval start, end;
291 getuptime(&start);
292
293 run_threads_and_wait(&bench);
294
295 getuptime(&end);
296 int64_t duration = tv_sub(&end, &start);
297
298 if (0 == duration)
299 duration = 1;
300
301 uint64_t secs = (uint64_t)duration / 1000 / 1000;
302 uint64_t total_iters = (uint64_t)bench.iters * bench.nthreads;
303 uint64_t iters_per_sec = total_iters * 1000 * 1000 / duration;
304
305 printf("Completed %" PRIu64 " iterations in %" PRId64 " usecs (%" PRIu64
306 " secs); %" PRIu64 " iters/sec\n",
307 total_iters, duration, secs, iters_per_sec);
308
309 return 0;
310}
311
312
313/**
314 * @}
315 */
Note: See TracBrowser for help on using the repository browser.