tests: use SPDX identifiers
[urcu.git] / tests / benchmark / test_urcu_lfs.c
1 // SPDX-FileCopyrightText: 2010-2012 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
2 // SPDX-FileCopyrightText: 2010 Paolo Bonzini <pbonzini@redhat.com>
3 //
4 // SPDX-License-Identifier: GPL-2.0-or-later
5
6 /*
7 * Userspace RCU library - example lock-free stack
8 */
9
10 #include <stdio.h>
11 #include <pthread.h>
12 #include <stdlib.h>
13 #include <stdint.h>
14 #include <stdbool.h>
15 #include <string.h>
16 #include <sys/types.h>
17 #include <sys/wait.h>
18 #include <unistd.h>
19 #include <stdio.h>
20 #include <errno.h>
21
22 #include <urcu/arch.h>
23 #include <urcu/assert.h>
24 #include <urcu/tls-compat.h>
25 #include "thread-id.h"
26
27 /* hardcoded number of CPUs */
28 #define NR_CPUS 16384
29
30 #ifndef DYNAMIC_LINK_TEST
31 #define _LGPL_SOURCE
32 #endif
33 #include <urcu.h>
34 #include <urcu/cds.h>
35
36 #define POISON_PTR ((void *) 0x42UL)
37
38 /*
39 * External synchronization used.
40 */
41 enum test_sync {
42 TEST_SYNC_NONE = 0,
43 TEST_SYNC_RCU,
44 };
45
46 static enum test_sync test_sync;
47
48 static volatile int test_go, test_stop;
49
50 static unsigned long rduration;
51
52 static unsigned long duration;
53
54 /* read-side C.S. duration, in loops */
55 static unsigned long wdelay;
56
57 static inline void loop_sleep(unsigned long loops)
58 {
59 while (loops-- != 0)
60 caa_cpu_relax();
61 }
62
63 static int verbose_mode;
64
65 static int test_pop, test_pop_all;
66
67 #define printf_verbose(fmt, args...) \
68 do { \
69 if (verbose_mode) \
70 printf(fmt, ## args); \
71 } while (0)
72
73 static unsigned int cpu_affinities[NR_CPUS];
74 static unsigned int next_aff = 0;
75 static int use_affinity = 0;
76
77 pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER;
78
79 static void set_affinity(void)
80 {
81 #ifdef HAVE_SCHED_SETAFFINITY
82 cpu_set_t mask;
83 int cpu, ret;
84 #endif /* HAVE_SCHED_SETAFFINITY */
85
86 if (!use_affinity)
87 return;
88
89 #ifdef HAVE_SCHED_SETAFFINITY
90 ret = pthread_mutex_lock(&affinity_mutex);
91 if (ret) {
92 perror("Error in pthread mutex lock");
93 exit(-1);
94 }
95 cpu = cpu_affinities[next_aff++];
96 ret = pthread_mutex_unlock(&affinity_mutex);
97 if (ret) {
98 perror("Error in pthread mutex unlock");
99 exit(-1);
100 }
101
102 CPU_ZERO(&mask);
103 CPU_SET(cpu, &mask);
104 sched_setaffinity(0, sizeof(mask), &mask);
105 #endif /* HAVE_SCHED_SETAFFINITY */
106 }
107
108 /*
109 * returns 0 if test should end.
110 */
111 static int test_duration_dequeue(void)
112 {
113 return !test_stop;
114 }
115
116 static int test_duration_enqueue(void)
117 {
118 return !test_stop;
119 }
120
121 static DEFINE_URCU_TLS(unsigned long long, nr_dequeues);
122 static DEFINE_URCU_TLS(unsigned long long, nr_enqueues);
123
124 static DEFINE_URCU_TLS(unsigned long long, nr_successful_dequeues);
125 static DEFINE_URCU_TLS(unsigned long long, nr_successful_enqueues);
126
127 static unsigned int nr_enqueuers;
128 static unsigned int nr_dequeuers;
129
130 struct test {
131 struct cds_lfs_node list;
132 struct rcu_head rcu;
133 };
134
135 static struct cds_lfs_stack s;
136
137 static void *thr_enqueuer(void *_count)
138 {
139 unsigned long long *count = _count;
140
141 printf_verbose("thread_begin %s, tid %lu\n",
142 "enqueuer", urcu_get_thread_id());
143
144 set_affinity();
145
146 rcu_register_thread();
147
148 while (!test_go)
149 {
150 }
151 cmm_smp_mb();
152
153 for (;;) {
154 struct test *node = malloc(sizeof(*node));
155 if (!node)
156 goto fail;
157 cds_lfs_node_init(&node->list);
158 cds_lfs_push(&s, &node->list);
159 URCU_TLS(nr_successful_enqueues)++;
160
161 if (caa_unlikely(wdelay))
162 loop_sleep(wdelay);
163 fail:
164 URCU_TLS(nr_enqueues)++;
165 if (caa_unlikely(!test_duration_enqueue()))
166 break;
167 }
168
169 rcu_unregister_thread();
170
171 count[0] = URCU_TLS(nr_enqueues);
172 count[1] = URCU_TLS(nr_successful_enqueues);
173 printf_verbose("enqueuer thread_end, tid %lu, "
174 "enqueues %llu successful_enqueues %llu\n",
175 urcu_get_thread_id(),
176 URCU_TLS(nr_enqueues),
177 URCU_TLS(nr_successful_enqueues));
178 return ((void*)1);
179
180 }
181
182 static
183 void free_node_cb(struct rcu_head *head)
184 {
185 struct test *node =
186 caa_container_of(head, struct test, rcu);
187 free(node);
188 }
189
190 static
191 void do_test_pop(enum test_sync sync)
192 {
193 struct cds_lfs_node *snode;
194
195 if (sync == TEST_SYNC_RCU)
196 rcu_read_lock();
197 snode = __cds_lfs_pop(&s);
198 if (sync == TEST_SYNC_RCU)
199 rcu_read_unlock();
200 if (snode) {
201 struct test *node;
202
203 snode->next = POISON_PTR;
204 node = caa_container_of(snode,
205 struct test, list);
206 if (sync == TEST_SYNC_RCU)
207 call_rcu(&node->rcu, free_node_cb);
208 else
209 free(node);
210 URCU_TLS(nr_successful_dequeues)++;
211 }
212 URCU_TLS(nr_dequeues)++;
213 }
214
215 static
216 void do_test_pop_all(enum test_sync sync)
217 {
218 struct cds_lfs_node *snode;
219 struct cds_lfs_head *head;
220 struct cds_lfs_node *n;
221
222 head = __cds_lfs_pop_all(&s);
223 cds_lfs_for_each_safe(head, snode, n) {
224 struct test *node;
225
226 snode->next = POISON_PTR;
227 node = caa_container_of(snode, struct test, list);
228 if (sync == TEST_SYNC_RCU)
229 call_rcu(&node->rcu, free_node_cb);
230 else
231 free(node);
232 URCU_TLS(nr_successful_dequeues)++;
233 URCU_TLS(nr_dequeues)++;
234 }
235
236 }
237
238 static void *thr_dequeuer(void *_count)
239 {
240 unsigned long long *count = _count;
241 unsigned int counter = 0;
242
243 printf_verbose("thread_begin %s, tid %lu\n",
244 "dequeuer", urcu_get_thread_id());
245
246 set_affinity();
247
248 rcu_register_thread();
249
250 while (!test_go)
251 {
252 }
253 cmm_smp_mb();
254
255 urcu_posix_assert(test_pop || test_pop_all);
256
257 for (;;) {
258 if (test_pop && test_pop_all) {
259 /* both pop and pop all */
260 if (counter & 1)
261 do_test_pop(test_sync);
262 else
263 do_test_pop_all(test_sync);
264 counter++;
265 } else {
266 if (test_pop)
267 do_test_pop(test_sync);
268 else
269 do_test_pop_all(test_sync);
270 }
271
272 if (caa_unlikely(!test_duration_dequeue()))
273 break;
274 if (caa_unlikely(rduration))
275 loop_sleep(rduration);
276 }
277
278 rcu_unregister_thread();
279
280 printf_verbose("dequeuer thread_end, tid %lu, "
281 "dequeues %llu, successful_dequeues %llu\n",
282 urcu_get_thread_id(),
283 URCU_TLS(nr_dequeues),
284 URCU_TLS(nr_successful_dequeues));
285 count[0] = URCU_TLS(nr_dequeues);
286 count[1] = URCU_TLS(nr_successful_dequeues);
287 return ((void*)2);
288 }
289
290 static void test_end(unsigned long long *nr_dequeues_l)
291 {
292 struct cds_lfs_node *snode;
293
294 do {
295 snode = __cds_lfs_pop(&s);
296 if (snode) {
297 struct test *node;
298
299 node = caa_container_of(snode, struct test, list);
300 free(node);
301 (*nr_dequeues_l)++;
302 }
303 } while (snode);
304 }
305
306 static void show_usage(char **argv)
307 {
308 printf("Usage : %s nr_dequeuers nr_enqueuers duration (s) <OPTIONS>\n",
309 argv[0]);
310 printf("OPTIONS:\n");
311 printf(" [-d delay] (enqueuer period (in loops))\n");
312 printf(" [-c duration] (dequeuer period (in loops))\n");
313 printf(" [-v] (verbose output)\n");
314 printf(" [-a cpu#] [-a cpu#]... (affinity)\n");
315 printf(" [-p] (test pop)\n");
316 printf(" [-P] (test pop_all, enabled by default)\n");
317 printf(" [-R] (use RCU external synchronization)\n");
318 printf(" Note: default: no external synchronization used.\n");
319 printf("\n");
320 }
321
322 int main(int argc, char **argv)
323 {
324 int err;
325 pthread_t *tid_enqueuer, *tid_dequeuer;
326 void *tret;
327 unsigned long long *count_enqueuer, *count_dequeuer;
328 unsigned long long tot_enqueues = 0, tot_dequeues = 0;
329 unsigned long long tot_successful_enqueues = 0,
330 tot_successful_dequeues = 0;
331 unsigned long long end_dequeues = 0;
332 int i, a;
333 unsigned int i_thr;
334
335 if (argc < 4) {
336 show_usage(argv);
337 return -1;
338 }
339
340 err = sscanf(argv[1], "%u", &nr_dequeuers);
341 if (err != 1) {
342 show_usage(argv);
343 return -1;
344 }
345
346 err = sscanf(argv[2], "%u", &nr_enqueuers);
347 if (err != 1) {
348 show_usage(argv);
349 return -1;
350 }
351
352 err = sscanf(argv[3], "%lu", &duration);
353 if (err != 1) {
354 show_usage(argv);
355 return -1;
356 }
357
358 for (i = 4; i < argc; i++) {
359 if (argv[i][0] != '-')
360 continue;
361 switch (argv[i][1]) {
362 case 'a':
363 if (argc < i + 2) {
364 show_usage(argv);
365 return -1;
366 }
367 a = atoi(argv[++i]);
368 cpu_affinities[next_aff++] = a;
369 use_affinity = 1;
370 printf_verbose("Adding CPU %d affinity\n", a);
371 break;
372 case 'c':
373 if (argc < i + 2) {
374 show_usage(argv);
375 return -1;
376 }
377 rduration = atol(argv[++i]);
378 break;
379 case 'd':
380 if (argc < i + 2) {
381 show_usage(argv);
382 return -1;
383 }
384 wdelay = atol(argv[++i]);
385 break;
386 case 'v':
387 verbose_mode = 1;
388 break;
389 case 'p':
390 test_pop = 1;
391 break;
392 case 'P':
393 test_pop_all = 1;
394 break;
395 case 'R':
396 test_sync = TEST_SYNC_RCU;
397 break;
398 }
399 }
400
401 /* activate pop_all test by default */
402 if (!test_pop && !test_pop_all)
403 test_pop_all = 1;
404
405 printf_verbose("running test for %lu seconds, %u enqueuers, "
406 "%u dequeuers.\n",
407 duration, nr_enqueuers, nr_dequeuers);
408 if (test_pop)
409 printf_verbose("pop test activated.\n");
410 if (test_pop_all)
411 printf_verbose("pop_all test activated.\n");
412 if (test_sync == TEST_SYNC_RCU)
413 printf_verbose("External sync: RCU.\n");
414 else
415 printf_verbose("External sync: none.\n");
416 printf_verbose("Writer delay : %lu loops.\n", rduration);
417 printf_verbose("Reader duration : %lu loops.\n", wdelay);
418 printf_verbose("thread %-6s, tid %lu\n",
419 "main", urcu_get_thread_id());
420
421 tid_enqueuer = calloc(nr_enqueuers, sizeof(*tid_enqueuer));
422 tid_dequeuer = calloc(nr_dequeuers, sizeof(*tid_dequeuer));
423 count_enqueuer = calloc(nr_enqueuers, 2 * sizeof(*count_enqueuer));
424 count_dequeuer = calloc(nr_dequeuers, 2 * sizeof(*count_dequeuer));
425 cds_lfs_init(&s);
426 err = create_all_cpu_call_rcu_data(0);
427 if (err) {
428 printf("Per-CPU call_rcu() worker threads unavailable. Using default global worker thread.\n");
429 }
430
431 next_aff = 0;
432
433 for (i_thr = 0; i_thr < nr_enqueuers; i_thr++) {
434 err = pthread_create(&tid_enqueuer[i_thr], NULL, thr_enqueuer,
435 &count_enqueuer[2 * i_thr]);
436 if (err != 0)
437 exit(1);
438 }
439 for (i_thr = 0; i_thr < nr_dequeuers; i_thr++) {
440 err = pthread_create(&tid_dequeuer[i_thr], NULL, thr_dequeuer,
441 &count_dequeuer[2 * i_thr]);
442 if (err != 0)
443 exit(1);
444 }
445
446 cmm_smp_mb();
447
448 test_go = 1;
449
450 for (i_thr = 0; i_thr < duration; i_thr++) {
451 sleep(1);
452 if (verbose_mode) {
453 fwrite(".", sizeof(char), 1, stdout);
454 fflush(stdout);
455 }
456 }
457
458 test_stop = 1;
459
460 for (i_thr = 0; i_thr < nr_enqueuers; i_thr++) {
461 err = pthread_join(tid_enqueuer[i_thr], &tret);
462 if (err != 0)
463 exit(1);
464 tot_enqueues += count_enqueuer[2 * i_thr];
465 tot_successful_enqueues += count_enqueuer[2 * i_thr + 1];
466 }
467 for (i_thr = 0; i_thr < nr_dequeuers; i_thr++) {
468 err = pthread_join(tid_dequeuer[i_thr], &tret);
469 if (err != 0)
470 exit(1);
471 tot_dequeues += count_dequeuer[2 * i_thr];
472 tot_successful_dequeues += count_dequeuer[2 * i_thr + 1];
473 }
474
475 test_end(&end_dequeues);
476
477 printf_verbose("total number of enqueues : %llu, dequeues %llu\n",
478 tot_enqueues, tot_dequeues);
479 printf_verbose("total number of successful enqueues : %llu, "
480 "successful dequeues %llu\n",
481 tot_successful_enqueues, tot_successful_dequeues);
482 printf("SUMMARY %-25s testdur %4lu nr_enqueuers %3u wdelay %6lu "
483 "nr_dequeuers %3u "
484 "rdur %6lu nr_enqueues %12llu nr_dequeues %12llu "
485 "successful enqueues %12llu successful dequeues %12llu "
486 "end_dequeues %llu nr_ops %12llu\n",
487 argv[0], duration, nr_enqueuers, wdelay,
488 nr_dequeuers, rduration, tot_enqueues, tot_dequeues,
489 tot_successful_enqueues,
490 tot_successful_dequeues, end_dequeues,
491 tot_enqueues + tot_dequeues);
492 if (tot_successful_enqueues != tot_successful_dequeues + end_dequeues)
493 printf("WARNING! Discrepancy between nr succ. enqueues %llu vs "
494 "succ. dequeues + end dequeues %llu.\n",
495 tot_successful_enqueues,
496 tot_successful_dequeues + end_dequeues);
497
498 free_all_cpu_call_rcu_data();
499 cds_lfs_destroy(&s);
500 free(count_enqueuer);
501 free(count_dequeuer);
502 free(tid_enqueuer);
503 free(tid_dequeuer);
504
505 return 0;
506 }
This page took 0.040131 seconds and 5 git commands to generate.