Home | History | Annotate | Line # | Download | only in benchmark
      1 // SPDX-FileCopyrightText: 2009 Mathieu Desnoyers <mathieu.desnoyers (at) efficios.com>
      2 //
      3 // SPDX-License-Identifier: GPL-2.0-or-later
      4 
      5 /*
      6  * Userspace RCU library - test program
      7  */
      8 
      9 #include <stdio.h>
     10 #include <pthread.h>
     11 #include <stdlib.h>
     12 #include <string.h>
     13 #include <sys/types.h>
     14 #include <sys/wait.h>
     15 #include <unistd.h>
     16 #include <stdio.h>
     17 #include <errno.h>
     18 
     19 #include <urcu/arch.h>
     20 #include <urcu/assert.h>
     21 #include <urcu/tls-compat.h>
     22 #include "thread-id.h"
     23 
     24 /* hardcoded number of CPUs */
     25 #define NR_CPUS 16384
     26 
     27 #ifndef DYNAMIC_LINK_TEST
     28 #define _LGPL_SOURCE
     29 #endif
     30 #include <urcu.h>
     31 
     32 struct test_array {
     33 	int a;
     34 };
     35 
     36 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
     37 
     38 static unsigned long wdelay;
     39 
     40 static volatile struct test_array test_array = { 8 };
     41 
     42 static unsigned long duration;
     43 
     44 /* read-side C.S. duration, in loops */
     45 static unsigned long rduration;
     46 
     47 /* write-side C.S. duration, in loops */
     48 static unsigned long wduration;
     49 
     50 static inline void loop_sleep(unsigned long loops)
     51 {
     52 	while (loops-- != 0)
     53 		caa_cpu_relax();
     54 }
     55 
     56 static int verbose_mode;
     57 
     58 #define printf_verbose(fmt, args...)		\
     59 	do {					\
     60 		if (verbose_mode)		\
     61 			printf(fmt, args);	\
     62 	} while (0)
     63 
     64 static unsigned int cpu_affinities[NR_CPUS];
     65 static unsigned int next_aff = 0;
     66 static int use_affinity = 0;
     67 
     68 pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER;
     69 
     70 static void set_affinity(void)
     71 {
     72 #ifdef HAVE_SCHED_SETAFFINITY
     73 	cpu_set_t mask;
     74 	int cpu, ret;
     75 #endif /* HAVE_SCHED_SETAFFINITY */
     76 
     77 	if (!use_affinity)
     78 		return;
     79 
     80 #ifdef HAVE_SCHED_SETAFFINITY
     81 	ret = pthread_mutex_lock(&affinity_mutex);
     82 	if (ret) {
     83 		perror("Error in pthread mutex lock");
     84 		exit(-1);
     85 	}
     86 	cpu = cpu_affinities[next_aff++];
     87 	ret = pthread_mutex_unlock(&affinity_mutex);
     88 	if (ret) {
     89 		perror("Error in pthread mutex unlock");
     90 		exit(-1);
     91 	}
     92 	CPU_ZERO(&mask);
     93 	CPU_SET(cpu, &mask);
     94 	sched_setaffinity(0, sizeof(mask), &mask);
     95 #endif /* HAVE_SCHED_SETAFFINITY */
     96 }
     97 
     98 static DEFINE_URCU_TLS(unsigned long long, nr_writes);
     99 static DEFINE_URCU_TLS(unsigned long long, nr_reads);
    100 
    101 static
    102 unsigned long long __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *tot_nr_writes;
    103 static
    104 unsigned long long __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *tot_nr_reads;
    105 
    106 static unsigned int nr_readers;
    107 static unsigned int nr_writers;
    108 
    109 pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER;
    110 
    111 static
    112 void *thr_reader(void *data)
    113 {
    114 	unsigned long tidx = (unsigned long)data;
    115 
    116 	printf_verbose("thread_begin %s, tid %lu\n",
    117 			"reader", urcu_get_thread_id());
    118 
    119 	set_affinity();
    120 
    121 	wait_until_go();
    122 
    123 	for (;;) {
    124 		int v;
    125 
    126 		pthread_mutex_lock(&lock);
    127 		v = test_array.a;
    128 		urcu_posix_assert(v == 8);
    129 		if (caa_unlikely(rduration))
    130 			loop_sleep(rduration);
    131 		pthread_mutex_unlock(&lock);
    132 		URCU_TLS(nr_reads)++;
    133 		if (caa_unlikely(!test_duration_read()))
    134 			break;
    135 	}
    136 
    137 	tot_nr_reads[tidx] = URCU_TLS(nr_reads);
    138 	printf_verbose("thread_end %s, tid %lu\n",
    139 			"reader", urcu_get_thread_id());
    140 	return ((void*)1);
    141 
    142 }
    143 
    144 static
    145 void *thr_writer(void *data)
    146 {
    147 	unsigned long wtidx = (unsigned long)data;
    148 
    149 	printf_verbose("thread_begin %s, tid %lu\n",
    150 			"writer", urcu_get_thread_id());
    151 
    152 	set_affinity();
    153 
    154 	wait_until_go();
    155 
    156 	for (;;) {
    157 		pthread_mutex_lock(&lock);
    158 		test_array.a = 0;
    159 		test_array.a = 8;
    160 		if (caa_unlikely(wduration))
    161 			loop_sleep(wduration);
    162 		pthread_mutex_unlock(&lock);
    163 		URCU_TLS(nr_writes)++;
    164 		if (caa_unlikely(!test_duration_write()))
    165 			break;
    166 		if (caa_unlikely(wdelay))
    167 			loop_sleep(wdelay);
    168 	}
    169 
    170 	printf_verbose("thread_end %s, tid %lu\n",
    171 			"writer", urcu_get_thread_id());
    172 	tot_nr_writes[wtidx] = URCU_TLS(nr_writes);
    173 	return ((void*)2);
    174 }
    175 
    176 static
    177 void show_usage(char **argv)
    178 {
    179 	printf("Usage : %s nr_readers nr_writers duration (s) <OPTIONS>\n",
    180 		argv[0]);
    181 	printf("OPTIONS:\n");
    182 	printf("	[-d delay] (writer period (us))\n");
    183 	printf("	[-c duration] (reader C.S. duration (in loops))\n");
    184 	printf("	[-e duration] (writer C.S. duration (in loops))\n");
    185 	printf("	[-v] (verbose output)\n");
    186 	printf("	[-a cpu#] [-a cpu#]... (affinity)\n");
    187 	printf("\n");
    188 }
    189 
    190 int main(int argc, char **argv)
    191 {
    192 	int err;
    193 	pthread_t *tid_reader, *tid_writer;
    194 	void *tret;
    195 	unsigned long long *count_reader, *count_writer;
    196 	unsigned long long tot_reads = 0, tot_writes = 0;
    197 	int i, a;
    198 	unsigned int i_thr;
    199 
    200 	if (argc < 4) {
    201 		show_usage(argv);
    202 		return -1;
    203 	}
    204 	cmm_smp_mb();
    205 
    206 	err = sscanf(argv[1], "%u", &nr_readers);
    207 	if (err != 1) {
    208 		show_usage(argv);
    209 		return -1;
    210 	}
    211 
    212 	err = sscanf(argv[2], "%u", &nr_writers);
    213 	if (err != 1) {
    214 		show_usage(argv);
    215 		return -1;
    216 	}
    217 
    218 	err = sscanf(argv[3], "%lu", &duration);
    219 	if (err != 1) {
    220 		show_usage(argv);
    221 		return -1;
    222 	}
    223 
    224 	for (i = 4; i < argc; i++) {
    225 		if (argv[i][0] != '-')
    226 			continue;
    227 		switch (argv[i][1]) {
    228 		case 'a':
    229 			if (argc < i + 2) {
    230 				show_usage(argv);
    231 				return -1;
    232 			}
    233 			a = atoi(argv[++i]);
    234 			cpu_affinities[next_aff++] = a;
    235 			use_affinity = 1;
    236 			printf_verbose("Adding CPU %d affinity\n", a);
    237 			break;
    238 		case 'c':
    239 			if (argc < i + 2) {
    240 				show_usage(argv);
    241 				return -1;
    242 			}
    243 			rduration = atol(argv[++i]);
    244 			break;
    245 		case 'd':
    246 			if (argc < i + 2) {
    247 				show_usage(argv);
    248 				return -1;
    249 			}
    250 			wdelay = atol(argv[++i]);
    251 			break;
    252 		case 'e':
    253 			if (argc < i + 2) {
    254 				show_usage(argv);
    255 				return -1;
    256 			}
    257 			wduration = atol(argv[++i]);
    258 			break;
    259 		case 'v':
    260 			verbose_mode = 1;
    261 			break;
    262 		}
    263 	}
    264 
    265 	printf_verbose("running test for %lu seconds, %u readers, %u writers.\n",
    266 		duration, nr_readers, nr_writers);
    267 	printf_verbose("Writer delay : %lu loops.\n", wdelay);
    268 	printf_verbose("Reader duration : %lu loops.\n", rduration);
    269 	printf_verbose("thread %-6s, tid %lu\n",
    270 			"main", urcu_get_thread_id());
    271 
    272 	tid_reader = calloc(nr_readers, sizeof(*tid_reader));
    273 	tid_writer = calloc(nr_writers, sizeof(*tid_writer));
    274 	count_reader = calloc(nr_readers, sizeof(*count_reader));
    275 	count_writer = calloc(nr_writers, sizeof(*count_writer));
    276 	tot_nr_reads = calloc(nr_readers, sizeof(*tot_nr_reads));
    277 	tot_nr_writes = calloc(nr_writers, sizeof(*tot_nr_writes));
    278 
    279 	next_aff = 0;
    280 
    281 	for (i_thr = 0; i_thr < nr_readers; i_thr++) {
    282 		err = pthread_create(&tid_reader[i_thr], NULL, thr_reader,
    283 				     (void *)(long)i_thr);
    284 		if (err != 0)
    285 			exit(1);
    286 	}
    287 	for (i_thr = 0; i_thr < nr_writers; i_thr++) {
    288 		err = pthread_create(&tid_writer[i_thr], NULL, thr_writer,
    289 				     (void *)(long)i_thr);
    290 		if (err != 0)
    291 			exit(1);
    292 	}
    293 
    294 	test_for(duration);
    295 
    296 	for (i_thr = 0; i_thr < nr_readers; i_thr++) {
    297 		err = pthread_join(tid_reader[i_thr], &tret);
    298 		if (err != 0)
    299 			exit(1);
    300 		tot_reads += tot_nr_reads[i_thr];
    301 	}
    302 	for (i_thr = 0; i_thr < nr_writers; i_thr++) {
    303 		err = pthread_join(tid_writer[i_thr], &tret);
    304 		if (err != 0)
    305 			exit(1);
    306 		tot_writes += tot_nr_writes[i_thr];
    307 	}
    308 
    309 	printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads,
    310 	       tot_writes);
    311 	printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu "
    312 		"nr_writers %3u "
    313 		"wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n",
    314 		argv[0], duration, nr_readers, rduration, wduration,
    315 		nr_writers, wdelay, tot_reads, tot_writes,
    316 		tot_reads + tot_writes);
    317 
    318 	free(tid_reader);
    319 	free(tid_writer);
    320 	free(count_reader);
    321 	free(count_writer);
    322 	free(tot_nr_reads);
    323 	free(tot_nr_writes);
    324 	return 0;
    325 }
    326