From: Mathieu Desnoyers Date: Fri, 9 Aug 2013 22:13:23 +0000 (-0400) Subject: tests: split in check, regtest and bench targets X-Git-Tag: v0.8.0~6 X-Git-Url: http://git.liburcu.org/?p=urcu.git;a=commitdiff_plain;h=f5ab766ee2c8300cb00ca5878b1cb464f960a66d tests: split in check, regtest and bench targets Allow make check to run without requiring build machines to use hours of CPU time. make check: short unit tests make regtest: long regression tests make bench: long benchmarks Fixes #611 Signed-off-by: Mathieu Desnoyers --- diff --git a/.gitignore b/.gitignore index 00dbc40..7b6704b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,75 +2,77 @@ urcu/arch.h urcu/uatomic.h tests/api.h -tests/urcutorture -tests/urcutorture-yield -tests/urcu-asm.S -tests/test_rwlock_timing -tests/test_urcu -tests/test_urcu_dynamic_link -tests/test_urcu_timing -tests/test_urcu_yield -tests/test_looplen -tests/test_mutex -tests/test_perthreadlock -tests/test_perthreadlock_timing -tests/test_qsbr -tests/test_qsbr_dynamic_link -tests/test_qsbr_gc -tests/test_qsbr_lgc -tests/test_qsbr_timing -tests/test_rwlock -tests/test_uatomic -tests/test_urcu_gc -tests/test_urcu_gc_mb -tests/test_urcu_lgc -tests/test_urcu_lgc_mb -tests/test_urcu_mb -tests/test_urcu_mb_defer -tests/test_urcu_assign -tests/test_urcu_assign_dynamic_link -tests/test_urcu_bp -tests/test_urcu_bp_dynamic_link -tests/rcutorture_qsbr -tests/rcutorture_urcu -tests/rcutorture_urcu_bp -tests/rcutorture_urcu_mb -tests/rcutorture_urcu_qsbr -tests/rcutorture_urcu_signal -tests/test_cycles_per_loop -tests/test_urcu_defer -tests/test_urcu_hash -tests/test_urcu_lfq -tests/test_urcu_lfq_dynlink -tests/test_urcu_lfs -tests/test_urcu_lfs_dynlink -tests/test_urcu_mb_gc -tests/test_urcu_mb_lgc -tests/test_urcu_qsbr -tests/test_urcu_qsbr_dynamic_link -tests/test_urcu_qsbr_gc -tests/test_urcu_qsbr_lgc -tests/test_urcu_qsbr_timing -tests/test_urcu_signal -tests/test_urcu_signal_dynamic_link -tests/test_urcu_signal_gc -tests/test_urcu_signal_lgc -tests/test_urcu_signal_timing -tests/test_urcu_signal_yield -tests/test_urcu_wfq -tests/test_urcu_wfq_dynlink -tests/test_urcu_wfs -tests/test_urcu_wfs_dynlink -tests/test_urcu_fork -tests/test_urcu_ja -tests/test_urcu_ja_range -tests/test_urcu_lfs_rcu -tests/test_urcu_lfs_rcu_dynlink -tests/test_urcu_multiflavor -tests/test_urcu_multiflavor_dynlink -tests/test_urcu_wfcq -tests/test_urcu_wfcq_dynlink -tests/*.log + +tests/unit/urcu-asm.S +tests/unit/test_uatomic +tests/unit/test_urcu_multiflavor +tests/unit/test_urcu_multiflavor_dynlink + +tests/regression/rcutorture_qsbr +tests/regression/rcutorture_urcu +tests/regression/rcutorture_urcu_bp +tests/regression/rcutorture_urcu_mb +tests/regression/rcutorture_urcu_qsbr +tests/regression/rcutorture_urcu_signal +tests/regression/test_urcu_fork + +tests/benchmark/test_rwlock_timing +tests/benchmark/test_urcu +tests/benchmark/test_urcu_dynamic_link +tests/benchmark/test_urcu_timing +tests/benchmark/test_urcu_yield +tests/benchmark/test_looplen +tests/benchmark/test_mutex +tests/benchmark/test_perthreadlock +tests/benchmark/test_perthreadlock_timing +tests/benchmark/test_qsbr +tests/benchmark/test_qsbr_dynamic_link +tests/benchmark/test_qsbr_gc +tests/benchmark/test_qsbr_lgc +tests/benchmark/test_qsbr_timing +tests/benchmark/test_rwlock +tests/benchmark/test_urcu_gc +tests/benchmark/test_urcu_gc_mb +tests/benchmark/test_urcu_lgc +tests/benchmark/test_urcu_lgc_mb +tests/benchmark/test_urcu_mb +tests/benchmark/test_urcu_mb_defer +tests/benchmark/test_urcu_assign +tests/benchmark/test_urcu_assign_dynamic_link +tests/benchmark/test_urcu_bp +tests/benchmark/test_urcu_bp_dynamic_link +tests/benchmark/test_cycles_per_loop +tests/benchmark/test_urcu_defer +tests/benchmark/test_urcu_hash +tests/benchmark/test_urcu_lfq +tests/benchmark/test_urcu_lfq_dynlink +tests/benchmark/test_urcu_lfs +tests/benchmark/test_urcu_lfs_dynlink +tests/benchmark/test_urcu_mb_gc +tests/benchmark/test_urcu_mb_lgc +tests/benchmark/test_urcu_qsbr +tests/benchmark/test_urcu_qsbr_dynamic_link +tests/benchmark/test_urcu_qsbr_gc +tests/benchmark/test_urcu_qsbr_lgc +tests/benchmark/test_urcu_qsbr_timing +tests/benchmark/test_urcu_signal +tests/benchmark/test_urcu_signal_dynamic_link +tests/benchmark/test_urcu_signal_gc +tests/benchmark/test_urcu_signal_lgc +tests/benchmark/test_urcu_signal_timing +tests/benchmark/test_urcu_signal_yield +tests/benchmark/test_urcu_wfq +tests/benchmark/test_urcu_wfq_dynlink +tests/benchmark/test_urcu_wfs +tests/benchmark/test_urcu_wfs_dynlink +tests/benchmark/test_urcu_ja +tests/benchmark/test_urcu_ja_range +tests/benchmark/test_urcu_lfs_rcu +tests/benchmark/test_urcu_lfs_rcu_dynlink +tests/benchmark/test_urcu_wfcq +tests/benchmark/test_urcu_wfcq_dynlink + +tests/benchmark/*.log *.so doc/examples/urcu-flavors/qsbr diff --git a/Makefile.am b/Makefile.am index 0a4d357..e09778f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -29,8 +29,7 @@ EXTRA_DIST = $(top_srcdir)/urcu/arch/*.h $(top_srcdir)/urcu/uatomic/*.h \ gpl-2.0.txt lgpl-2.1.txt lgpl-relicensing.txt \ LICENSE compat_arch_x86.c \ urcu-call-rcu-impl.h urcu-defer-impl.h \ - rculfhash-internal.h \ - $(top_srcdir)/tests/*.sh + rculfhash-internal.h if COMPAT_ARCH COMPAT=compat_arch_@ARCHTYPE@.c @@ -84,3 +83,9 @@ pkgconfig_DATA = liburcu-cds.pc liburcu.pc liburcu-bp.pc liburcu-qsbr.pc \ dist_doc_DATA = README ChangeLog dist_noinst_DATA = CodingStyle + +.PHONY: bench regtest +bench: + cd tests && $(MAKE) $(AM_MAKEFLAGS) bench +regtest: + cd tests && $(MAKE) $(AM_MAKEFLAGS) regtest diff --git a/configure.ac b/configure.ac index 52b20d8..fc1240d 100644 --- a/configure.ac +++ b/configure.ac @@ -293,6 +293,10 @@ AC_CONFIG_FILES([ doc/Makefile doc/examples/Makefile tests/Makefile + tests/common/Makefile + tests/unit/Makefile + tests/benchmark/Makefile + tests/regression/Makefile liburcu.pc liburcu-bp.pc liburcu-cds.pc diff --git a/tests/Makefile.am b/tests/Makefile.am index aa36bf4..d8fc5ef 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,235 +1,8 @@ -AM_LDFLAGS=-lpthread -AM_CFLAGS=-I$(top_srcdir) -I$(top_builddir) -g +SUBDIRS = common unit benchmark regression -noinst_PROGRAMS = test_urcu test_urcu_dynamic_link test_urcu_timing \ - test_urcu_signal test_urcu_signal_dynamic_link test_urcu_signal_timing \ - test_rwlock_timing test_rwlock test_perthreadlock_timing \ - test_perthreadlock test_urcu_yield test_urcu_signal_yield test_urcu_mb \ - test_urcu_qsbr_timing test_urcu_qsbr rcutorture_urcu rcutorture_urcu_signal \ - rcutorture_urcu_mb rcutorture_urcu_bp rcutorture_urcu_qsbr \ - test_mutex test_looplen test_urcu_gc test_urcu_signal_gc \ - test_urcu_lgc \ - test_urcu_mb_gc test_urcu_qsbr_gc test_urcu_qsbr_lgc test_urcu_signal_lgc \ - test_urcu_mb_lgc test_urcu_qsbr_dynamic_link test_urcu_defer \ - test_uatomic test_urcu_assign test_urcu_assign_dynamic_link \ - test_urcu_bp test_urcu_bp_dynamic_link test_cycles_per_loop \ - test_urcu_lfq test_urcu_wfq test_urcu_lfs test_urcu_wfs \ - test_urcu_lfs_rcu \ - test_urcu_wfcq \ - test_urcu_wfq_dynlink test_urcu_wfs_dynlink \ - test_urcu_wfcq_dynlink \ - test_urcu_lfq_dynlink test_urcu_lfs_dynlink test_urcu_hash \ - test_urcu_lfs_rcu_dynlink \ - test_urcu_multiflavor test_urcu_multiflavor_dynlink \ - test_urcu_fork -noinst_HEADERS = rcutorture.h test_urcu_multiflavor.h cpuset.h thread-id.h +.PHONY: bench regtest -if COMPAT_ARCH -COMPAT=$(top_srcdir)/compat_arch_@ARCHTYPE@.c -else -COMPAT= -endif - -if COMPAT_FUTEX -COMPAT+=$(top_srcdir)/compat_futex.c -endif - -URCU=$(top_srcdir)/urcu.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT) -URCU_QSBR=$(top_srcdir)/urcu-qsbr.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT) -# URCU_MB uses urcu.c but -DRCU_MB must be defined -URCU_MB=$(top_srcdir)/urcu.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT) -# URCU_SIGNAL uses urcu.c but -DRCU_SIGNAL must be defined -URCU_SIGNAL=$(top_srcdir)/urcu.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT) -URCU_BP=$(top_srcdir)/urcu-bp.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT) -URCU_DEFER=$(top_srcdir)/urcu.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT) - -URCU_COMMON_LIB=$(top_builddir)/liburcu-common.la -URCU_LIB=$(top_builddir)/liburcu.la -URCU_QSBR_LIB=$(top_builddir)/liburcu-qsbr.la -URCU_MB_LIB=$(top_builddir)/liburcu-mb.la -URCU_SIGNAL_LIB=$(top_builddir)/liburcu-signal.la -URCU_BP_LIB=$(top_builddir)/liburcu-bp.la -URCU_CDS_LIB=$(top_builddir)/liburcu-cds.la - -EXTRA_DIST = $(top_srcdir)/tests/api.h runall.sh runhash.sh - -test_urcu_SOURCES = test_urcu.c $(URCU) - -test_urcu_dynamic_link_SOURCES = test_urcu.c $(URCU) -test_urcu_dynamic_link_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS) - -test_urcu_timing_SOURCES = test_urcu_timing.c $(URCU) - -test_urcu_yield_SOURCES = test_urcu.c $(URCU) -test_urcu_yield_CFLAGS = -DDEBUG_YIELD $(AM_CFLAGS) - - -test_urcu_qsbr_SOURCES = test_urcu_qsbr.c $(URCU_QSBR) - -test_urcu_qsbr_timing_SOURCES = test_urcu_qsbr_timing.c $(URCU_QSBR) - - -test_urcu_mb_SOURCES = test_urcu.c $(URCU_MB) -test_urcu_mb_CFLAGS = -DRCU_MB $(AM_CFLAGS) - - -test_urcu_signal_SOURCES = test_urcu.c $(URCU_SIGNAL) -test_urcu_signal_CFLAGS = -DRCU_SIGNAL $(AM_CFLAGS) - -test_urcu_signal_dynamic_link_SOURCES = test_urcu.c $(URCU_SIGNAL) -test_urcu_signal_dynamic_link_CFLAGS = -DRCU_SIGNAL -DDYNAMIC_LINK_TEST \ - $(AM_CFLAGS) - -test_urcu_signal_timing_SOURCES = test_urcu_timing.c $(URCU_SIGNAL) -test_urcu_signal_timing_CFLAGS= -DRCU_SIGNAL $(AM_CFLAGS) - -test_urcu_signal_yield_SOURCES = test_urcu.c $(URCU_SIGNAL) -test_urcu_signal_yield_CFLAGS = -DRCU_SIGNAL -DDEBUG_YIELD $(AM_CFLAGS) - -test_urcu_fork_SOURCES = test_urcu_fork.c $(URCU) - -test_rwlock_timing_SOURCES = test_rwlock_timing.c $(URCU_SIGNAL) - -test_rwlock_SOURCES = test_rwlock.c $(URCU_SIGNAL) - -test_perthreadlock_timing_SOURCES = test_perthreadlock_timing.c $(URCU_SIGNAL) - -test_perthreadlock_SOURCES = test_perthreadlock.c $(URCU_SIGNAL) - - -rcutorture_urcu_SOURCES = urcutorture.c -rcutorture_urcu_CFLAGS = -DRCU_MEMBARRIER $(AM_CFLAGS) -rcutorture_urcu_LDADD = $(URCU) - -rcutorture_urcu_mb_SOURCES = urcutorture.c -rcutorture_urcu_mb_CFLAGS = -DRCU_MB $(AM_CFLAGS) -rcutorture_urcu_mb_LDADD = $(URCU_MB_LIB) - -rcutorture_urcu_qsbr_SOURCES = urcutorture.c -rcutorture_urcu_qsbr_CFLAGS = -DTORTURE_QSBR -DRCU_QSBR $(AM_CFLAGS) -rcutorture_urcu_qsbr_LDADD = $(URCU_QSBR_LIB) - -rcutorture_urcu_signal_SOURCES = urcutorture.c -rcutorture_urcu_signal_CFLAGS = -DRCU_SIGNAL $(AM_CFLAGS) -rcutorture_urcu_signal_LDADD = $(URCU_SIGNAL_LIB) - -rcutorture_urcu_bp_SOURCES = urcutorture.c -rcutorture_urcu_bp_CFLAGS = -DRCU_BP $(AM_CFLAGS) -rcutorture_urcu_bp_LDADD = $(URCU_BP_LIB) - -test_mutex_SOURCES = test_mutex.c $(URCU) - -test_looplen_SOURCES = test_looplen.c - -test_urcu_gc_SOURCES = test_urcu_gc.c $(URCU) - -test_urcu_signal_gc_SOURCES = test_urcu_gc.c $(URCU_SIGNAL) -test_urcu_signal_gc_CFLAGS = -DRCU_SIGNAL $(AM_CFLAGS) - -test_urcu_mb_gc_SOURCES = test_urcu_gc.c $(URCU_MB) -test_urcu_mb_gc_CFLAGS = -DRCU_MB $(AM_CFLAGS) - -test_urcu_qsbr_gc_SOURCES = test_urcu_qsbr_gc.c $(URCU_QSBR) - -test_urcu_qsbr_lgc_SOURCES = test_urcu_qsbr_gc.c $(URCU_QSBR) -test_urcu_qsbr_lgc_CFLAGS = -DTEST_LOCAL_GC $(AM_CFLAGS) - -test_urcu_lgc_SOURCES = test_urcu_gc.c $(URCU) -test_urcu_lgc_CFLAGS = -DTEST_LOCAL_GC $(AM_CFLAGS) - -test_urcu_signal_lgc_SOURCES = test_urcu_gc.c $(URCU_SIGNAL) -test_urcu_signal_lgc_CFLAGS = -DRCU_SIGNAL -DTEST_LOCAL_GC $(AM_CFLAGS) - -test_urcu_mb_lgc_SOURCES = test_urcu_gc.c $(URCU_MB) -test_urcu_mb_lgc_CFLAGS = -DTEST_LOCAL_GC -DRCU_MB $(AM_CFLAGS) - -test_urcu_qsbr_dynamic_link_SOURCES = test_urcu_qsbr.c $(URCU_QSBR) -test_urcu_qsbr_dynamic_link_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS) - -test_urcu_defer_SOURCES = test_urcu_defer.c $(URCU_DEFER) - -test_uatomic_SOURCES = test_uatomic.c $(COMPAT) - -test_cycles_per_loop_SOURCES = test_cycles_per_loop.c - -test_urcu_assign_SOURCES = test_urcu_assign.c $(URCU) - -test_urcu_assign_dynamic_link_SOURCES = test_urcu_assign.c $(URCU) -test_urcu_assign_dynamic_link_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS) - -test_urcu_bp_SOURCES = test_urcu_bp.c $(URCU_BP) - -test_urcu_bp_dynamic_link_SOURCES = test_urcu_bp.c $(URCU_BP) -test_urcu_bp_dynamic_link_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS) - -test_urcu_lfq_SOURCES = test_urcu_lfq.c $(URCU) -test_urcu_lfq_LDADD = $(URCU_CDS_LIB) - -test_urcu_lfq_dynlink_SOURCES = test_urcu_lfq.c $(URCU) -test_urcu_lfq_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS) -test_urcu_lfq_dynlink_LDADD = $(URCU_CDS_LIB) - -test_urcu_wfq_SOURCES = test_urcu_wfq.c $(COMPAT) -test_urcu_wfq_LDADD = $(URCU_COMMON_LIB) - -test_urcu_wfq_dynlink_SOURCES = test_urcu_wfq.c -test_urcu_wfq_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS) -test_urcu_wfq_dynlink_LDADD = $(URCU_COMMON_LIB) - -test_urcu_wfcq_SOURCES = test_urcu_wfcq.c $(COMPAT) -test_urcu_wfcq_LDADD = $(URCU_COMMON_LIB) - -test_urcu_wfcq_dynlink_SOURCES = test_urcu_wfcq.c -test_urcu_wfcq_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS) -test_urcu_wfcq_dynlink_LDADD = $(URCU_COMMON_LIB) - -test_urcu_lfs_SOURCES = test_urcu_lfs.c $(URCU) -test_urcu_lfs_LDADD = $(URCU_CDS_LIB) - -test_urcu_lfs_rcu_SOURCES = test_urcu_lfs_rcu.c $(URCU) -test_urcu_lfs_rcu_LDADD = $(URCU_CDS_LIB) - -test_urcu_lfs_dynlink_SOURCES = test_urcu_lfs.c $(URCU) -test_urcu_lfs_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS) -test_urcu_lfs_dynlink_LDADD = $(URCU_CDS_LIB) - -test_urcu_lfs_rcu_dynlink_SOURCES = test_urcu_lfs_rcu.c $(URCU) -test_urcu_lfs_rcu_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS) -test_urcu_lfs_rcu_dynlink_LDADD = $(URCU_CDS_LIB) - -test_urcu_wfs_SOURCES = test_urcu_wfs.c $(COMPAT) -test_urcu_wfs_LDADD = $(URCU_COMMON_LIB) - -test_urcu_wfs_dynlink_SOURCES = test_urcu_wfs.c -test_urcu_wfs_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS) -test_urcu_wfs_dynlink_LDADD = $(URCU_COMMON_LIB) - -test_urcu_hash_SOURCES = test_urcu_hash.c test_urcu_hash.h \ - test_urcu_hash_rw.c test_urcu_hash_unique.c $(COMPAT) -test_urcu_hash_CFLAGS = -DRCU_QSBR $(AM_CFLAGS) -test_urcu_hash_LDADD = $(URCU_QSBR_LIB) $(URCU_CDS_LIB) - -test_urcu_multiflavor_SOURCES = test_urcu_multiflavor.c \ - test_urcu_multiflavor-memb.c \ - test_urcu_multiflavor-mb.c \ - test_urcu_multiflavor-signal.c \ - test_urcu_multiflavor-qsbr.c \ - test_urcu_multiflavor-bp.c -test_urcu_multiflavor_LDADD = $(URCU_LIB) $(URCU_MB_LIB) \ - $(URCU_SIGNAL_LIB) $(URCU_QSBR_LIB) $(URCU_BP_LIB) - -test_urcu_multiflavor_dynlink_SOURCES = test_urcu_multiflavor.c \ - test_urcu_multiflavor-memb.c \ - test_urcu_multiflavor-mb.c \ - test_urcu_multiflavor-signal.c \ - test_urcu_multiflavor-qsbr.c \ - test_urcu_multiflavor-bp.c -test_urcu_multiflavor_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS) -test_urcu_multiflavor_dynlink_LDADD = $(URCU_LIB) $(URCU_MB_LIB) \ - $(URCU_SIGNAL_LIB) $(URCU_QSBR_LIB) $(URCU_BP_LIB) - -urcutorture.c: api.h - -check-am: - ./test_uatomic - ./runall.sh +bench: + cd benchmark && $(MAKE) $(AM_MAKEFLAGS) bench +regtest: + cd regression && $(MAKE) $(AM_MAKEFLAGS) regtest diff --git a/tests/api.h b/tests/api.h deleted file mode 100644 index dced0d8..0000000 --- a/tests/api.h +++ /dev/null @@ -1,311 +0,0 @@ -#ifndef _INCLUDE_API_H -#define _INCLUDE_API_H - -#define _GNU_SOURCE -#include "../config.h" - -/* - * common.h: Common Linux kernel-isms. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; but version 2 of the License only due - * to code included from the Linux kernel. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (c) 2006 Paul E. McKenney, IBM. - * - * Much code taken from the Linux kernel. For such code, the option - * to redistribute under later versions of GPL might not be available. - */ - -#include -#include -#include "cpuset.h" - -/* - * Machine parameters. - */ - -#define ____cacheline_internodealigned_in_smp \ - __attribute__((__aligned__(CAA_CACHE_LINE_SIZE))) - -/* - * api_pthreads.h: API mapping to pthreads environment. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. However, please note that much - * of the code in this file derives from the Linux kernel, and that such - * code may not be available except under GPLv2. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (c) 2006 Paul E. McKenney, IBM. - */ - -#include -#include -#include -#include -#include -#include -#include -/* #include "atomic.h" */ - -/* - * Exclusive locking primitives. - */ - -typedef pthread_mutex_t spinlock_t; - -#define DEFINE_SPINLOCK(lock) spinlock_t lock = PTHREAD_MUTEX_INITIALIZER; -#define __SPIN_LOCK_UNLOCKED(lockp) PTHREAD_MUTEX_INITIALIZER - -static void spin_lock_init(spinlock_t *sp) -{ - if (pthread_mutex_init(sp, NULL) != 0) { - perror("spin_lock_init:pthread_mutex_init"); - exit(-1); - } -} - -static void spin_lock(spinlock_t *sp) -{ - if (pthread_mutex_lock(sp) != 0) { - perror("spin_lock:pthread_mutex_lock"); - exit(-1); - } -} - -static void spin_unlock(spinlock_t *sp) -{ - if (pthread_mutex_unlock(sp) != 0) { - perror("spin_unlock:pthread_mutex_unlock"); - exit(-1); - } -} - -#define spin_lock_irqsave(l, f) do { f = 1; spin_lock(l); } while (0) -#define spin_unlock_irqrestore(l, f) do { f = 0; spin_unlock(l); } while (0) - -/* - * Thread creation/destruction primitives. - */ - -typedef pthread_t thread_id_t; - -#define NR_THREADS 128 - -#define __THREAD_ID_MAP_EMPTY ((thread_id_t) 0) -#define __THREAD_ID_MAP_WAITING ((thread_id_t) 1) -thread_id_t __thread_id_map[NR_THREADS]; -spinlock_t __thread_id_map_mutex; - -#define for_each_thread(t) \ - for (t = 0; t < NR_THREADS; t++) - -#define for_each_running_thread(t) \ - for (t = 0; t < NR_THREADS; t++) \ - if ((__thread_id_map[t] != __THREAD_ID_MAP_EMPTY) && \ - (__thread_id_map[t] != __THREAD_ID_MAP_WAITING)) - -#define for_each_tid(t, tid) \ - for (t = 0; t < NR_THREADS; t++) \ - if ((((tid) = __thread_id_map[t]) != __THREAD_ID_MAP_EMPTY) && \ - ((tid) != __THREAD_ID_MAP_WAITING)) - -pthread_key_t thread_id_key; - -static int __smp_thread_id(void) -{ - int i; - thread_id_t tid = pthread_self(); - - for (i = 0; i < NR_THREADS; i++) { - if (__thread_id_map[i] == tid) { - long v = i + 1; /* must be non-NULL. */ - - if (pthread_setspecific(thread_id_key, (void *)v) != 0) { - perror("pthread_setspecific"); - exit(-1); - } - return i; - } - } - spin_lock(&__thread_id_map_mutex); - for (i = 0; i < NR_THREADS; i++) { - if (__thread_id_map[i] == tid) { - spin_unlock(&__thread_id_map_mutex); - return i; - } - } - spin_unlock(&__thread_id_map_mutex); - fprintf(stderr, "smp_thread_id: Rogue thread, id: %lu(%#lx)\n", - (unsigned long) tid, (unsigned long) tid); - exit(-1); -} - -static int smp_thread_id(void) -{ - void *id; - - id = pthread_getspecific(thread_id_key); - if (id == NULL) - return __smp_thread_id(); - return (long)(id - 1); -} - -static thread_id_t create_thread(void *(*func)(void *), void *arg) -{ - thread_id_t tid; - int i; - - spin_lock(&__thread_id_map_mutex); - for (i = 0; i < NR_THREADS; i++) { - if (__thread_id_map[i] == __THREAD_ID_MAP_EMPTY) - break; - } - if (i >= NR_THREADS) { - spin_unlock(&__thread_id_map_mutex); - fprintf(stderr, "Thread limit of %d exceeded!\n", NR_THREADS); - exit(-1); - } - __thread_id_map[i] = __THREAD_ID_MAP_WAITING; - spin_unlock(&__thread_id_map_mutex); - if (pthread_create(&tid, NULL, func, arg) != 0) { - perror("create_thread:pthread_create"); - exit(-1); - } - __thread_id_map[i] = tid; - return tid; -} - -static void *wait_thread(thread_id_t tid) -{ - int i; - void *vp; - - for (i = 0; i < NR_THREADS; i++) { - if (__thread_id_map[i] == tid) - break; - } - if (i >= NR_THREADS){ - fprintf(stderr, "wait_thread: bad tid = %lu(%#lx)\n", - (unsigned long)tid, (unsigned long)tid); - exit(-1); - } - if (pthread_join(tid, &vp) != 0) { - perror("wait_thread:pthread_join"); - exit(-1); - } - __thread_id_map[i] = __THREAD_ID_MAP_EMPTY; - return vp; -} - -static void wait_all_threads(void) -{ - int i; - thread_id_t tid; - - for (i = 1; i < NR_THREADS; i++) { - tid = __thread_id_map[i]; - if (tid != __THREAD_ID_MAP_EMPTY && - tid != __THREAD_ID_MAP_WAITING) - (void)wait_thread(tid); - } -} - -static void run_on(int cpu) -{ -#if HAVE_SCHED_SETAFFINITY - cpu_set_t mask; - - CPU_ZERO(&mask); - CPU_SET(cpu, &mask); -#if SCHED_SETAFFINITY_ARGS == 2 - sched_setaffinity(0, &mask); -#else - sched_setaffinity(0, sizeof(mask), &mask); -#endif -#endif /* HAVE_SCHED_SETAFFINITY */ -} - -/* - * timekeeping -- very crude -- should use MONOTONIC... - */ - -long long get_microseconds(void) -{ - struct timeval tv; - - if (gettimeofday(&tv, NULL) != 0) - abort(); - return ((long long)tv.tv_sec) * 1000000LL + (long long)tv.tv_usec; -} - -/* - * Per-thread variables. - */ - -#define DEFINE_PER_THREAD(type, name) \ - struct { \ - __typeof__(type) v \ - __attribute__((__aligned__(CAA_CACHE_LINE_SIZE))); \ - } __per_thread_##name[NR_THREADS]; -#define DECLARE_PER_THREAD(type, name) extern DEFINE_PER_THREAD(type, name) - -#define per_thread(name, thread) __per_thread_##name[thread].v -#define __get_thread_var(name) per_thread(name, smp_thread_id()) - -#define init_per_thread(name, v) \ - do { \ - int __i_p_t_i; \ - for (__i_p_t_i = 0; __i_p_t_i < NR_THREADS; __i_p_t_i++) \ - per_thread(name, __i_p_t_i) = v; \ - } while (0) - -DEFINE_PER_THREAD(int, smp_processor_id); - -/* - * Bug checks. - */ - -#define BUG_ON(c) do { if (!(c)) abort(); } while (0) - -/* - * Initialization -- Must be called before calling any primitives. - */ - -static void smp_init(void) -{ - int i; - - spin_lock_init(&__thread_id_map_mutex); - __thread_id_map[0] = pthread_self(); - for (i = 1; i < NR_THREADS; i++) - __thread_id_map[i] = __THREAD_ID_MAP_EMPTY; - init_per_thread(smp_processor_id, 0); - if (pthread_key_create(&thread_id_key, NULL) != 0) { - perror("pthread_key_create"); - exit(-1); - } -} - -#endif diff --git a/tests/benchmark/Makefile.am b/tests/benchmark/Makefile.am new file mode 100644 index 0000000..e071034 --- /dev/null +++ b/tests/benchmark/Makefile.am @@ -0,0 +1,186 @@ +AM_LDFLAGS=-lpthread +AM_CFLAGS=-I$(top_srcdir) -I$(top_builddir) -I$(top_srcdir)/tests/common -g + +noinst_PROGRAMS = test_urcu test_urcu_dynamic_link test_urcu_timing \ + test_urcu_signal test_urcu_signal_dynamic_link test_urcu_signal_timing \ + test_rwlock_timing test_rwlock test_perthreadlock_timing \ + test_perthreadlock test_urcu_yield test_urcu_signal_yield test_urcu_mb \ + test_urcu_qsbr_timing test_urcu_qsbr \ + test_mutex test_looplen test_urcu_gc test_urcu_signal_gc \ + test_urcu_lgc \ + test_urcu_mb_gc test_urcu_qsbr_gc test_urcu_qsbr_lgc test_urcu_signal_lgc \ + test_urcu_mb_lgc test_urcu_qsbr_dynamic_link test_urcu_defer \ + test_urcu_assign test_urcu_assign_dynamic_link \ + test_urcu_bp test_urcu_bp_dynamic_link test_cycles_per_loop \ + test_urcu_lfq test_urcu_wfq test_urcu_lfs test_urcu_wfs \ + test_urcu_lfs_rcu \ + test_urcu_wfcq \ + test_urcu_wfq_dynlink test_urcu_wfs_dynlink \ + test_urcu_wfcq_dynlink \ + test_urcu_lfq_dynlink test_urcu_lfs_dynlink test_urcu_hash \ + test_urcu_lfs_rcu_dynlink + +if COMPAT_ARCH +COMPAT=$(top_srcdir)/compat_arch_@ARCHTYPE@.c +else +COMPAT= +endif + +if COMPAT_FUTEX +COMPAT+=$(top_srcdir)/compat_futex.c +endif + +URCU=$(top_srcdir)/urcu.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT) +URCU_QSBR=$(top_srcdir)/urcu-qsbr.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT) +# URCU_MB uses urcu.c but -DRCU_MB must be defined +URCU_MB=$(top_srcdir)/urcu.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT) +# URCU_SIGNAL uses urcu.c but -DRCU_SIGNAL must be defined +URCU_SIGNAL=$(top_srcdir)/urcu.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT) +URCU_BP=$(top_srcdir)/urcu-bp.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT) +URCU_DEFER=$(top_srcdir)/urcu.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT) + +URCU_COMMON_LIB=$(top_builddir)/liburcu-common.la +URCU_LIB=$(top_builddir)/liburcu.la +URCU_QSBR_LIB=$(top_builddir)/liburcu-qsbr.la +URCU_MB_LIB=$(top_builddir)/liburcu-mb.la +URCU_SIGNAL_LIB=$(top_builddir)/liburcu-signal.la +URCU_BP_LIB=$(top_builddir)/liburcu-bp.la +URCU_CDS_LIB=$(top_builddir)/liburcu-cds.la + +EXTRA_DIST = *.sh + +test_urcu_SOURCES = test_urcu.c $(URCU) + +test_urcu_dynamic_link_SOURCES = test_urcu.c $(URCU) +test_urcu_dynamic_link_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS) + +test_urcu_timing_SOURCES = test_urcu_timing.c $(URCU) + +test_urcu_yield_SOURCES = test_urcu.c $(URCU) +test_urcu_yield_CFLAGS = -DDEBUG_YIELD $(AM_CFLAGS) + + +test_urcu_qsbr_SOURCES = test_urcu_qsbr.c $(URCU_QSBR) + +test_urcu_qsbr_timing_SOURCES = test_urcu_qsbr_timing.c $(URCU_QSBR) + + +test_urcu_mb_SOURCES = test_urcu.c $(URCU_MB) +test_urcu_mb_CFLAGS = -DRCU_MB $(AM_CFLAGS) + + +test_urcu_signal_SOURCES = test_urcu.c $(URCU_SIGNAL) +test_urcu_signal_CFLAGS = -DRCU_SIGNAL $(AM_CFLAGS) + +test_urcu_signal_dynamic_link_SOURCES = test_urcu.c $(URCU_SIGNAL) +test_urcu_signal_dynamic_link_CFLAGS = -DRCU_SIGNAL -DDYNAMIC_LINK_TEST \ + $(AM_CFLAGS) + +test_urcu_signal_timing_SOURCES = test_urcu_timing.c $(URCU_SIGNAL) +test_urcu_signal_timing_CFLAGS= -DRCU_SIGNAL $(AM_CFLAGS) + +test_urcu_signal_yield_SOURCES = test_urcu.c $(URCU_SIGNAL) +test_urcu_signal_yield_CFLAGS = -DRCU_SIGNAL -DDEBUG_YIELD $(AM_CFLAGS) + +test_rwlock_timing_SOURCES = test_rwlock_timing.c $(URCU_SIGNAL) + +test_rwlock_SOURCES = test_rwlock.c $(URCU_SIGNAL) + +test_perthreadlock_timing_SOURCES = test_perthreadlock_timing.c $(URCU_SIGNAL) + +test_perthreadlock_SOURCES = test_perthreadlock.c $(URCU_SIGNAL) + +test_mutex_SOURCES = test_mutex.c $(URCU) + +test_looplen_SOURCES = test_looplen.c + +test_urcu_gc_SOURCES = test_urcu_gc.c $(URCU) + +test_urcu_signal_gc_SOURCES = test_urcu_gc.c $(URCU_SIGNAL) +test_urcu_signal_gc_CFLAGS = -DRCU_SIGNAL $(AM_CFLAGS) + +test_urcu_mb_gc_SOURCES = test_urcu_gc.c $(URCU_MB) +test_urcu_mb_gc_CFLAGS = -DRCU_MB $(AM_CFLAGS) + +test_urcu_qsbr_gc_SOURCES = test_urcu_qsbr_gc.c $(URCU_QSBR) + +test_urcu_qsbr_lgc_SOURCES = test_urcu_qsbr_gc.c $(URCU_QSBR) +test_urcu_qsbr_lgc_CFLAGS = -DTEST_LOCAL_GC $(AM_CFLAGS) + +test_urcu_lgc_SOURCES = test_urcu_gc.c $(URCU) +test_urcu_lgc_CFLAGS = -DTEST_LOCAL_GC $(AM_CFLAGS) + +test_urcu_signal_lgc_SOURCES = test_urcu_gc.c $(URCU_SIGNAL) +test_urcu_signal_lgc_CFLAGS = -DRCU_SIGNAL -DTEST_LOCAL_GC $(AM_CFLAGS) + +test_urcu_mb_lgc_SOURCES = test_urcu_gc.c $(URCU_MB) +test_urcu_mb_lgc_CFLAGS = -DTEST_LOCAL_GC -DRCU_MB $(AM_CFLAGS) + +test_urcu_qsbr_dynamic_link_SOURCES = test_urcu_qsbr.c $(URCU_QSBR) +test_urcu_qsbr_dynamic_link_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS) + +test_urcu_defer_SOURCES = test_urcu_defer.c $(URCU_DEFER) + +test_cycles_per_loop_SOURCES = test_cycles_per_loop.c + +test_urcu_assign_SOURCES = test_urcu_assign.c $(URCU) + +test_urcu_assign_dynamic_link_SOURCES = test_urcu_assign.c $(URCU) +test_urcu_assign_dynamic_link_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS) + +test_urcu_bp_SOURCES = test_urcu_bp.c $(URCU_BP) + +test_urcu_bp_dynamic_link_SOURCES = test_urcu_bp.c $(URCU_BP) +test_urcu_bp_dynamic_link_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS) + +test_urcu_lfq_SOURCES = test_urcu_lfq.c $(URCU) +test_urcu_lfq_LDADD = $(URCU_CDS_LIB) + +test_urcu_lfq_dynlink_SOURCES = test_urcu_lfq.c $(URCU) +test_urcu_lfq_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS) +test_urcu_lfq_dynlink_LDADD = $(URCU_CDS_LIB) + +test_urcu_wfq_SOURCES = test_urcu_wfq.c $(COMPAT) +test_urcu_wfq_LDADD = $(URCU_COMMON_LIB) + +test_urcu_wfq_dynlink_SOURCES = test_urcu_wfq.c +test_urcu_wfq_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS) +test_urcu_wfq_dynlink_LDADD = $(URCU_COMMON_LIB) + +test_urcu_wfcq_SOURCES = test_urcu_wfcq.c $(COMPAT) +test_urcu_wfcq_LDADD = $(URCU_COMMON_LIB) + +test_urcu_wfcq_dynlink_SOURCES = test_urcu_wfcq.c +test_urcu_wfcq_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS) +test_urcu_wfcq_dynlink_LDADD = $(URCU_COMMON_LIB) + +test_urcu_lfs_SOURCES = test_urcu_lfs.c $(URCU) +test_urcu_lfs_LDADD = $(URCU_CDS_LIB) + +test_urcu_lfs_rcu_SOURCES = test_urcu_lfs_rcu.c $(URCU) +test_urcu_lfs_rcu_LDADD = $(URCU_CDS_LIB) + +test_urcu_lfs_dynlink_SOURCES = test_urcu_lfs.c $(URCU) +test_urcu_lfs_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS) +test_urcu_lfs_dynlink_LDADD = $(URCU_CDS_LIB) + +test_urcu_lfs_rcu_dynlink_SOURCES = test_urcu_lfs_rcu.c $(URCU) +test_urcu_lfs_rcu_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS) +test_urcu_lfs_rcu_dynlink_LDADD = $(URCU_CDS_LIB) + +test_urcu_wfs_SOURCES = test_urcu_wfs.c $(COMPAT) +test_urcu_wfs_LDADD = $(URCU_COMMON_LIB) + +test_urcu_wfs_dynlink_SOURCES = test_urcu_wfs.c +test_urcu_wfs_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS) +test_urcu_wfs_dynlink_LDADD = $(URCU_COMMON_LIB) + +test_urcu_hash_SOURCES = test_urcu_hash.c test_urcu_hash.h \ + test_urcu_hash_rw.c test_urcu_hash_unique.c $(COMPAT) +test_urcu_hash_CFLAGS = -DRCU_QSBR $(AM_CFLAGS) +test_urcu_hash_LDADD = $(URCU_QSBR_LIB) $(URCU_CDS_LIB) + +.PHONY: bench + +bench: + ./runall.sh diff --git a/tests/benchmark/common.sh b/tests/benchmark/common.sh new file mode 100644 index 0000000..9eb03f8 --- /dev/null +++ b/tests/benchmark/common.sh @@ -0,0 +1,12 @@ +# +# This file is meant to be sourced from other tests scripts. +# + +if [ -x "$URCU_TEST_TIME_BIN" ]; then + test_time_bin="$URCU_TEST_TIME_BIN" +elif [ -x "/usr/bin/time" ]; then + test_time_bin="/usr/bin/time" +else + test_time_bin="" +fi + diff --git a/tests/benchmark/runall.sh b/tests/benchmark/runall.sh new file mode 100755 index 0000000..ced1765 --- /dev/null +++ b/tests/benchmark/runall.sh @@ -0,0 +1,104 @@ +#!/bin/sh + +#run all tests + +#set to number of active CPUS +NUM_CPUS=8 + +#extra options, e.g. for setting affinity on even CPUs : +#EXTRA_OPTS=$(for a in $(seq 0 2 127); do echo -n "-a ${a} "; done) + +#ppc64 striding, use with NUM_CPUS=8 + +#stride 1 +#EXTRA_OPTS=$(for a in $(seq 0 2 15); do echo -n "-a ${a} "; done) +#stride 2 +#EXTRA_OPTS=$(for a in $(seq 0 4 31); do echo -n "-a ${a} "; done) +#stride 4 +#EXTRA_OPTS=$(for a in $(seq 0 8 63); do echo -n "-a ${a} "; done) +#stride 8 +#EXTRA_OPTS=$(for a in $(seq 0 16 127); do echo -n "-a ${a} "; done) + +#Vary update fraction +#x: vary update fraction from 0 to 0.0001 + #fix number of readers and reader C.S. length, vary delay between updates +#y: ops/s + +rm -f runall.log +rm -fr runall.detail.log + + +echo Executing batch RCU test + +DURATION=10 +BATCH_ARRAY="1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 + 131072 262144" +NR_WRITERS=$((${NUM_CPUS} / 2)) + +rm -f batch-rcu.log + +NR_READERS=$((${NUM_CPUS} - ${NR_WRITERS})) +for BATCH_SIZE in ${BATCH_ARRAY}; do + echo "./runtests-batch.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d 0 -b ${BATCH_SIZE} ${EXTRA_OPTS} | tee -a batch-rcu.log" >> runall.log + (./runtests-batch.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d 0 -b ${BATCH_SIZE} ${EXTRA_OPTS} | tee -a batch-rcu.log) || exit 1 +done + +#setting gc each 32768. ** UPDATE FOR YOUR ARCHITECTURE BASED ON TEST ABOVE ** +EXTRA_OPTS="${EXTRA_OPTS} -b 32768" + +echo Executing update fraction test + +DURATION=10 +WDELAY_ARRAY="0 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 + 65536 131072 262144 524288 1048576 2097152 4194304 8388608 + 16777216 33554432 67108864 134217728" +NR_WRITERS=$((${NUM_CPUS} / 2)) + +rm -f update-fraction.log + +NR_READERS=$((${NUM_CPUS} - ${NR_WRITERS})) +for WDELAY in ${WDELAY_ARRAY}; do + echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d ${WDELAY} ${EXTRA_OPTS} | tee -a update-fraction.log" >> runall.log + (./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d ${WDELAY} ${EXTRA_OPTS} | tee -a update-fraction.log) || exit 1 +done + +#Test scalability : +# x: vary number of readers from 0 to num cpus +# y: ops/s +# 0 writer. + +echo Executing scalability test + +NR_WRITERS=0 +DURATION=10 + +rm -f scalability.log + +for NR_READERS in $(seq 1 ${NUM_CPUS}); do + echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS}| tee -a scalability.log" >> runall.log + (./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS}| tee -a scalability.log) || exit 1 +done + + +# x: Vary reader C.S. length from 0 to 100 us +# y: ops/s +# 8 readers +# 0 writers + +echo Executing reader C.S. length test + +NR_READERS=${NUM_CPUS} +NR_WRITERS=0 +DURATION=10 +#in loops. +READERCSLEN_ARRAY="0 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 524288 1048576 2097152" + +rm -f readercslen.log + +for READERCSLEN in ${READERCSLEN_ARRAY}; do + echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS} -c ${READERCSLEN} | tee -a readercslen.log" >> runall.log + (./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS} -c ${READERCSLEN} | tee -a readercslen.log) || exit 1 +done + +echo Executing Hash table test +./runhash.sh || exit 1 diff --git a/tests/benchmark/runhash.sh b/tests/benchmark/runhash.sh new file mode 100755 index 0000000..0ba20ef --- /dev/null +++ b/tests/benchmark/runhash.sh @@ -0,0 +1,136 @@ +#!/bin/sh + +# TODO: missing tests: +# - send kill signals during tests to change the behavior between +# add/remove/random +# - validate that "nr_leaked" is always 0 in SUMMARY for all tests + +# 30 seconds per test +TIME_UNITS=30 + +TESTPROG=./test_urcu_hash + +#thread multiplier +THREAD_MUL=1 + +EXTRA_PARAMS=-v + +# ** test update coherency with single-value table + +# rw test, single key, replace and del randomly, 4 threads, auto resize. +# key range: init, lookup, and update: 0 to 0 +${TESTPROG} 0 $((4*${THREAD_MUL})) ${TIME_UNITS} -A -s -M 1 -N 1 -O 1 ${EXTRA_PARAMS} || exit 1 + +# rw test, single key, add unique and del randomly, 4 threads, auto resize. +# key range: init, lookup, and update: 0 to 0 +${TESTPROG} 0 $((4*${THREAD_MUL})) ${TIME_UNITS} -A -u -M 1 -N 1 -O 1 ${EXTRA_PARAMS} || exit 1 + +# rw test, single key, replace and del randomly, 2 lookup threads, 2 update threads, auto resize. +# key range: init, lookup, and update: 0 to 0 +${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A -s -M 1 -N 1 -O 1 ${EXTRA_PARAMS} || exit 1 + +# rw test, single key, add and del randomly, 2 lookup threads, 2 update threads, auto resize. +# key range: init, lookup, and update: 0 to 0 +${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A -M 1 -N 1 -O 1 ${EXTRA_PARAMS} || exit 1 + + +# ** test updates vs lookups with default table + +# rw test, 2 lookup, 2 update threads, add and del randomly, auto resize. +# max 1048576 buckets +# key range: init, lookup, and update: 0 to 999999 +${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A ${EXTRA_PARAMS} || exit 1 + +# rw test, 2 lookup, 2 update threads, add_replace and del randomly, auto resize. +# max 1048576 buckets +# key range: init, lookup, and update: 0 to 999999 +${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A -s ${EXTRA_PARAMS} || exit 1 + +# rw test, 2 lookup, 2 update threads, add_unique and del randomly, auto resize. +# max 1048576 buckets +# key range: init, lookup, and update: 0 to 999999 +${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A -u ${EXTRA_PARAMS} || exit 1 + + +# test memory management backends + +# rw test, 2 lookup, 2 update threads, add only, auto resize. +# max buckets: 1048576 +# key range: init, lookup, and update: 0 to 99999999 +# mm backend: "order" +${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A -m 1 -n 1048576 -i \ + -M 100000000 -N 100000000 -O 100000000 -B order ${EXTRA_PARAMS} || exit 1 + +# rw test, 2 lookup, 2 update threads, add only, auto resize. +# max buckets: 1048576 +# key range: init, lookup, and update: 0 to 99999999 +# mm backend: "chunk" +${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A -m 1 -n 1048576 -i \ + -M 100000000 -N 100000000 -O 100000000 -B chunk ${EXTRA_PARAMS} || exit 1 + +# rw test, 2 lookup, 2 update threads, add only, auto resize. +# max buckets: 1048576 +# key range: init, lookup, and update: 0 to 99999999 +# mm backend: "mmap" +${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A -m 1 -n 1048576 -i \ + -M 100000000 -N 100000000 -O 100000000 -B mmap ${EXTRA_PARAMS} || exit 1 + + +# ** key range tests + +# rw test, 2 lookup, 2 update threads, add and del randomly, auto resize. +# max 1048576 buckets +# key range: init, and update: 0 to 999999 +# key range: lookup: 1000000 to 1999999 +# NOTE: reader threads in this test should never have a successful +# lookup. TODO +${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A \ + -R 1000000 ${EXTRA_PARAMS} || exit 1 + +# ** small key range + +# rw test, 2 lookup, 2 update threads, add and del randomly, auto resize. +# max 1048576 buckets +# key range: init, update, and lookups: 0 to 9 +${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A \ + -M 10 -N 10 -O 10 ${EXTRA_PARAMS} || exit 1 + +# rw test, 2 lookup, 2 update threads, add_unique and del randomly, auto resize. +# max 1048576 buckets +# key range: init, update, and lookups: 0 to 9 +${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A \ + -M 10 -N 10 -O 10 -u ${EXTRA_PARAMS} || exit 1 + +# rw test, 2 lookup, 2 update threads, add_replace and del randomly, auto resize. +# max 1048576 buckets +# key range: init, update, and lookups: 0 to 9 +${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A \ + -M 10 -N 10 -O 10 -s ${EXTRA_PARAMS} || exit 1 + +# ** lookup for known keys + +# rw test, 2 lookup, 2 update threads, add_replace and del randomly, auto resize. +# max 1048576 buckets +# lookup range is entirely populated. +# key range: init, and lookups: 0 to 9 +# key range: updates: 10 to 19 +# NOTE: reader threads in this test should always have successful +# lookups. TODO +${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A \ + -M 10 -N 10 -O 10 -R 0 -T 0 -S 10 -k 10 -s ${EXTRA_PARAMS} || exit 1 + +# ** Uniqueness test + +# rw test, 2 lookup, 2 update threads, add_unique, add_replace and del randomly, auto resize. +# max 1048576 buckets +# asserts that no duplicates are observed by reader threads +# standard length hash chains +${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A \ + -U ${EXTRA_PARAMS} || exit 1 + +# rw test, 2 lookup, 2 update threads, add_unique, add_replace and del randomly, auto resize. +# max 1048576 buckets +# asserts that no duplicates are observed by reader threads +# create long hash chains: using modulo 4 on keys as hash +${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A \ + -U -C 4 ${EXTRA_PARAMS} || exit 1 diff --git a/tests/benchmark/runpaul-phase1.sh b/tests/benchmark/runpaul-phase1.sh new file mode 100755 index 0000000..d2c8649 --- /dev/null +++ b/tests/benchmark/runpaul-phase1.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +#run all tests + +#set to number of active CPUS +NUM_CPUS=64 + +#extra options, e.g. for setting affinity on even CPUs : +EXTRA_OPTS=$(for a in $(seq 0 2 127); do echo -n "-a ${a} "; done) + +#ppc64 striding, use with NUM_CPUS=8 + +#stride 1 +#EXTRA_OPTS=$(for a in $(seq 0 2 15); do echo -n "-a ${a} "; done) +#stride 2 +#EXTRA_OPTS=$(for a in $(seq 0 4 31); do echo -n "-a ${a} "; done) +#stride 4 +#EXTRA_OPTS=$(for a in $(seq 0 8 63); do echo -n "-a ${a} "; done) +#stride 8 +#EXTRA_OPTS=$(for a in $(seq 0 16 127); do echo -n "-a ${a} "; done) + +#Vary update fraction +#x: vary update fraction from 0 to 0.0001 + #fix number of readers and reader C.S. length, vary delay between updates +#y: ops/s + +rm -f runall.log +rm -fr runall.detail.log + + +echo Executing batch RCU test + +DURATION=10 +BATCH_ARRAY="1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 + 131072 262144" +NR_WRITERS=$((${NUM_CPUS} / 2)) + +rm -f batch-rcu.log + +NR_READERS=$((${NUM_CPUS} - ${NR_WRITERS})) +for BATCH_SIZE in ${BATCH_ARRAY}; do + echo "./runtests-batch.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d 0 -b ${BATCH_SIZE} ${EXTRA_OPTS} | tee -a batch-rcu.log" >> runall.log + ./runtests-batch.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d 0 -b ${BATCH_SIZE} ${EXTRA_OPTS} | tee -a batch-rcu.log +done diff --git a/tests/benchmark/runpaul-phase2.sh b/tests/benchmark/runpaul-phase2.sh new file mode 100755 index 0000000..bba9e3e --- /dev/null +++ b/tests/benchmark/runpaul-phase2.sh @@ -0,0 +1,47 @@ +#!/bin/sh + +#run all tests + +#set to number of active CPUS +NUM_CPUS=64 + +#extra options, e.g. for setting affinity on even CPUs : +EXTRA_OPTS=$(for a in $(seq 0 2 127); do echo -n "-a ${a} "; done) + +#ppc64 striding, use with NUM_CPUS=8 + +#stride 1 +#EXTRA_OPTS=$(for a in $(seq 0 2 15); do echo -n "-a ${a} "; done) +#stride 2 +#EXTRA_OPTS=$(for a in $(seq 0 4 31); do echo -n "-a ${a} "; done) +#stride 4 +#EXTRA_OPTS=$(for a in $(seq 0 8 63); do echo -n "-a ${a} "; done) +#stride 8 +#EXTRA_OPTS=$(for a in $(seq 0 16 127); do echo -n "-a ${a} "; done) + +#Vary update fraction +#x: vary update fraction from 0 to 0.0001 + #fix number of readers and reader C.S. length, vary delay between updates +#y: ops/s + +rm -f runall.log +rm -fr runall.detail.log + +#setting gc each 32768. ** UPDATE FOR YOUR ARCHITECTURE BASED ON PHASE 1 RESULT ** +EXTRA_OPTS="${EXTRA_OPTS} -b 32768" + +echo Executing update fraction test + +DURATION=10 +WDELAY_ARRAY="0 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 + 65536 131072 262144 524288 1048576 2097152 4194304 8388608 + 16777216 33554432 67108864 134217728" +NR_WRITERS=$((${NUM_CPUS} / 2)) + +rm -f update-fraction.log + +NR_READERS=$((${NUM_CPUS} - ${NR_WRITERS})) +for WDELAY in ${WDELAY_ARRAY}; do + echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d ${WDELAY} ${EXTRA_OPTS} | tee -a update-fraction.log" >> runall.log + ./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d ${WDELAY} ${EXTRA_OPTS} | tee -a update-fraction.log +done diff --git a/tests/benchmark/runpaul-phase3.sh b/tests/benchmark/runpaul-phase3.sh new file mode 100755 index 0000000..7c5f055 --- /dev/null +++ b/tests/benchmark/runpaul-phase3.sh @@ -0,0 +1,50 @@ +#!/bin/sh + +#run all tests + +#set to number of active CPUS +NUM_CPUS=64 + +#extra options, e.g. for setting affinity on even CPUs : +EXTRA_OPTS=$(for a in $(seq 0 2 127); do echo -n "-a ${a} "; done) + +#ppc64 striding, use with NUM_CPUS=8 + +#stride 1 +#EXTRA_OPTS=$(for a in $(seq 0 2 15); do echo -n "-a ${a} "; done) +#stride 2 +#EXTRA_OPTS=$(for a in $(seq 0 4 31); do echo -n "-a ${a} "; done) +#stride 4 +#EXTRA_OPTS=$(for a in $(seq 0 8 63); do echo -n "-a ${a} "; done) +#stride 8 +#EXTRA_OPTS=$(for a in $(seq 0 16 127); do echo -n "-a ${a} "; done) + +#Vary update fraction +#x: vary update fraction from 0 to 0.0001 + #fix number of readers and reader C.S. length, vary delay between updates +#y: ops/s + +rm -f runall.log +rm -fr runall.detail.log + +#setting gc each 32768. ** UPDATE FOR YOUR ARCHITECTURE BASED ON PHASE 1 RESULT ** +EXTRA_OPTS="${EXTRA_OPTS} -b 32768" + +#Test scalability : +# x: vary number of readers from 0 to num cpus +# y: ops/s +# 0 writer. + +echo Executing scalability test + +NR_WRITERS=0 +DURATION=10 + +rm -f scalability.log + +for NR_READERS in $(seq 1 ${NUM_CPUS}); do + echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS}| tee -a scalability.log" >> runall.log + ./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS}| tee -a scalability.log +done + + diff --git a/tests/benchmark/runpaul-phase4.sh b/tests/benchmark/runpaul-phase4.sh new file mode 100755 index 0000000..ede402c --- /dev/null +++ b/tests/benchmark/runpaul-phase4.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +#run all tests + +#set to number of active CPUS +export NUM_CPUS=8 + +#extra options, e.g. for setting affinity on even CPUs : +#EXTRA_OPTS=$(for a in $(seq 0 2 127); do echo -n "-a ${a} "; done) + +#ppc64 striding, use with NUM_CPUS=8 + +rm -f *.log + +#stride 1 +export EXTRA_OPTS=$(for a in $(seq 0 2 15); do echo -n "-a ${a} "; done) +sh subphase4.sh $* +mkdir ppc64-8cores-stride1 +mv *.log ppc64-8cores-stride1/ + + +#stride 2 +export EXTRA_OPTS=$(for a in $(seq 0 4 31); do echo -n "-a ${a} "; done) +sh subphase4.sh $* +mkdir ppc64-8cores-stride2 +mv *.log ppc64-8cores-stride2/ + + +#stride 4 +export EXTRA_OPTS=$(for a in $(seq 0 8 63); do echo -n "-a ${a} "; done) +sh subphase4.sh $* +mkdir ppc64-8cores-stride4 +mv *.log ppc64-8cores-stride4/ + + +#stride 8 +export EXTRA_OPTS=$(for a in $(seq 0 16 127); do echo -n "-a ${a} "; done) +sh subphase4.sh $* +mkdir ppc64-8cores-stride8 +mv *.log ppc64-8cores-stride8/ diff --git a/tests/benchmark/runpaul-phase5.sh b/tests/benchmark/runpaul-phase5.sh new file mode 100644 index 0000000..bb4bfe7 --- /dev/null +++ b/tests/benchmark/runpaul-phase5.sh @@ -0,0 +1,11 @@ +# test run after write-size update + +sh runpaul-phase1.sh +mkdir runpaul-phase1 +mv *.log runpaul-phase1/ + +sh runpaul-phase2.sh +mkdir runpaul-phase2 +mv *.log runpaul-phase2/ + +sh runpaul-phase4.sh diff --git a/tests/benchmark/runpaul-phase6.sh b/tests/benchmark/runpaul-phase6.sh new file mode 100644 index 0000000..5f65072 --- /dev/null +++ b/tests/benchmark/runpaul-phase6.sh @@ -0,0 +1,7 @@ +sh runpaul-phase1.sh +mkdir runpaul-phase1 +mv *.log runpaul-phase1/ + +sh runpaul-phase2.sh +mkdir runpaul-phase2 +mv *.log runpaul-phase2/ diff --git a/tests/benchmark/runpaul-phase7.sh b/tests/benchmark/runpaul-phase7.sh new file mode 100755 index 0000000..4c301da --- /dev/null +++ b/tests/benchmark/runpaul-phase7.sh @@ -0,0 +1,41 @@ +#!/bin/sh + +#run all tests + +#set to number of active CPUS +export NUM_CPUS=64 +#export NUM_CPUS=8 + +#extra options, e.g. for setting affinity on even CPUs : +EXTRA_OPTS=$(for a in $(seq 0 2 127); do echo -n "-a ${a} "; done) +#EXTRA_OPTS=$(for a in $(seq 0 1 7); do echo -n "-a ${a} "; done) + +rm -f *.log + +# x: Vary writer C.S. length from 0 to 100 us +# y: reads/s +# 4 readers +# 4 writers + +echo Executing writer C.S. length test + +NR_READERS=$((${NUM_CPUS} / 2)) +NR_WRITERS=$((${NUM_CPUS} / 2)) +DURATION=10 +WDELAY=10 +#in loops. +WRITERCSLEN_ARRAY="0 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 524288 1048576 2097152" + +rm -f writercslen.log + +for WRITERCSLEN in ${WRITERCSLEN_ARRAY}; do + echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS} -d ${WDELAY} -e ${WRITERCSLEN} | tee -a writercslen.log" >> runall.log + ./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS} -d ${WDELAY} -e ${WRITERCSLEN} | tee -a writercslen.log +done + + + +mkdir ppc64-writercslen +mv *.log ppc64-writercslen/ +#mkdir xeon-writercslen +#mv *.log xeon-writercslen/ diff --git a/tests/benchmark/runtests-batch.sh b/tests/benchmark/runtests-batch.sh new file mode 100755 index 0000000..2da1401 --- /dev/null +++ b/tests/benchmark/runtests-batch.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +. ./common.sh + +log_file="runall.detail.log" + +# Check if time bin is non-empty +if [ -n "$test_time_bin" ]; then + time_command="$test_time_bin -a -o $log_file" +else + time_command="" +fi + +#for a in test_urcu_gc test_urcu_gc_mb test_urcu_qsbr_gc; do +for a in test_urcu_gc; do + echo "./${a} $*" | tee -a "$log_file" + $time_command ./${a} $* +done + diff --git a/tests/benchmark/runtests.sh b/tests/benchmark/runtests.sh new file mode 100755 index 0000000..6001174 --- /dev/null +++ b/tests/benchmark/runtests.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +. ./common.sh + +log_file="runall.detail.log" + +# Check if time bin is non-empty +if [ -n "$test_time_bin" ]; then + time_command="$test_time_bin -a -o $log_file" +else + time_command="" +fi + +for a in test_urcu_gc test_urcu_signal_gc test_urcu_mb_gc test_urcu_qsbr_gc \ + test_urcu_lgc test_urcu_signal_lgc test_urcu_mb_lgc test_urcu_qsbr_lgc \ + test_urcu test_urcu_signal test_urcu_mb test_urcu_qsbr \ + test_rwlock test_perthreadlock test_mutex; do + echo "./${a} $*" | tee -a "$log_file" + $time_command ./${a} $* +done + diff --git a/tests/benchmark/subphase4.sh b/tests/benchmark/subphase4.sh new file mode 100755 index 0000000..d0d7587 --- /dev/null +++ b/tests/benchmark/subphase4.sh @@ -0,0 +1,101 @@ +#!/bin/sh + +#run all tests + +#set to number of active CPUS +#NUM_CPUS=8 + +#extra options, e.g. for setting affinity on even CPUs : +#EXTRA_OPTS=$(for a in $(seq 0 2 127); do echo -n "-a ${a} "; done) + +#ppc64 striding, use with NUM_CPUS=8 + +#stride 1 +#EXTRA_OPTS=$(for a in $(seq 0 2 15); do echo -n "-a ${a} "; done) +#stride 2 +#EXTRA_OPTS=$(for a in $(seq 0 4 31); do echo -n "-a ${a} "; done) +#stride 4 +#EXTRA_OPTS=$(for a in $(seq 0 8 63); do echo -n "-a ${a} "; done) +#stride 8 +#EXTRA_OPTS=$(for a in $(seq 0 16 127); do echo -n "-a ${a} "; done) + +#Vary update fraction +#x: vary update fraction from 0 to 0.0001 + #fix number of readers and reader C.S. length, vary delay between updates +#y: ops/s + +rm -f runall.log +rm -fr runall.detail.log + + +echo Executing batch RCU test + +DURATION=10 +BATCH_ARRAY="1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 + 131072 262144" +NR_WRITERS=$((${NUM_CPUS} / 2)) + +rm -f batch-rcu.log + +NR_READERS=$((${NUM_CPUS} - ${NR_WRITERS})) +for BATCH_SIZE in ${BATCH_ARRAY}; do + echo "./runtests-batch.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d 0 -b ${BATCH_SIZE} ${EXTRA_OPTS} | tee -a batch-rcu.log" >> runall.log + ./runtests-batch.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d 0 -b ${BATCH_SIZE} ${EXTRA_OPTS} | tee -a batch-rcu.log +done + +#setting gc each 4096. ** UPDATE FOR YOUR ARCHITECTURE BASED ON TEST ABOVE ** +EXTRA_OPTS="${EXTRA_OPTS} -b 32768" + +echo Executing update fraction test + +DURATION=10 +WDELAY_ARRAY="0 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 + 65536 131072 262144 524288 1048576 2097152 4194304 8388608 + 16777216 33554432 67108864 134217728" +NR_WRITERS=$((${NUM_CPUS} / 2)) + +rm -f update-fraction.log + +NR_READERS=$((${NUM_CPUS} - ${NR_WRITERS})) +for WDELAY in ${WDELAY_ARRAY}; do + echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d ${WDELAY} ${EXTRA_OPTS} | tee -a update-fraction.log" >> runall.log + ./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d ${WDELAY} ${EXTRA_OPTS} | tee -a update-fraction.log +done + +#Test scalability : +# x: vary number of readers from 0 to num cpus +# y: ops/s +# 0 writer. + +echo Executing scalability test + +NR_WRITERS=0 +DURATION=10 + +rm -f scalability.log + +for NR_READERS in $(seq 1 ${NUM_CPUS}); do + echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS}| tee -a scalability.log" >> runall.log + ./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS}| tee -a scalability.log +done + + +# x: Vary reader C.S. length from 0 to 100 us +# y: ops/s +# 8 readers +# 0 writers + +echo Executing reader C.S. length test + +NR_READERS=${NUM_CPUS} +NR_WRITERS=0 +DURATION=10 +#in loops. +READERCSLEN_ARRAY="0 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 524288 1048576 2097152" + +rm -f readercslen.log + +for READERCSLEN in ${READERCSLEN_ARRAY}; do + echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS} -c ${READERCSLEN} | tee -a readercslen.log" >> runall.log + ./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS} -c ${READERCSLEN} | tee -a readercslen.log +done diff --git a/tests/benchmark/test_cycles_per_loop.c b/tests/benchmark/test_cycles_per_loop.c new file mode 100644 index 0000000..6ff100b --- /dev/null +++ b/tests/benchmark/test_cycles_per_loop.c @@ -0,0 +1,44 @@ +/* + * test_cycles_per_loop.c + * + * Userspace RCU library - test cycles per loop + * + * Copyright February 2009 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include + +#define NR_LOOPS 1000000UL + +static inline void loop_sleep(unsigned long loops) +{ + while (loops-- != 0) + caa_cpu_relax(); +} + +int main() +{ + cycles_t time1, time2; + + time1 = caa_get_cycles(); + loop_sleep(NR_LOOPS); + time2 = caa_get_cycles(); + printf("CPU clock cycles per loop: %g\n", (time2 - time1) / + (double)NR_LOOPS); + return 0; +} diff --git a/tests/benchmark/test_looplen.c b/tests/benchmark/test_looplen.c new file mode 100644 index 0000000..16674e7 --- /dev/null +++ b/tests/benchmark/test_looplen.c @@ -0,0 +1,74 @@ +/* + * test_looplen.c + * + * Userspace RCU library - test program + * + * Copyright February 2009 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifndef DYNAMIC_LINK_TEST +#define _LGPL_SOURCE +#else +#define debug_yield_read() +#endif +#include + +static inline void loop_sleep(unsigned long loops) +{ + while (loops-- != 0) + caa_cpu_relax(); +} + +#define LOOPS 1048576 +#define TESTS 10 + +int main(int argc, char **argv) +{ + unsigned long i; + cycles_t time1, time2; + cycles_t time_tot = 0; + double cpl; + + for (i = 0; i < TESTS; i++) { + time1 = caa_get_cycles(); + loop_sleep(LOOPS); + time2 = caa_get_cycles(); + time_tot += time2 - time1; + } + cpl = ((double)time_tot) / (double)TESTS / (double)LOOPS; + + printf("CALIBRATION : %g cycles per loop\n", cpl); + printf("time_tot = %llu, LOOPS = %d, TESTS = %d\n", + time_tot, LOOPS, TESTS); + + return 0; +} diff --git a/tests/benchmark/test_mutex.c b/tests/benchmark/test_mutex.c new file mode 100644 index 0000000..255acfc --- /dev/null +++ b/tests/benchmark/test_mutex.c @@ -0,0 +1,399 @@ +/* + * test_urcu.c + * + * Userspace RCU library - test program + * + * Copyright February 2009 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "cpuset.h" +#include "thread-id.h" + +/* hardcoded number of CPUs */ +#define NR_CPUS 16384 + +#ifndef DYNAMIC_LINK_TEST +#define _LGPL_SOURCE +#else +#define debug_yield_read() +#endif +#include + +struct test_array { + int a; +}; + +static pthread_mutex_t lock; + +static volatile int test_go, test_stop; + +static unsigned long wdelay; + +static volatile struct test_array test_array = { 8 }; + +static unsigned long duration; + +/* read-side C.S. duration, in loops */ +static unsigned long rduration; + +/* write-side C.S. duration, in loops */ +static unsigned long wduration; + +static inline void loop_sleep(unsigned long loops) +{ + while (loops-- != 0) + caa_cpu_relax(); +} + +static int verbose_mode; + +#define printf_verbose(fmt, args...) \ + do { \ + if (verbose_mode) \ + printf(fmt, args); \ + } while (0) + +static unsigned int cpu_affinities[NR_CPUS]; +static unsigned int next_aff = 0; +static int use_affinity = 0; + +pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void set_affinity(void) +{ +#if HAVE_SCHED_SETAFFINITY + cpu_set_t mask; + int cpu, ret; +#endif /* HAVE_SCHED_SETAFFINITY */ + + if (!use_affinity) + return; + +#if HAVE_SCHED_SETAFFINITY + ret = pthread_mutex_lock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } + cpu = cpu_affinities[next_aff++]; + ret = pthread_mutex_unlock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } + CPU_ZERO(&mask); + CPU_SET(cpu, &mask); +#if SCHED_SETAFFINITY_ARGS == 2 + sched_setaffinity(0, &mask); +#else + sched_setaffinity(0, sizeof(mask), &mask); +#endif +#endif /* HAVE_SCHED_SETAFFINITY */ +} + +/* + * returns 0 if test should end. + */ +static int test_duration_write(void) +{ + return !test_stop; +} + +static int test_duration_read(void) +{ + return !test_stop; +} + +static DEFINE_URCU_TLS(unsigned long long, nr_writes); +static DEFINE_URCU_TLS(unsigned long long, nr_reads); + +static +unsigned long long __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *tot_nr_writes; +static +unsigned long long __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *tot_nr_reads; + +static unsigned int nr_readers; +static unsigned int nr_writers; + +pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; + +void rcu_copy_mutex_lock(void) +{ + int ret; + ret = pthread_mutex_lock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } +} + +void rcu_copy_mutex_unlock(void) +{ + int ret; + + ret = pthread_mutex_unlock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } +} + +void *thr_reader(void *data) +{ + unsigned long tidx = (unsigned long)data; + + printf_verbose("thread_begin %s, tid %lu\n", + "reader", urcu_get_thread_id()); + + set_affinity(); + + while (!test_go) + { + } + + for (;;) { + pthread_mutex_lock(&lock); + assert(test_array.a == 8); + if (caa_unlikely(rduration)) + loop_sleep(rduration); + pthread_mutex_unlock(&lock); + URCU_TLS(nr_reads)++; + if (caa_unlikely(!test_duration_read())) + break; + } + + tot_nr_reads[tidx] = URCU_TLS(nr_reads); + printf_verbose("thread_end %s, tid %lu\n", + "reader", urcu_get_thread_id()); + return ((void*)1); + +} + +void *thr_writer(void *data) +{ + unsigned long wtidx = (unsigned long)data; + + printf_verbose("thread_begin %s, tid %lu\n", + "writer", urcu_get_thread_id()); + + set_affinity(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + pthread_mutex_lock(&lock); + test_array.a = 0; + test_array.a = 8; + if (caa_unlikely(wduration)) + loop_sleep(wduration); + pthread_mutex_unlock(&lock); + URCU_TLS(nr_writes)++; + if (caa_unlikely(!test_duration_write())) + break; + if (caa_unlikely(wdelay)) + loop_sleep(wdelay); + } + + printf_verbose("thread_end %s, tid %lu\n", + "writer", urcu_get_thread_id()); + tot_nr_writes[wtidx] = URCU_TLS(nr_writes); + return ((void*)2); +} + +void show_usage(int argc, char **argv) +{ + printf("Usage : %s nr_readers nr_writers duration (s) \n", + argv[0]); + printf("OPTIONS:\n"); +#ifdef DEBUG_YIELD + printf(" [-r] [-w] (yield reader and/or writer)\n"); +#endif + printf(" [-d delay] (writer period (us))\n"); + printf(" [-c duration] (reader C.S. duration (in loops))\n"); + printf(" [-e duration] (writer C.S. duration (in loops))\n"); + printf(" [-v] (verbose output)\n"); + printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); + printf("\n"); +} + +int main(int argc, char **argv) +{ + int err; + pthread_t *tid_reader, *tid_writer; + void *tret; + unsigned long long *count_reader, *count_writer; + unsigned long long tot_reads = 0, tot_writes = 0; + int i, a; + + if (argc < 4) { + show_usage(argc, argv); + return -1; + } + cmm_smp_mb(); + + err = sscanf(argv[1], "%u", &nr_readers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[2], "%u", &nr_writers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[3], "%lu", &duration); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + for (i = 4; i < argc; i++) { + if (argv[i][0] != '-') + continue; + switch (argv[i][1]) { +#ifdef DEBUG_YIELD + case 'r': + yield_active |= YIELD_READ; + break; + case 'w': + yield_active |= YIELD_WRITE; + break; +#endif + case 'a': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + a = atoi(argv[++i]); + cpu_affinities[next_aff++] = a; + use_affinity = 1; + printf_verbose("Adding CPU %d affinity\n", a); + break; + case 'c': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + rduration = atol(argv[++i]); + break; + case 'd': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wdelay = atol(argv[++i]); + break; + case 'e': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wduration = atol(argv[++i]); + break; + case 'v': + verbose_mode = 1; + break; + } + } + + printf_verbose("running test for %lu seconds, %u readers, %u writers.\n", + duration, nr_readers, nr_writers); + printf_verbose("Writer delay : %lu loops.\n", wdelay); + printf_verbose("Reader duration : %lu loops.\n", rduration); + printf_verbose("thread %-6s, tid %lu\n", + "main", urcu_get_thread_id()); + + tid_reader = calloc(nr_readers, sizeof(*tid_reader)); + tid_writer = calloc(nr_writers, sizeof(*tid_writer)); + count_reader = calloc(nr_readers, sizeof(*count_reader)); + count_writer = calloc(nr_writers, sizeof(*count_writer)); + tot_nr_reads = calloc(nr_readers, sizeof(*tot_nr_reads)); + tot_nr_writes = calloc(nr_writers, sizeof(*tot_nr_writes)); + + next_aff = 0; + + for (i = 0; i < nr_readers; i++) { + err = pthread_create(&tid_reader[i], NULL, thr_reader, + (void *)(long)i); + if (err != 0) + exit(1); + } + for (i = 0; i < nr_writers; i++) { + err = pthread_create(&tid_writer[i], NULL, thr_writer, + (void *)(long)i); + if (err != 0) + exit(1); + } + + cmm_smp_mb(); + + test_go = 1; + + sleep(duration); + + test_stop = 1; + + for (i = 0; i < nr_readers; i++) { + err = pthread_join(tid_reader[i], &tret); + if (err != 0) + exit(1); + tot_reads += tot_nr_reads[i]; + } + for (i = 0; i < nr_writers; i++) { + err = pthread_join(tid_writer[i], &tret); + if (err != 0) + exit(1); + tot_writes += tot_nr_writes[i]; + } + + printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads, + tot_writes); + printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu " + "nr_writers %3u " + "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n", + argv[0], duration, nr_readers, rduration, wduration, + nr_writers, wdelay, tot_reads, tot_writes, + tot_reads + tot_writes); + + free(tid_reader); + free(tid_writer); + free(count_reader); + free(count_writer); + free(tot_nr_reads); + free(tot_nr_writes); + return 0; +} diff --git a/tests/benchmark/test_perthreadlock.c b/tests/benchmark/test_perthreadlock.c new file mode 100644 index 0000000..01c4fb0 --- /dev/null +++ b/tests/benchmark/test_perthreadlock.c @@ -0,0 +1,410 @@ +/* + * test_urcu.c + * + * Userspace RCU library - test program + * + * Copyright February 2009 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "cpuset.h" +#include "thread-id.h" + +/* hardcoded number of CPUs */ +#define NR_CPUS 16384 + +#ifndef DYNAMIC_LINK_TEST +#define _LGPL_SOURCE +#else +#define debug_yield_read() +#endif +#include + +struct test_array { + int a; +}; + +struct per_thread_lock { + pthread_mutex_t lock; +} __attribute__((aligned(CAA_CACHE_LINE_SIZE))); /* cache-line aligned */ + +static struct per_thread_lock *per_thread_lock; + +static volatile int test_go, test_stop; + +static unsigned long wdelay; + +static volatile struct test_array test_array = { 8 }; + +static unsigned long duration; + +/* read-side C.S. duration, in loops */ +static unsigned long rduration; + +/* write-side C.S. duration, in loops */ +static unsigned long wduration; + +static inline void loop_sleep(unsigned long loops) +{ + while (loops-- != 0) + caa_cpu_relax(); +} + +static int verbose_mode; + +#define printf_verbose(fmt, args...) \ + do { \ + if (verbose_mode) \ + printf(fmt, args); \ + } while (0) + +static unsigned int cpu_affinities[NR_CPUS]; +static unsigned int next_aff = 0; +static int use_affinity = 0; + +pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void set_affinity(void) +{ +#if HAVE_SCHED_SETAFFINITY + cpu_set_t mask; + int cpu, ret; +#endif /* HAVE_SCHED_SETAFFINITY */ + + if (!use_affinity) + return; + +#if HAVE_SCHED_SETAFFINITY + ret = pthread_mutex_lock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } + cpu = cpu_affinities[next_aff++]; + ret = pthread_mutex_unlock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } + CPU_ZERO(&mask); + CPU_SET(cpu, &mask); +#if SCHED_SETAFFINITY_ARGS == 2 + sched_setaffinity(0, &mask); +#else + sched_setaffinity(0, sizeof(mask), &mask); +#endif +#endif /* HAVE_SCHED_SETAFFINITY */ +} + +/* + * returns 0 if test should end. + */ +static int test_duration_write(void) +{ + return !test_stop; +} + +static int test_duration_read(void) +{ + return !test_stop; +} + +static DEFINE_URCU_TLS(unsigned long long, nr_writes); +static DEFINE_URCU_TLS(unsigned long long, nr_reads); + +static +unsigned long long __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *tot_nr_writes; +static +unsigned long long __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *tot_nr_reads; + +static unsigned int nr_readers; +static unsigned int nr_writers; + +pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; + +void rcu_copy_mutex_lock(void) +{ + int ret; + ret = pthread_mutex_lock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } +} + +void rcu_copy_mutex_unlock(void) +{ + int ret; + + ret = pthread_mutex_unlock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } +} + +void *thr_reader(void *data) +{ + unsigned long tidx = (unsigned long)data; + + printf_verbose("thread_begin %s, tid %lu\n", + "reader", urcu_get_thread_id()); + + set_affinity(); + + while (!test_go) + { + } + + for (;;) { + pthread_mutex_lock(&per_thread_lock[tidx].lock); + assert(test_array.a == 8); + if (caa_unlikely(rduration)) + loop_sleep(rduration); + pthread_mutex_unlock(&per_thread_lock[tidx].lock); + URCU_TLS(nr_reads)++; + if (caa_unlikely(!test_duration_read())) + break; + } + + tot_nr_reads[tidx] = URCU_TLS(nr_reads); + printf_verbose("thread_end %s, tid %lu\n", + "reader", urcu_get_thread_id()); + return ((void*)1); + +} + +void *thr_writer(void *data) +{ + unsigned long wtidx = (unsigned long)data; + long tidx; + + printf_verbose("thread_begin %s, tid %lu\n", + "writer", urcu_get_thread_id()); + + set_affinity(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + for (tidx = 0; tidx < nr_readers; tidx++) { + pthread_mutex_lock(&per_thread_lock[tidx].lock); + } + test_array.a = 0; + test_array.a = 8; + if (caa_unlikely(wduration)) + loop_sleep(wduration); + for (tidx = (long)nr_readers - 1; tidx >= 0; tidx--) { + pthread_mutex_unlock(&per_thread_lock[tidx].lock); + } + URCU_TLS(nr_writes)++; + if (caa_unlikely(!test_duration_write())) + break; + if (caa_unlikely(wdelay)) + loop_sleep(wdelay); + } + + printf_verbose("thread_end %s, tid %lu\n", + "writer", urcu_get_thread_id()); + tot_nr_writes[wtidx] = URCU_TLS(nr_writes); + return ((void*)2); +} + +void show_usage(int argc, char **argv) +{ + printf("Usage : %s nr_readers nr_writers duration (s) \n", + argv[0]); + printf("OPTIONS:\n"); +#ifdef DEBUG_YIELD + printf(" [-r] [-w] (yield reader and/or writer)\n"); +#endif + printf(" [-d delay] (writer period (us))\n"); + printf(" [-c duration] (reader C.S. duration (in loops))\n"); + printf(" [-e duration] (writer C.S. duration (in loops))\n"); + printf(" [-v] (verbose output)\n"); + printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); + printf("\n"); +} + +int main(int argc, char **argv) +{ + int err; + pthread_t *tid_reader, *tid_writer; + void *tret; + unsigned long long tot_reads = 0, tot_writes = 0; + int i, a; + + if (argc < 4) { + show_usage(argc, argv); + return -1; + } + cmm_smp_mb(); + + err = sscanf(argv[1], "%u", &nr_readers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[2], "%u", &nr_writers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[3], "%lu", &duration); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + for (i = 4; i < argc; i++) { + if (argv[i][0] != '-') + continue; + switch (argv[i][1]) { +#ifdef DEBUG_YIELD + case 'r': + yield_active |= YIELD_READ; + break; + case 'w': + yield_active |= YIELD_WRITE; + break; +#endif + case 'a': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + a = atoi(argv[++i]); + cpu_affinities[next_aff++] = a; + use_affinity = 1; + printf_verbose("Adding CPU %d affinity\n", a); + break; + case 'c': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + rduration = atol(argv[++i]); + break; + case 'd': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wdelay = atol(argv[++i]); + break; + case 'e': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wduration = atol(argv[++i]); + break; + case 'v': + verbose_mode = 1; + break; + } + } + + printf_verbose("running test for %lu seconds, %u readers, %u writers.\n", + duration, nr_readers, nr_writers); + printf_verbose("Writer delay : %lu loops.\n", wdelay); + printf_verbose("Reader duration : %lu loops.\n", rduration); + printf_verbose("thread %-6s, tid %lu\n", + "main", urcu_get_thread_id()); + + tid_reader = calloc(nr_readers, sizeof(*tid_reader)); + tid_writer = calloc(nr_writers, sizeof(*tid_writer)); + tot_nr_reads = calloc(nr_readers, sizeof(*tot_nr_reads)); + tot_nr_writes = calloc(nr_writers, sizeof(*tot_nr_writes)); + per_thread_lock = calloc(nr_readers, sizeof(*per_thread_lock)); + for (i = 0; i < nr_readers; i++) { + err = pthread_mutex_init(&per_thread_lock[i].lock, NULL); + if (err != 0) + exit(1); + } + + next_aff = 0; + + for (i = 0; i < nr_readers; i++) { + err = pthread_create(&tid_reader[i], NULL, thr_reader, + (void *)(long)i); + if (err != 0) + exit(1); + } + for (i = 0; i < nr_writers; i++) { + err = pthread_create(&tid_writer[i], NULL, thr_writer, + (void *)(long)i); + if (err != 0) + exit(1); + } + + cmm_smp_mb(); + + test_go = 1; + + sleep(duration); + + test_stop = 1; + + for (i = 0; i < nr_readers; i++) { + err = pthread_join(tid_reader[i], &tret); + if (err != 0) + exit(1); + tot_reads += tot_nr_reads[i]; + } + for (i = 0; i < nr_writers; i++) { + err = pthread_join(tid_writer[i], &tret); + if (err != 0) + exit(1); + tot_writes += tot_nr_writes[i]; + } + + printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads, + tot_writes); + printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu " + "nr_writers %3u " + "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n", + argv[0], duration, nr_readers, rduration, wduration, + nr_writers, wdelay, tot_reads, tot_writes, + tot_reads + tot_writes); + + free(tid_reader); + free(tid_writer); + free(tot_nr_reads); + free(tot_nr_writes); + free(per_thread_lock); + return 0; +} diff --git a/tests/benchmark/test_perthreadlock_timing.c b/tests/benchmark/test_perthreadlock_timing.c new file mode 100644 index 0000000..97bdd04 --- /dev/null +++ b/tests/benchmark/test_perthreadlock_timing.c @@ -0,0 +1,198 @@ +/* + * test_perthreadloc_timing.c + * + * Per thread locks - test program + * + * Copyright February 2009 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "thread-id.h" + +#include + +struct test_array { + int a; +}; + +static struct test_array test_array = { 8 }; + +struct per_thread_lock { + pthread_mutex_t lock; +} __attribute__((aligned(CAA_CACHE_LINE_SIZE))); /* cache-line aligned */ + +static struct per_thread_lock *per_thread_lock; + +#define OUTER_READ_LOOP 200U +#define INNER_READ_LOOP 100000U +#define READ_LOOP ((unsigned long long)OUTER_READ_LOOP * INNER_READ_LOOP) + +#define OUTER_WRITE_LOOP 10U +#define INNER_WRITE_LOOP 200U +#define WRITE_LOOP ((unsigned long long)OUTER_WRITE_LOOP * INNER_WRITE_LOOP) + +static int num_read; +static int num_write; + +#define NR_READ num_read +#define NR_WRITE num_write + +static cycles_t __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *reader_time; +static cycles_t __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *writer_time; + +void *thr_reader(void *arg) +{ + int i, j; + cycles_t time1, time2; + long tidx = (long)arg; + + printf("thread_begin %s, tid %lu\n", + "reader", urcu_get_thread_id()); + sleep(2); + + time1 = caa_get_cycles(); + for (i = 0; i < OUTER_READ_LOOP; i++) { + for (j = 0; j < INNER_READ_LOOP; j++) { + pthread_mutex_lock(&per_thread_lock[tidx].lock); + assert(test_array.a == 8); + pthread_mutex_unlock(&per_thread_lock[tidx].lock); + } + } + time2 = caa_get_cycles(); + + reader_time[tidx] = time2 - time1; + + sleep(2); + printf("thread_end %s, tid %lu\n", + "reader", urcu_get_thread_id()); + return ((void*)1); + +} + +void *thr_writer(void *arg) +{ + int i, j; + long tidx; + cycles_t time1, time2; + + printf("thread_begin %s, tid %lu\n", + "writer", urcu_get_thread_id()); + sleep(2); + + for (i = 0; i < OUTER_WRITE_LOOP; i++) { + for (j = 0; j < INNER_WRITE_LOOP; j++) { + time1 = caa_get_cycles(); + for (tidx = 0; tidx < NR_READ; tidx++) { + pthread_mutex_lock(&per_thread_lock[tidx].lock); + } + test_array.a = 8; + for (tidx = NR_READ - 1; tidx >= 0; tidx--) { + pthread_mutex_unlock(&per_thread_lock[tidx].lock); + } + time2 = caa_get_cycles(); + writer_time[(unsigned long)arg] += time2 - time1; + usleep(1); + } + } + + printf("thread_end %s, tid %lu\n", + "writer", urcu_get_thread_id()); + return ((void*)2); +} + +int main(int argc, char **argv) +{ + int err; + pthread_t *tid_reader, *tid_writer; + void *tret; + int i; + cycles_t tot_rtime = 0; + cycles_t tot_wtime = 0; + + if (argc < 2) { + printf("Usage : %s nr_readers nr_writers\n", argv[0]); + exit(-1); + } + num_read = atoi(argv[1]); + num_write = atoi(argv[2]); + + reader_time = calloc(num_read, sizeof(*reader_time)); + writer_time = calloc(num_write, sizeof(*writer_time)); + tid_reader = calloc(num_read, sizeof(*tid_reader)); + tid_writer = calloc(num_write, sizeof(*tid_writer)); + + printf("thread %-6s, tid %lu\n", + "main", urcu_get_thread_id()); + + per_thread_lock = malloc(sizeof(struct per_thread_lock) * NR_READ); + + for (i = 0; i < NR_READ; i++) { + pthread_mutex_init(&per_thread_lock[i].lock, NULL); + } + for (i = 0; i < NR_READ; i++) { + err = pthread_create(&tid_reader[i], NULL, thr_reader, + (void *)(long)i); + if (err != 0) + exit(1); + } + for (i = 0; i < NR_WRITE; i++) { + err = pthread_create(&tid_writer[i], NULL, thr_writer, + (void *)(long)i); + if (err != 0) + exit(1); + } + + sleep(10); + + for (i = 0; i < NR_READ; i++) { + err = pthread_join(tid_reader[i], &tret); + if (err != 0) + exit(1); + tot_rtime += reader_time[i]; + } + for (i = 0; i < NR_WRITE; i++) { + err = pthread_join(tid_writer[i], &tret); + if (err != 0) + exit(1); + tot_wtime += writer_time[i]; + } + printf("Time per read : %g cycles\n", + (double)tot_rtime / ((double)NR_READ * (double)READ_LOOP)); + printf("Time per write : %g cycles\n", + (double)tot_wtime / ((double)NR_WRITE * (double)WRITE_LOOP)); + free(per_thread_lock); + + free(reader_time); + free(writer_time); + free(tid_reader); + free(tid_writer); + + return 0; +} diff --git a/tests/benchmark/test_rwlock.c b/tests/benchmark/test_rwlock.c new file mode 100644 index 0000000..32e3c39 --- /dev/null +++ b/tests/benchmark/test_rwlock.c @@ -0,0 +1,391 @@ +/* + * test_urcu.c + * + * Userspace RCU library - test program + * + * Copyright February 2009 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "cpuset.h" +#include "thread-id.h" + +/* hardcoded number of CPUs */ +#define NR_CPUS 16384 + +#ifndef DYNAMIC_LINK_TEST +#define _LGPL_SOURCE +#else +#define debug_yield_read() +#endif +#include + +struct test_array { + int a; +}; + +pthread_rwlock_t lock = PTHREAD_RWLOCK_INITIALIZER; + +static volatile int test_go, test_stop; + +static unsigned long wdelay; + +static volatile struct test_array test_array = { 8 }; + +static unsigned long duration; + +/* read-side C.S. duration, in loops */ +static unsigned long rduration; + +/* write-side C.S. duration, in loops */ +static unsigned long wduration; + +static inline void loop_sleep(unsigned long loops) +{ + while (loops-- != 0) + caa_cpu_relax(); +} + +static int verbose_mode; + +#define printf_verbose(fmt, args...) \ + do { \ + if (verbose_mode) \ + printf(fmt, args); \ + } while (0) + +static unsigned int cpu_affinities[NR_CPUS]; +static unsigned int next_aff = 0; +static int use_affinity = 0; + +pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void set_affinity(void) +{ +#if HAVE_SCHED_SETAFFINITY + cpu_set_t mask; + int cpu, ret; +#endif /* HAVE_SCHED_SETAFFINITY */ + + if (!use_affinity) + return; + +#if HAVE_SCHED_SETAFFINITY + ret = pthread_mutex_lock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } + cpu = cpu_affinities[next_aff++]; + ret = pthread_mutex_unlock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } + + CPU_ZERO(&mask); + CPU_SET(cpu, &mask); +#if SCHED_SETAFFINITY_ARGS == 2 + sched_setaffinity(0, &mask); +#else + sched_setaffinity(0, sizeof(mask), &mask); +#endif +#endif /* HAVE_SCHED_SETAFFINITY */ +} + +/* + * returns 0 if test should end. + */ +static int test_duration_write(void) +{ + return !test_stop; +} + +static int test_duration_read(void) +{ + return !test_stop; +} + +static DEFINE_URCU_TLS(unsigned long long, nr_writes); +static DEFINE_URCU_TLS(unsigned long long, nr_reads); + +static unsigned int nr_readers; +static unsigned int nr_writers; + +pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; + +void rcu_copy_mutex_lock(void) +{ + int ret; + ret = pthread_mutex_lock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } +} + +void rcu_copy_mutex_unlock(void) +{ + int ret; + + ret = pthread_mutex_unlock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } +} + +void *thr_reader(void *_count) +{ + unsigned long long *count = _count; + + printf_verbose("thread_begin %s, tid %lu\n", + "reader", urcu_get_thread_id()); + + set_affinity(); + + while (!test_go) + { + } + + for (;;) { + pthread_rwlock_rdlock(&lock); + assert(test_array.a == 8); + if (caa_unlikely(rduration)) + loop_sleep(rduration); + pthread_rwlock_unlock(&lock); + URCU_TLS(nr_reads)++; + if (caa_unlikely(!test_duration_read())) + break; + } + + *count = URCU_TLS(nr_reads); + printf_verbose("thread_end %s, tid %lu\n", + "reader", urcu_get_thread_id()); + return ((void*)1); + +} + +void *thr_writer(void *_count) +{ + unsigned long long *count = _count; + + printf_verbose("thread_begin %s, tid %lu\n", + "writer", urcu_get_thread_id()); + + set_affinity(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + pthread_rwlock_wrlock(&lock); + test_array.a = 0; + test_array.a = 8; + if (caa_unlikely(wduration)) + loop_sleep(wduration); + pthread_rwlock_unlock(&lock); + URCU_TLS(nr_writes)++; + if (caa_unlikely(!test_duration_write())) + break; + if (caa_unlikely(wdelay)) + loop_sleep(wdelay); + } + + printf_verbose("thread_end %s, tid %lu\n", + "writer", urcu_get_thread_id()); + *count = URCU_TLS(nr_writes); + return ((void*)2); +} + +void show_usage(int argc, char **argv) +{ + printf("Usage : %s nr_readers nr_writers duration (s) \n", + argv[0]); + printf("OPTIONS:\n"); +#ifdef DEBUG_YIELD + printf(" [-r] [-w] (yield reader and/or writer)\n"); +#endif + printf(" [-d delay] (writer period (us))\n"); + printf(" [-c duration] (reader C.S. duration (in loops))\n"); + printf(" [-e duration] (writer C.S. duration (in loops))\n"); + printf(" [-v] (verbose output)\n"); + printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); + printf("\n"); +} + +int main(int argc, char **argv) +{ + int err; + pthread_t *tid_reader, *tid_writer; + void *tret; + unsigned long long *count_reader, *count_writer; + unsigned long long tot_reads = 0, tot_writes = 0; + int i, a; + + if (argc < 4) { + show_usage(argc, argv); + return -1; + } + cmm_smp_mb(); + + err = sscanf(argv[1], "%u", &nr_readers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[2], "%u", &nr_writers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[3], "%lu", &duration); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + for (i = 4; i < argc; i++) { + if (argv[i][0] != '-') + continue; + switch (argv[i][1]) { +#ifdef DEBUG_YIELD + case 'r': + yield_active |= YIELD_READ; + break; + case 'w': + yield_active |= YIELD_WRITE; + break; +#endif + case 'a': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + a = atoi(argv[++i]); + cpu_affinities[next_aff++] = a; + use_affinity = 1; + printf_verbose("Adding CPU %d affinity\n", a); + break; + case 'c': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + rduration = atol(argv[++i]); + break; + case 'd': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wdelay = atol(argv[++i]); + break; + case 'e': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wduration = atol(argv[++i]); + break; + case 'v': + verbose_mode = 1; + break; + } + } + + printf_verbose("running test for %lu seconds, %u readers, %u writers.\n", + duration, nr_readers, nr_writers); + printf_verbose("Writer delay : %lu loops.\n", wdelay); + printf_verbose("Reader duration : %lu loops.\n", rduration); + printf_verbose("thread %-6s, tid %lu\n", + "main", urcu_get_thread_id()); + + tid_reader = calloc(nr_readers, sizeof(*tid_reader)); + tid_writer = calloc(nr_writers, sizeof(*tid_writer)); + count_reader = calloc(nr_readers, sizeof(*count_reader)); + count_writer = calloc(nr_writers, sizeof(*count_writer)); + + next_aff = 0; + + for (i = 0; i < nr_readers; i++) { + err = pthread_create(&tid_reader[i], NULL, thr_reader, + &count_reader[i]); + if (err != 0) + exit(1); + } + for (i = 0; i < nr_writers; i++) { + err = pthread_create(&tid_writer[i], NULL, thr_writer, + &count_writer[i]); + if (err != 0) + exit(1); + } + + cmm_smp_mb(); + + test_go = 1; + + sleep(duration); + + test_stop = 1; + + for (i = 0; i < nr_readers; i++) { + err = pthread_join(tid_reader[i], &tret); + if (err != 0) + exit(1); + tot_reads += count_reader[i]; + } + for (i = 0; i < nr_writers; i++) { + err = pthread_join(tid_writer[i], &tret); + if (err != 0) + exit(1); + tot_writes += count_writer[i]; + } + + printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads, + tot_writes); + printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu " + "nr_writers %3u " + "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n", + argv[0], duration, nr_readers, rduration, wduration, + nr_writers, wdelay, tot_reads, tot_writes, + tot_reads + tot_writes); + + free(tid_reader); + free(tid_writer); + free(count_reader); + free(count_writer); + return 0; +} diff --git a/tests/benchmark/test_rwlock_timing.c b/tests/benchmark/test_rwlock_timing.c new file mode 100644 index 0000000..d916071 --- /dev/null +++ b/tests/benchmark/test_rwlock_timing.c @@ -0,0 +1,183 @@ +/* + * test_urcu.c + * + * Userspace RCU library - test program + * + * Copyright February 2009 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "thread-id.h" + +#include + +struct test_array { + int a; +}; + +pthread_rwlock_t lock = PTHREAD_RWLOCK_INITIALIZER; + +static struct test_array test_array = { 8 }; + +#define OUTER_READ_LOOP 200U +#define INNER_READ_LOOP 100000U +#define READ_LOOP ((unsigned long long)OUTER_READ_LOOP * INNER_READ_LOOP) + +#define OUTER_WRITE_LOOP 10U +#define INNER_WRITE_LOOP 200U +#define WRITE_LOOP ((unsigned long long)OUTER_WRITE_LOOP * INNER_WRITE_LOOP) + +static int num_read; +static int num_write; + +#define NR_READ num_read +#define NR_WRITE num_write + +static cycles_t __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *reader_time; +static cycles_t __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *writer_time; + +void *thr_reader(void *arg) +{ + int i, j; + cycles_t time1, time2; + + printf("thread_begin %s, tid %lu\n", + "reader", urcu_get_thread_id()); + sleep(2); + + time1 = caa_get_cycles(); + for (i = 0; i < OUTER_READ_LOOP; i++) { + for (j = 0; j < INNER_READ_LOOP; j++) { + pthread_rwlock_rdlock(&lock); + assert(test_array.a == 8); + pthread_rwlock_unlock(&lock); + } + } + time2 = caa_get_cycles(); + + reader_time[(unsigned long)arg] = time2 - time1; + + sleep(2); + printf("thread_end %s, tid %lu\n", + "reader", urcu_get_thread_id()); + return ((void*)1); + +} + +void *thr_writer(void *arg) +{ + int i, j; + cycles_t time1, time2; + + printf("thread_begin %s, tid %lu\n", + "writer", urcu_get_thread_id()); + sleep(2); + + for (i = 0; i < OUTER_WRITE_LOOP; i++) { + for (j = 0; j < INNER_WRITE_LOOP; j++) { + time1 = caa_get_cycles(); + pthread_rwlock_wrlock(&lock); + test_array.a = 8; + pthread_rwlock_unlock(&lock); + time2 = caa_get_cycles(); + writer_time[(unsigned long)arg] += time2 - time1; + usleep(1); + } + } + + printf("thread_end %s, tid %lu\n", + "writer", urcu_get_thread_id()); + return ((void*)2); +} + +int main(int argc, char **argv) +{ + int err; + pthread_t *tid_reader, *tid_writer; + void *tret; + int i; + cycles_t tot_rtime = 0; + cycles_t tot_wtime = 0; + + if (argc < 2) { + printf("Usage : %s nr_readers nr_writers\n", argv[0]); + exit(-1); + } + num_read = atoi(argv[1]); + num_write = atoi(argv[2]); + + reader_time = calloc(num_read, sizeof(*reader_time)); + writer_time = calloc(num_write, sizeof(*writer_time)); + tid_reader = calloc(num_read, sizeof(*tid_reader)); + tid_writer = calloc(num_write, sizeof(*tid_writer)); + + printf("thread %-6s, tid %lu\n", + "main", urcu_get_thread_id()); + + for (i = 0; i < NR_READ; i++) { + err = pthread_create(&tid_reader[i], NULL, thr_reader, + (void *)(long)i); + if (err != 0) + exit(1); + } + for (i = 0; i < NR_WRITE; i++) { + err = pthread_create(&tid_writer[i], NULL, thr_writer, + (void *)(long)i); + if (err != 0) + exit(1); + } + + sleep(10); + + for (i = 0; i < NR_READ; i++) { + err = pthread_join(tid_reader[i], &tret); + if (err != 0) + exit(1); + tot_rtime += reader_time[i]; + } + for (i = 0; i < NR_WRITE; i++) { + err = pthread_join(tid_writer[i], &tret); + if (err != 0) + exit(1); + tot_wtime += writer_time[i]; + } + printf("Time per read : %g cycles\n", + (double)tot_rtime / ((double)NR_READ * (double)READ_LOOP)); + printf("Time per write : %g cycles\n", + (double)tot_wtime / ((double)NR_WRITE * (double)WRITE_LOOP)); + + free(reader_time); + free(writer_time); + free(tid_reader); + free(tid_writer); + + return 0; +} diff --git a/tests/benchmark/test_urcu.c b/tests/benchmark/test_urcu.c new file mode 100644 index 0000000..e87e235 --- /dev/null +++ b/tests/benchmark/test_urcu.c @@ -0,0 +1,404 @@ +/* + * test_urcu.c + * + * Userspace RCU library - test program + * + * Copyright February 2009 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "cpuset.h" +#include "thread-id.h" + +/* hardcoded number of CPUs */ +#define NR_CPUS 16384 + +#ifndef DYNAMIC_LINK_TEST +#define _LGPL_SOURCE +#else +#define rcu_debug_yield_read() +#endif +#include + +static volatile int test_go, test_stop; + +static unsigned long wdelay; + +static int *test_rcu_pointer; + +static unsigned long duration; + +/* read-side C.S. duration, in loops */ +static unsigned long rduration; + +/* write-side C.S. duration, in loops */ +static unsigned long wduration; + +static inline void loop_sleep(unsigned long loops) +{ + while (loops-- != 0) + caa_cpu_relax(); +} + +static int verbose_mode; + +#define printf_verbose(fmt, args...) \ + do { \ + if (verbose_mode) \ + printf(fmt, args); \ + } while (0) + +static unsigned int cpu_affinities[NR_CPUS]; +static unsigned int next_aff = 0; +static int use_affinity = 0; + +pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void set_affinity(void) +{ +#if HAVE_SCHED_SETAFFINITY + cpu_set_t mask; + int cpu, ret; +#endif /* HAVE_SCHED_SETAFFINITY */ + + if (!use_affinity) + return; + +#if HAVE_SCHED_SETAFFINITY + ret = pthread_mutex_lock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } + cpu = cpu_affinities[next_aff++]; + ret = pthread_mutex_unlock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } + + CPU_ZERO(&mask); + CPU_SET(cpu, &mask); +#if SCHED_SETAFFINITY_ARGS == 2 + sched_setaffinity(0, &mask); +#else + sched_setaffinity(0, sizeof(mask), &mask); +#endif +#endif /* HAVE_SCHED_SETAFFINITY */ +} + +/* + * returns 0 if test should end. + */ +static int test_duration_write(void) +{ + return !test_stop; +} + +static int test_duration_read(void) +{ + return !test_stop; +} + +static DEFINE_URCU_TLS(unsigned long long, nr_writes); +static DEFINE_URCU_TLS(unsigned long long, nr_reads); + +static unsigned int nr_readers; +static unsigned int nr_writers; + +pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; + +void rcu_copy_mutex_lock(void) +{ + int ret; + ret = pthread_mutex_lock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } +} + +void rcu_copy_mutex_unlock(void) +{ + int ret; + + ret = pthread_mutex_unlock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } +} + +void *thr_reader(void *_count) +{ + unsigned long long *count = _count; + int *local_ptr; + + printf_verbose("thread_begin %s, tid %lu\n", + "reader", urcu_get_thread_id()); + + set_affinity(); + + rcu_register_thread(); + assert(!rcu_read_ongoing()); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + rcu_read_lock(); + assert(rcu_read_ongoing()); + local_ptr = rcu_dereference(test_rcu_pointer); + rcu_debug_yield_read(); + if (local_ptr) + assert(*local_ptr == 8); + if (caa_unlikely(rduration)) + loop_sleep(rduration); + rcu_read_unlock(); + URCU_TLS(nr_reads)++; + if (caa_unlikely(!test_duration_read())) + break; + } + + rcu_unregister_thread(); + + /* test extra thread registration */ + rcu_register_thread(); + rcu_unregister_thread(); + + *count = URCU_TLS(nr_reads); + printf_verbose("thread_end %s, tid %lu\n", + "reader", urcu_get_thread_id()); + return ((void*)1); + +} + +void *thr_writer(void *_count) +{ + unsigned long long *count = _count; + int *new, *old; + + printf_verbose("thread_begin %s, tid %lu\n", + "writer", urcu_get_thread_id()); + + set_affinity(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + new = malloc(sizeof(int)); + assert(new); + *new = 8; + old = rcu_xchg_pointer(&test_rcu_pointer, new); + if (caa_unlikely(wduration)) + loop_sleep(wduration); + synchronize_rcu(); + if (old) + *old = 0; + free(old); + URCU_TLS(nr_writes)++; + if (caa_unlikely(!test_duration_write())) + break; + if (caa_unlikely(wdelay)) + loop_sleep(wdelay); + } + + printf_verbose("thread_end %s, tid %lu\n", + "writer", urcu_get_thread_id()); + *count = URCU_TLS(nr_writes); + return ((void*)2); +} + +void show_usage(int argc, char **argv) +{ + printf("Usage : %s nr_readers nr_writers duration (s) \n", + argv[0]); + printf("OPTIONS:\n"); +#ifdef DEBUG_YIELD + printf(" [-r] [-w] (yield reader and/or writer)\n"); +#endif + printf(" [-d delay] (writer period (us))\n"); + printf(" [-c duration] (reader C.S. duration (in loops))\n"); + printf(" [-e duration] (writer C.S. duration (in loops))\n"); + printf(" [-v] (verbose output)\n"); + printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); + printf("\n"); +} + +int main(int argc, char **argv) +{ + int err; + pthread_t *tid_reader, *tid_writer; + void *tret; + unsigned long long *count_reader, *count_writer; + unsigned long long tot_reads = 0, tot_writes = 0; + int i, a; + + if (argc < 4) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[1], "%u", &nr_readers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[2], "%u", &nr_writers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[3], "%lu", &duration); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + for (i = 4; i < argc; i++) { + if (argv[i][0] != '-') + continue; + switch (argv[i][1]) { +#ifdef DEBUG_YIELD + case 'r': + rcu_yield_active |= RCU_YIELD_READ; + break; + case 'w': + rcu_yield_active |= RCU_YIELD_WRITE; + break; +#endif + case 'a': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + a = atoi(argv[++i]); + cpu_affinities[next_aff++] = a; + use_affinity = 1; + printf_verbose("Adding CPU %d affinity\n", a); + break; + case 'c': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + rduration = atol(argv[++i]); + break; + case 'd': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wdelay = atol(argv[++i]); + break; + case 'e': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wduration = atol(argv[++i]); + break; + case 'v': + verbose_mode = 1; + break; + } + } + + printf_verbose("running test for %lu seconds, %u readers, %u writers.\n", + duration, nr_readers, nr_writers); + printf_verbose("Writer delay : %lu loops.\n", wdelay); + printf_verbose("Reader duration : %lu loops.\n", rduration); + printf_verbose("thread %-6s, tid %lu\n", + "main", urcu_get_thread_id()); + + tid_reader = calloc(nr_readers, sizeof(*tid_reader)); + tid_writer = calloc(nr_writers, sizeof(*tid_writer)); + count_reader = calloc(nr_readers, sizeof(*count_reader)); + count_writer = calloc(nr_writers, sizeof(*count_writer)); + + next_aff = 0; + + for (i = 0; i < nr_readers; i++) { + err = pthread_create(&tid_reader[i], NULL, thr_reader, + &count_reader[i]); + if (err != 0) + exit(1); + } + for (i = 0; i < nr_writers; i++) { + err = pthread_create(&tid_writer[i], NULL, thr_writer, + &count_writer[i]); + if (err != 0) + exit(1); + } + + cmm_smp_mb(); + + test_go = 1; + + sleep(duration); + + test_stop = 1; + + for (i = 0; i < nr_readers; i++) { + err = pthread_join(tid_reader[i], &tret); + if (err != 0) + exit(1); + tot_reads += count_reader[i]; + } + for (i = 0; i < nr_writers; i++) { + err = pthread_join(tid_writer[i], &tret); + if (err != 0) + exit(1); + tot_writes += count_writer[i]; + } + + printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads, + tot_writes); + printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu " + "nr_writers %3u " + "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n", + argv[0], duration, nr_readers, rduration, wduration, + nr_writers, wdelay, tot_reads, tot_writes, + tot_reads + tot_writes); + free(test_rcu_pointer); + free(tid_reader); + free(tid_writer); + free(count_reader); + free(count_writer); + return 0; +} diff --git a/tests/benchmark/test_urcu_assign.c b/tests/benchmark/test_urcu_assign.c new file mode 100644 index 0000000..6c4adc3 --- /dev/null +++ b/tests/benchmark/test_urcu_assign.c @@ -0,0 +1,439 @@ +/* + * test_urcu_assign.c + * + * Userspace RCU library - test program + * + * Copyright February 2009 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "cpuset.h" +#include "thread-id.h" + +/* hardcoded number of CPUs */ +#define NR_CPUS 16384 + +#ifndef DYNAMIC_LINK_TEST +#define _LGPL_SOURCE +#else +#define rcu_debug_yield_read() +#endif +#include + +struct test_array { + int a; +}; + +static volatile int test_go, test_stop; + +static unsigned long wdelay; + +static struct test_array *test_rcu_pointer; + +static unsigned long duration; + +/* read-side C.S. duration, in loops */ +static unsigned long rduration; + +/* write-side C.S. duration, in loops */ +static unsigned long wduration; + +static inline void loop_sleep(unsigned long loops) +{ + while (loops-- != 0) + caa_cpu_relax(); +} + +static int verbose_mode; + +#define printf_verbose(fmt, args...) \ + do { \ + if (verbose_mode) \ + printf(fmt, args); \ + } while (0) + +static unsigned int cpu_affinities[NR_CPUS]; +static unsigned int next_aff = 0; +static int use_affinity = 0; + +pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void set_affinity(void) +{ +#if HAVE_SCHED_SETAFFINITY + cpu_set_t mask; + int cpu, ret; +#endif /* HAVE_SCHED_SETAFFINITY */ + + if (!use_affinity) + return; + +#if HAVE_SCHED_SETAFFINITY + ret = pthread_mutex_lock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } + cpu = cpu_affinities[next_aff++]; + ret = pthread_mutex_unlock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } + + CPU_ZERO(&mask); + CPU_SET(cpu, &mask); +#if SCHED_SETAFFINITY_ARGS == 2 + sched_setaffinity(0, &mask); +#else + sched_setaffinity(0, sizeof(mask), &mask); +#endif +#endif /* HAVE_SCHED_SETAFFINITY */ +} + +/* + * returns 0 if test should end. + */ +static int test_duration_write(void) +{ + return !test_stop; +} + +static int test_duration_read(void) +{ + return !test_stop; +} + +static DEFINE_URCU_TLS(unsigned long long, nr_writes); +static DEFINE_URCU_TLS(unsigned long long, nr_reads); + +static unsigned int nr_readers; +static unsigned int nr_writers; + +pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; + +void rcu_copy_mutex_lock(void) +{ + int ret; + ret = pthread_mutex_lock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } +} + +void rcu_copy_mutex_unlock(void) +{ + int ret; + + ret = pthread_mutex_unlock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } +} + +/* + * malloc/free are reusing memory areas too quickly, which does not let us + * test races appropriately. Use a large circular array for allocations. + * ARRAY_SIZE is larger than nr_writers, and we keep the mutex across + * both alloc and free, which insures we never run over our tail. + */ +#define ARRAY_SIZE (1048576 * nr_writers) +#define ARRAY_POISON 0xDEADBEEF +static int array_index; +static struct test_array *test_array; + +static struct test_array *test_array_alloc(void) +{ + struct test_array *ret; + int index; + + index = array_index % ARRAY_SIZE; + assert(test_array[index].a == ARRAY_POISON || + test_array[index].a == 0); + ret = &test_array[index]; + array_index++; + if (array_index == ARRAY_SIZE) + array_index = 0; + return ret; +} + +static void test_array_free(struct test_array *ptr) +{ + if (!ptr) + return; + ptr->a = ARRAY_POISON; +} + +void *thr_reader(void *_count) +{ + unsigned long long *count = _count; + struct test_array *local_ptr; + + printf_verbose("thread_begin %s, tid %lu\n", + "reader", urcu_get_thread_id()); + + set_affinity(); + + rcu_register_thread(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + rcu_read_lock(); + local_ptr = rcu_dereference(test_rcu_pointer); + rcu_debug_yield_read(); + if (local_ptr) + assert(local_ptr->a == 8); + if (caa_unlikely(rduration)) + loop_sleep(rduration); + rcu_read_unlock(); + URCU_TLS(nr_reads)++; + if (caa_unlikely(!test_duration_read())) + break; + } + + rcu_unregister_thread(); + + *count = URCU_TLS(nr_reads); + printf_verbose("thread_end %s, tid %lu\n", + "reader", urcu_get_thread_id()); + return ((void*)1); + +} + +void *thr_writer(void *_count) +{ + unsigned long long *count = _count; + struct test_array *new, *old; + + printf_verbose("thread_begin %s, tid %lu\n", + "writer", urcu_get_thread_id()); + + set_affinity(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + rcu_copy_mutex_lock(); + new = test_array_alloc(); + new->a = 8; + old = test_rcu_pointer; + rcu_assign_pointer(test_rcu_pointer, new); + if (caa_unlikely(wduration)) + loop_sleep(wduration); + synchronize_rcu(); + if (old) + old->a = 0; + test_array_free(old); + rcu_copy_mutex_unlock(); + URCU_TLS(nr_writes)++; + if (caa_unlikely(!test_duration_write())) + break; + if (caa_unlikely(wdelay)) + loop_sleep(wdelay); + } + + printf_verbose("thread_end %s, tid %lu\n", + "writer", urcu_get_thread_id()); + *count = URCU_TLS(nr_writes); + return ((void*)2); +} + +void show_usage(int argc, char **argv) +{ + printf("Usage : %s nr_readers nr_writers duration (s) \n", + argv[0]); + printf("OPTIONS:\n"); +#ifdef DEBUG_YIELD + printf(" [-r] [-w] (yield reader and/or writer)\n"); +#endif + printf(" [-d delay] (writer period (us))\n"); + printf(" [-c duration] (reader C.S. duration (in loops))\n"); + printf(" [-e duration] (writer C.S. duration (in loops))\n"); + printf(" [-v] (verbose output)\n"); + printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); + printf("\n"); +} + +int main(int argc, char **argv) +{ + int err; + pthread_t *tid_reader, *tid_writer; + void *tret; + unsigned long long *count_reader, *count_writer; + unsigned long long tot_reads = 0, tot_writes = 0; + int i, a; + + if (argc < 4) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[1], "%u", &nr_readers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[2], "%u", &nr_writers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[3], "%lu", &duration); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + for (i = 4; i < argc; i++) { + if (argv[i][0] != '-') + continue; + switch (argv[i][1]) { +#ifdef DEBUG_YIELD + case 'r': + rcu_yield_active |= RCU_YIELD_READ; + break; + case 'w': + rcu_yield_active |= RCU_YIELD_WRITE; + break; +#endif + case 'a': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + a = atoi(argv[++i]); + cpu_affinities[next_aff++] = a; + use_affinity = 1; + printf_verbose("Adding CPU %d affinity\n", a); + break; + case 'c': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + rduration = atol(argv[++i]); + break; + case 'd': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wdelay = atol(argv[++i]); + break; + case 'e': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wduration = atol(argv[++i]); + break; + case 'v': + verbose_mode = 1; + break; + } + } + + printf_verbose("running test for %lu seconds, %u readers, %u writers.\n", + duration, nr_readers, nr_writers); + printf_verbose("Writer delay : %lu loops.\n", wdelay); + printf_verbose("Reader duration : %lu loops.\n", rduration); + printf_verbose("thread %-6s, tid %lu\n", + "main", urcu_get_thread_id()); + + test_array = calloc(1, sizeof(*test_array) * ARRAY_SIZE); + tid_reader = calloc(nr_readers, sizeof(*tid_reader)); + tid_writer = calloc(nr_writers, sizeof(*tid_writer)); + count_reader = calloc(nr_readers, sizeof(*count_reader)); + count_writer = calloc(nr_writers, sizeof(*count_writer)); + + next_aff = 0; + + for (i = 0; i < nr_readers; i++) { + err = pthread_create(&tid_reader[i], NULL, thr_reader, + &count_reader[i]); + if (err != 0) + exit(1); + } + for (i = 0; i < nr_writers; i++) { + err = pthread_create(&tid_writer[i], NULL, thr_writer, + &count_writer[i]); + if (err != 0) + exit(1); + } + + cmm_smp_mb(); + + test_go = 1; + + sleep(duration); + + test_stop = 1; + + for (i = 0; i < nr_readers; i++) { + err = pthread_join(tid_reader[i], &tret); + if (err != 0) + exit(1); + tot_reads += count_reader[i]; + } + for (i = 0; i < nr_writers; i++) { + err = pthread_join(tid_writer[i], &tret); + if (err != 0) + exit(1); + tot_writes += count_writer[i]; + } + + printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads, + tot_writes); + printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu " + "nr_writers %3u " + "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n", + argv[0], duration, nr_readers, rduration, wduration, + nr_writers, wdelay, tot_reads, tot_writes, + tot_reads + tot_writes); + test_array_free(test_rcu_pointer); + free(test_array); + free(tid_reader); + free(tid_writer); + free(count_reader); + free(count_writer); + return 0; +} diff --git a/tests/benchmark/test_urcu_bp.c b/tests/benchmark/test_urcu_bp.c new file mode 100644 index 0000000..0f21da9 --- /dev/null +++ b/tests/benchmark/test_urcu_bp.c @@ -0,0 +1,399 @@ +/* + * test_urcu.c + * + * Userspace RCU library - test program + * + * Copyright February 2009 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "cpuset.h" +#include "thread-id.h" + +/* hardcoded number of CPUs */ +#define NR_CPUS 16384 + +#ifndef DYNAMIC_LINK_TEST +#define _LGPL_SOURCE +#else +#define rcu_debug_yield_read() +#endif +#include + +static volatile int test_go, test_stop; + +static unsigned long wdelay; + +static int *test_rcu_pointer; + +static unsigned long duration; + +/* read-side C.S. duration, in loops */ +static unsigned long rduration; + +/* write-side C.S. duration, in loops */ +static unsigned long wduration; + +static inline void loop_sleep(unsigned long loops) +{ + while (loops-- != 0) + caa_cpu_relax(); +} + +static int verbose_mode; + +#define printf_verbose(fmt, args...) \ + do { \ + if (verbose_mode) \ + printf(fmt, args); \ + } while (0) + +static unsigned int cpu_affinities[NR_CPUS]; +static unsigned int next_aff = 0; +static int use_affinity = 0; + +pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void set_affinity(void) +{ +#if HAVE_SCHED_SETAFFINITY + cpu_set_t mask; + int cpu, ret; +#endif /* HAVE_SCHED_SETAFFINITY */ + + if (!use_affinity) + return; + +#if HAVE_SCHED_SETAFFINITY + ret = pthread_mutex_lock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } + cpu = cpu_affinities[next_aff++]; + ret = pthread_mutex_unlock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } + + CPU_ZERO(&mask); + CPU_SET(cpu, &mask); +#if SCHED_SETAFFINITY_ARGS == 2 + sched_setaffinity(0, &mask); +#else + sched_setaffinity(0, sizeof(mask), &mask); +#endif +#endif /* HAVE_SCHED_SETAFFINITY */ +} + +/* + * returns 0 if test should end. + */ +static int test_duration_write(void) +{ + return !test_stop; +} + +static int test_duration_read(void) +{ + return !test_stop; +} + +static DEFINE_URCU_TLS(unsigned long long, nr_writes); +static DEFINE_URCU_TLS(unsigned long long, nr_reads); + +static unsigned int nr_readers; +static unsigned int nr_writers; + +pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; + +void rcu_copy_mutex_lock(void) +{ + int ret; + ret = pthread_mutex_lock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } +} + +void rcu_copy_mutex_unlock(void) +{ + int ret; + + ret = pthread_mutex_unlock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } +} + +void *thr_reader(void *_count) +{ + unsigned long long *count = _count; + int *local_ptr; + + printf_verbose("thread_begin %s, tid %lu\n", + "reader", urcu_get_thread_id()); + + set_affinity(); + + rcu_register_thread(); + assert(!rcu_read_ongoing()); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + rcu_read_lock(); + assert(rcu_read_ongoing()); + local_ptr = rcu_dereference(test_rcu_pointer); + rcu_debug_yield_read(); + if (local_ptr) + assert(*local_ptr == 8); + if (caa_unlikely(rduration)) + loop_sleep(rduration); + rcu_read_unlock(); + URCU_TLS(nr_reads)++; + if (caa_unlikely(!test_duration_read())) + break; + } + + rcu_unregister_thread(); + + *count = URCU_TLS(nr_reads); + printf_verbose("thread_end %s, tid %lu\n", + "reader", urcu_get_thread_id()); + return ((void*)1); + +} + +void *thr_writer(void *_count) +{ + unsigned long long *count = _count; + int *new, *old; + + printf_verbose("thread_begin %s, tid %lu\n", + "writer", urcu_get_thread_id()); + + set_affinity(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + new = malloc(sizeof(int)); + *new = 8; + old = rcu_xchg_pointer(&test_rcu_pointer, new); + if (caa_unlikely(wduration)) + loop_sleep(wduration); + synchronize_rcu(); + if (old) + *old = 0; + free(old); + URCU_TLS(nr_writes)++; + if (caa_unlikely(!test_duration_write())) + break; + if (caa_unlikely(wdelay)) + loop_sleep(wdelay); + } + + printf_verbose("thread_end %s, tid %lu\n", + "writer", urcu_get_thread_id()); + *count = URCU_TLS(nr_writes); + return ((void*)2); +} + +void show_usage(int argc, char **argv) +{ + printf("Usage : %s nr_readers nr_writers duration (s) \n", + argv[0]); + printf("OPTIONS:\n"); +#ifdef DEBUG_YIELD + printf(" [-r] [-w] (yield reader and/or writer)\n"); +#endif + printf(" [-d delay] (writer period (us))\n"); + printf(" [-c duration] (reader C.S. duration (in loops))\n"); + printf(" [-e duration] (writer C.S. duration (in loops))\n"); + printf(" [-v] (verbose output)\n"); + printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); + printf("\n"); +} + +int main(int argc, char **argv) +{ + int err; + pthread_t *tid_reader, *tid_writer; + void *tret; + unsigned long long *count_reader, *count_writer; + unsigned long long tot_reads = 0, tot_writes = 0; + int i, a; + + if (argc < 4) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[1], "%u", &nr_readers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[2], "%u", &nr_writers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[3], "%lu", &duration); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + for (i = 4; i < argc; i++) { + if (argv[i][0] != '-') + continue; + switch (argv[i][1]) { +#ifdef DEBUG_YIELD + case 'r': + rcu_yield_active |= RCU_YIELD_READ; + break; + case 'w': + rcu_yield_active |= RCU_YIELD_WRITE; + break; +#endif + case 'a': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + a = atoi(argv[++i]); + cpu_affinities[next_aff++] = a; + use_affinity = 1; + printf_verbose("Adding CPU %d affinity\n", a); + break; + case 'c': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + rduration = atol(argv[++i]); + break; + case 'd': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wdelay = atol(argv[++i]); + break; + case 'e': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wduration = atol(argv[++i]); + break; + case 'v': + verbose_mode = 1; + break; + } + } + + printf_verbose("running test for %lu seconds, %u readers, %u writers.\n", + duration, nr_readers, nr_writers); + printf_verbose("Writer delay : %lu loops.\n", wdelay); + printf_verbose("Reader duration : %lu loops.\n", rduration); + printf_verbose("thread %-6s, tid %lu\n", + "main", urcu_get_thread_id()); + + tid_reader = calloc(nr_readers, sizeof(*tid_reader)); + tid_writer = calloc(nr_writers, sizeof(*tid_writer)); + count_reader = calloc(nr_readers, sizeof(*count_reader)); + count_writer = calloc(nr_writers, sizeof(*count_writer)); + + next_aff = 0; + + for (i = 0; i < nr_readers; i++) { + err = pthread_create(&tid_reader[i], NULL, thr_reader, + &count_reader[i]); + if (err != 0) + exit(1); + } + for (i = 0; i < nr_writers; i++) { + err = pthread_create(&tid_writer[i], NULL, thr_writer, + &count_writer[i]); + if (err != 0) + exit(1); + } + + cmm_smp_mb(); + + test_go = 1; + + sleep(duration); + + test_stop = 1; + + for (i = 0; i < nr_readers; i++) { + err = pthread_join(tid_reader[i], &tret); + if (err != 0) + exit(1); + tot_reads += count_reader[i]; + } + for (i = 0; i < nr_writers; i++) { + err = pthread_join(tid_writer[i], &tret); + if (err != 0) + exit(1); + tot_writes += count_writer[i]; + } + + printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads, + tot_writes); + printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu " + "nr_writers %3u " + "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n", + argv[0], duration, nr_readers, rduration, wduration, + nr_writers, wdelay, tot_reads, tot_writes, + tot_reads + tot_writes); + free(test_rcu_pointer); + free(tid_reader); + free(tid_writer); + free(count_reader); + free(count_writer); + return 0; +} diff --git a/tests/benchmark/test_urcu_defer.c b/tests/benchmark/test_urcu_defer.c new file mode 100644 index 0000000..80249bd --- /dev/null +++ b/tests/benchmark/test_urcu_defer.c @@ -0,0 +1,426 @@ +/* + * test_urcu_defer.c + * + * Userspace RCU library - test program (with automatic reclamation) + * + * Copyright February 2009 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "cpuset.h" +#include "thread-id.h" + +/* hardcoded number of CPUs */ +#define NR_CPUS 16384 + +#ifndef DYNAMIC_LINK_TEST +#define _LGPL_SOURCE +#else +#define rcu_debug_yield_read() +#endif +#include +#include + +struct test_array { + int a; +}; + +static volatile int test_go, test_stop; + +static unsigned long wdelay; + +static struct test_array *test_rcu_pointer; + +static unsigned long duration; + +/* read-side C.S. duration, in loops */ +static unsigned long rduration; + +/* write-side C.S. duration, in loops */ +static unsigned long wduration; + +static inline void loop_sleep(unsigned long loops) +{ + while (loops-- != 0) + caa_cpu_relax(); +} + +static int verbose_mode; + +#define printf_verbose(fmt, args...) \ + do { \ + if (verbose_mode) \ + printf(fmt, args); \ + } while (0) + +static unsigned int cpu_affinities[NR_CPUS]; +static unsigned int next_aff = 0; +static int use_affinity = 0; + +pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void set_affinity(void) +{ +#if HAVE_SCHED_SETAFFINITY + cpu_set_t mask; + int cpu, ret; +#endif /* HAVE_SCHED_SETAFFINITY */ + + if (!use_affinity) + return; + +#if HAVE_SCHED_SETAFFINITY + ret = pthread_mutex_lock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } + cpu = cpu_affinities[next_aff++]; + ret = pthread_mutex_unlock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } + + CPU_ZERO(&mask); + CPU_SET(cpu, &mask); +#if SCHED_SETAFFINITY_ARGS == 2 + sched_setaffinity(0, &mask); +#else + sched_setaffinity(0, sizeof(mask), &mask); +#endif +#endif /* HAVE_SCHED_SETAFFINITY */ +} + +/* + * returns 0 if test should end. + */ +static int test_duration_write(void) +{ + return !test_stop; +} + +static int test_duration_read(void) +{ + return !test_stop; +} + +static DEFINE_URCU_TLS(unsigned long long, nr_writes); +static DEFINE_URCU_TLS(unsigned long long, nr_reads); + +static +unsigned long long __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *tot_nr_writes; + +static unsigned int nr_readers; +static unsigned int nr_writers; + +pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; + +void rcu_copy_mutex_lock(void) +{ + int ret; + ret = pthread_mutex_lock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } +} + +void rcu_copy_mutex_unlock(void) +{ + int ret; + + ret = pthread_mutex_unlock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } +} + +void *thr_reader(void *_count) +{ + unsigned long long *count = _count; + struct test_array *local_ptr; + + printf_verbose("thread_begin %s, tid %lu\n", + "reader", urcu_get_thread_id()); + + set_affinity(); + + rcu_register_thread(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + rcu_read_lock(); + local_ptr = rcu_dereference(test_rcu_pointer); + rcu_debug_yield_read(); + if (local_ptr) + assert(local_ptr->a == 8); + if (caa_unlikely(rduration)) + loop_sleep(rduration); + rcu_read_unlock(); + URCU_TLS(nr_reads)++; + if (caa_unlikely(!test_duration_read())) + break; + } + + rcu_unregister_thread(); + + *count = URCU_TLS(nr_reads); + printf_verbose("thread_end %s, tid %lu\n", + "reader", urcu_get_thread_id()); + return ((void*)1); + +} + +static void test_cb2(void *data) +{ +} + +static void test_cb1(void *data) +{ +} + +void *thr_writer(void *data) +{ + unsigned long wtidx = (unsigned long)data; + struct test_array *new, *old = NULL; + int ret; + + printf_verbose("thread_begin %s, tid %lu\n", + "writer", urcu_get_thread_id()); + + set_affinity(); + + ret = rcu_defer_register_thread(); + if (ret) { + printf("Error in rcu_defer_register_thread\n"); + exit(-1); + } + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + new = malloc(sizeof(*new)); + new->a = 8; + old = rcu_xchg_pointer(&test_rcu_pointer, new); + if (caa_unlikely(wduration)) + loop_sleep(wduration); + defer_rcu(free, old); + defer_rcu(test_cb1, old); + defer_rcu(test_cb1, (void *)-2L); + defer_rcu(test_cb1, (void *)-2L); + defer_rcu(test_cb1, old); + defer_rcu(test_cb2, (void *)-2L); + defer_rcu(test_cb2, (void *)-4L); + defer_rcu(test_cb2, (void *)-2L); + URCU_TLS(nr_writes)++; + if (caa_unlikely(!test_duration_write())) + break; + if (caa_unlikely(wdelay)) + loop_sleep(wdelay); + } + + rcu_defer_unregister_thread(); + + printf_verbose("thread_end %s, tid %lu\n", + "writer", urcu_get_thread_id()); + tot_nr_writes[wtidx] = URCU_TLS(nr_writes); + return ((void*)2); +} + +void show_usage(int argc, char **argv) +{ + printf("Usage : %s nr_readers nr_writers duration (s) \n", + argv[0]); + printf("OPTIONS:\n"); +#ifdef DEBUG_YIELD + printf(" [-r] [-w] (yield reader and/or writer)\n"); +#endif + printf(" [-d delay] (writer period (us))\n"); + printf(" [-c duration] (reader C.S. duration (in loops))\n"); + printf(" [-e duration] (writer C.S. duration (in loops))\n"); + printf(" [-v] (verbose output)\n"); + printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); + printf("\n"); +} + +int main(int argc, char **argv) +{ + int err; + pthread_t *tid_reader, *tid_writer; + void *tret; + unsigned long long *count_reader; + unsigned long long tot_reads = 0, tot_writes = 0; + int i, a; + + if (argc < 4) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[1], "%u", &nr_readers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[2], "%u", &nr_writers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[3], "%lu", &duration); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + for (i = 4; i < argc; i++) { + if (argv[i][0] != '-') + continue; + switch (argv[i][1]) { +#ifdef DEBUG_YIELD + case 'r': + rcu_yield_active |= RCU_YIELD_READ; + break; + case 'w': + rcu_yield_active |= RCU_YIELD_WRITE; + break; +#endif + case 'a': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + a = atoi(argv[++i]); + cpu_affinities[next_aff++] = a; + use_affinity = 1; + printf_verbose("Adding CPU %d affinity\n", a); + break; + case 'c': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + rduration = atol(argv[++i]); + break; + case 'd': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wdelay = atol(argv[++i]); + break; + case 'e': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wduration = atol(argv[++i]); + break; + case 'v': + verbose_mode = 1; + break; + } + } + + printf_verbose("running test for %lu seconds, %u readers, %u writers.\n", + duration, nr_readers, nr_writers); + printf_verbose("Writer delay : %lu loops.\n", wdelay); + printf_verbose("Reader duration : %lu loops.\n", rduration); + printf_verbose("thread %-6s, tid %lu\n", + "main", urcu_get_thread_id()); + + tid_reader = calloc(nr_readers, sizeof(*tid_reader)); + tid_writer = calloc(nr_writers, sizeof(*tid_writer)); + count_reader = calloc(nr_readers, sizeof(*count_reader)); + tot_nr_writes = calloc(nr_writers, sizeof(*tot_nr_writes)); + + next_aff = 0; + + for (i = 0; i < nr_readers; i++) { + err = pthread_create(&tid_reader[i], NULL, thr_reader, + &count_reader[i]); + if (err != 0) + exit(1); + } + for (i = 0; i < nr_writers; i++) { + err = pthread_create(&tid_writer[i], NULL, thr_writer, + (void *)(long)i); + if (err != 0) + exit(1); + } + + cmm_smp_mb(); + + test_go = 1; + + sleep(duration); + + test_stop = 1; + + for (i = 0; i < nr_readers; i++) { + err = pthread_join(tid_reader[i], &tret); + if (err != 0) + exit(1); + tot_reads += count_reader[i]; + } + for (i = 0; i < nr_writers; i++) { + err = pthread_join(tid_writer[i], &tret); + if (err != 0) + exit(1); + tot_writes += tot_nr_writes[i]; + } + + printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads, + tot_writes); + printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu " + "nr_writers %3u " + "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n", + argv[0], duration, nr_readers, rduration, wduration, + nr_writers, wdelay, tot_reads, tot_writes, + tot_reads + tot_writes); + free(tid_reader); + free(tid_writer); + free(count_reader); + free(tot_nr_writes); + + return 0; +} diff --git a/tests/benchmark/test_urcu_gc.c b/tests/benchmark/test_urcu_gc.c new file mode 100644 index 0000000..1375a5e --- /dev/null +++ b/tests/benchmark/test_urcu_gc.c @@ -0,0 +1,470 @@ +/* + * test_urcu_gc.c + * + * Userspace RCU library - test program (with batch reclamation) + * + * Copyright February 2009 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "cpuset.h" +#include "thread-id.h" + +/* hardcoded number of CPUs */ +#define NR_CPUS 16384 + +#ifndef DYNAMIC_LINK_TEST +#define _LGPL_SOURCE +#else +#define rcu_debug_yield_read() +#endif +#include + +struct test_array { + int a; +}; + +static volatile int test_go, test_stop; + +static unsigned long wdelay; + +static struct test_array *test_rcu_pointer; + +static unsigned int reclaim_batch = 1; + +struct reclaim_queue { + void **queue; /* Beginning of queue */ + void **head; /* Insert position */ +}; + +static struct reclaim_queue *pending_reclaims; + +static unsigned long duration; + +/* read-side C.S. duration, in loops */ +static unsigned long rduration; + +/* write-side C.S. duration, in loops */ +static unsigned long wduration; + +static inline void loop_sleep(unsigned long loops) +{ + while (loops-- != 0) + caa_cpu_relax(); +} + +static int verbose_mode; + +#define printf_verbose(fmt, args...) \ + do { \ + if (verbose_mode) \ + printf(fmt, args); \ + } while (0) + +static unsigned int cpu_affinities[NR_CPUS]; +static unsigned int next_aff = 0; +static int use_affinity = 0; + +pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void set_affinity(void) +{ +#if HAVE_SCHED_SETAFFINITY + cpu_set_t mask; + int cpu, ret; +#endif /* HAVE_SCHED_SETAFFINITY */ + + if (!use_affinity) + return; + +#if HAVE_SCHED_SETAFFINITY + ret = pthread_mutex_lock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } + cpu = cpu_affinities[next_aff++]; + ret = pthread_mutex_unlock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } + + CPU_ZERO(&mask); + CPU_SET(cpu, &mask); +#if SCHED_SETAFFINITY_ARGS == 2 + sched_setaffinity(0, &mask); +#else + sched_setaffinity(0, sizeof(mask), &mask); +#endif +#endif /* HAVE_SCHED_SETAFFINITY */ +} + +/* + * returns 0 if test should end. + */ +static int test_duration_write(void) +{ + return !test_stop; +} + +static int test_duration_read(void) +{ + return !test_stop; +} + +static DEFINE_URCU_TLS(unsigned long long, nr_writes); +static DEFINE_URCU_TLS(unsigned long long, nr_reads); + +static +unsigned long long __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *tot_nr_writes; + +static unsigned int nr_readers; +static unsigned int nr_writers; + +pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; + +void rcu_copy_mutex_lock(void) +{ + int ret; + ret = pthread_mutex_lock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } +} + +void rcu_copy_mutex_unlock(void) +{ + int ret; + + ret = pthread_mutex_unlock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } +} + +void *thr_reader(void *_count) +{ + unsigned long long *count = _count; + struct test_array *local_ptr; + + printf_verbose("thread_begin %s, tid %lu\n", + "reader", urcu_get_thread_id()); + + set_affinity(); + + rcu_register_thread(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + rcu_read_lock(); + local_ptr = rcu_dereference(test_rcu_pointer); + rcu_debug_yield_read(); + if (local_ptr) + assert(local_ptr->a == 8); + if (caa_unlikely(rduration)) + loop_sleep(rduration); + rcu_read_unlock(); + URCU_TLS(nr_reads)++; + if (caa_unlikely(!test_duration_read())) + break; + } + + rcu_unregister_thread(); + + *count = URCU_TLS(nr_reads); + printf_verbose("thread_end %s, tid %lu\n", + "reader", urcu_get_thread_id()); + return ((void*)1); + +} + +static void rcu_gc_clear_queue(unsigned long wtidx) +{ + void **p; + + /* Wait for Q.S and empty queue */ + synchronize_rcu(); + + for (p = pending_reclaims[wtidx].queue; + p < pending_reclaims[wtidx].head; p++) { + /* poison */ + if (*p) + ((struct test_array *)*p)->a = 0; + free(*p); + } + pending_reclaims[wtidx].head = pending_reclaims[wtidx].queue; +} + +/* Using per-thread queue */ +static void rcu_gc_reclaim(unsigned long wtidx, void *old) +{ + /* Queue pointer */ + *pending_reclaims[wtidx].head = old; + pending_reclaims[wtidx].head++; + + if (caa_likely(pending_reclaims[wtidx].head - pending_reclaims[wtidx].queue + < reclaim_batch)) + return; + + rcu_gc_clear_queue(wtidx); +} + +void *thr_writer(void *data) +{ + unsigned long wtidx = (unsigned long)data; +#ifdef TEST_LOCAL_GC + struct test_array *old = NULL; +#else + struct test_array *new, *old; +#endif + + printf_verbose("thread_begin %s, tid %lu\n", + "writer", urcu_get_thread_id()); + + set_affinity(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { +#ifndef TEST_LOCAL_GC + new = malloc(sizeof(*new)); + new->a = 8; + old = rcu_xchg_pointer(&test_rcu_pointer, new); +#endif + if (caa_unlikely(wduration)) + loop_sleep(wduration); + rcu_gc_reclaim(wtidx, old); + URCU_TLS(nr_writes)++; + if (caa_unlikely(!test_duration_write())) + break; + if (caa_unlikely(wdelay)) + loop_sleep(wdelay); + } + + printf_verbose("thread_end %s, tid %lu\n", + "writer", urcu_get_thread_id()); + tot_nr_writes[wtidx] = URCU_TLS(nr_writes); + return ((void*)2); +} + +void show_usage(int argc, char **argv) +{ + printf("Usage : %s nr_readers nr_writers duration (s) \n", + argv[0]); + printf("OPTIONS:\n"); +#ifdef DEBUG_YIELD + printf(" [-r] [-w] (yield reader and/or writer)\n"); +#endif + printf(" [-d delay] (writer period (us))\n"); + printf(" [-c duration] (reader C.S. duration (in loops))\n"); + printf(" [-e duration] (writer C.S. duration (in loops))\n"); + printf(" [-v] (verbose output)\n"); + printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); + printf("\n"); +} + +int main(int argc, char **argv) +{ + int err; + pthread_t *tid_reader, *tid_writer; + void *tret; + unsigned long long *count_reader; + unsigned long long tot_reads = 0, tot_writes = 0; + int i, a; + + if (argc < 4) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[1], "%u", &nr_readers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[2], "%u", &nr_writers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[3], "%lu", &duration); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + for (i = 4; i < argc; i++) { + if (argv[i][0] != '-') + continue; + switch (argv[i][1]) { +#ifdef DEBUG_YIELD + case 'r': + rcu_yield_active |= RCU_YIELD_READ; + break; + case 'w': + rcu_yield_active |= RCU_YIELD_WRITE; + break; +#endif + case 'a': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + a = atoi(argv[++i]); + cpu_affinities[next_aff++] = a; + use_affinity = 1; + printf_verbose("Adding CPU %d affinity\n", a); + break; + case 'b': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + reclaim_batch = atol(argv[++i]); + break; + case 'c': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + rduration = atol(argv[++i]); + break; + case 'd': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wdelay = atol(argv[++i]); + break; + case 'e': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wduration = atol(argv[++i]); + break; + case 'v': + verbose_mode = 1; + break; + } + } + + printf_verbose("running test for %lu seconds, %u readers, %u writers.\n", + duration, nr_readers, nr_writers); + printf_verbose("Writer delay : %lu loops.\n", wdelay); + printf_verbose("Reader duration : %lu loops.\n", rduration); + printf_verbose("thread %-6s, tid %lu\n", + "main", urcu_get_thread_id()); + + tid_reader = calloc(nr_readers, sizeof(*tid_reader)); + tid_writer = calloc(nr_writers, sizeof(*tid_writer)); + count_reader = calloc(nr_readers, sizeof(*count_reader)); + tot_nr_writes = calloc(nr_writers, sizeof(*tot_nr_writes)); + pending_reclaims = calloc(nr_writers, sizeof(*pending_reclaims)); + if (reclaim_batch * sizeof(*pending_reclaims[i].queue) + < CAA_CACHE_LINE_SIZE) + for (i = 0; i < nr_writers; i++) + pending_reclaims[i].queue = calloc(1, CAA_CACHE_LINE_SIZE); + else + for (i = 0; i < nr_writers; i++) + pending_reclaims[i].queue = calloc(reclaim_batch, + sizeof(*pending_reclaims[i].queue)); + for (i = 0; i < nr_writers; i++) + pending_reclaims[i].head = pending_reclaims[i].queue; + + next_aff = 0; + + for (i = 0; i < nr_readers; i++) { + err = pthread_create(&tid_reader[i], NULL, thr_reader, + &count_reader[i]); + if (err != 0) + exit(1); + } + for (i = 0; i < nr_writers; i++) { + err = pthread_create(&tid_writer[i], NULL, thr_writer, + (void *)(long)i); + if (err != 0) + exit(1); + } + + cmm_smp_mb(); + + test_go = 1; + + sleep(duration); + + test_stop = 1; + + for (i = 0; i < nr_readers; i++) { + err = pthread_join(tid_reader[i], &tret); + if (err != 0) + exit(1); + tot_reads += count_reader[i]; + } + for (i = 0; i < nr_writers; i++) { + err = pthread_join(tid_writer[i], &tret); + if (err != 0) + exit(1); + tot_writes += tot_nr_writes[i]; + rcu_gc_clear_queue(i); + } + + printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads, + tot_writes); + printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu " + "nr_writers %3u " + "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu " + "batch %u\n", + argv[0], duration, nr_readers, rduration, wduration, + nr_writers, wdelay, tot_reads, tot_writes, + tot_reads + tot_writes, reclaim_batch); + free(tid_reader); + free(tid_writer); + free(count_reader); + free(tot_nr_writes); + for (i = 0; i < nr_writers; i++) + free(pending_reclaims[i].queue); + free(pending_reclaims); + + return 0; +} diff --git a/tests/benchmark/test_urcu_hash.c b/tests/benchmark/test_urcu_hash.c new file mode 100644 index 0000000..f862adb --- /dev/null +++ b/tests/benchmark/test_urcu_hash.c @@ -0,0 +1,774 @@ +/* + * test_urcu_hash.c + * + * Userspace RCU library - test program + * + * Copyright 2009-2012 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include "test_urcu_hash.h" + +enum test_hash { + TEST_HASH_RW, + TEST_HASH_UNIQUE, +}; + +struct test_hash_cb { + void (*sigusr1)(int signo); + void (*sigusr2)(int signo); + void *(*thr_reader)(void *_count); + void *(*thr_writer)(void *_count); + int (*populate_hash)(void); +}; + +static +struct test_hash_cb test_hash_cb[] = { + [TEST_HASH_RW] = { + test_hash_rw_sigusr1_handler, + test_hash_rw_sigusr2_handler, + test_hash_rw_thr_reader, + test_hash_rw_thr_writer, + test_hash_rw_populate_hash, + }, + [TEST_HASH_UNIQUE] = { + test_hash_unique_sigusr1_handler, + test_hash_unique_sigusr2_handler, + test_hash_unique_thr_reader, + test_hash_unique_thr_writer, + test_hash_unique_populate_hash, + }, + +}; + +static enum test_hash test_choice = TEST_HASH_RW; + +void (*get_sigusr1_cb(void))(int) +{ + return test_hash_cb[test_choice].sigusr1; +} + +void (*get_sigusr2_cb(void))(int) +{ + return test_hash_cb[test_choice].sigusr2; +} + +void *(*get_thr_reader_cb(void))(void *) +{ + return test_hash_cb[test_choice].thr_reader; +} + +void *(*get_thr_writer_cb(void))(void *) +{ + return test_hash_cb[test_choice].thr_writer; +} + +int (*get_populate_hash_cb(void))(void) +{ + return test_hash_cb[test_choice].populate_hash; +} + +DEFINE_URCU_TLS(unsigned int, rand_lookup); +DEFINE_URCU_TLS(unsigned long, nr_add); +DEFINE_URCU_TLS(unsigned long, nr_addexist); +DEFINE_URCU_TLS(unsigned long, nr_del); +DEFINE_URCU_TLS(unsigned long, nr_delnoent); +DEFINE_URCU_TLS(unsigned long, lookup_fail); +DEFINE_URCU_TLS(unsigned long, lookup_ok); + +struct cds_lfht *test_ht; + +volatile int test_go, test_stop; + +unsigned long wdelay; + +unsigned long duration; + +/* read-side C.S. duration, in loops */ +unsigned long rduration; + +unsigned long init_hash_size = DEFAULT_HASH_SIZE; +unsigned long min_hash_alloc_size = DEFAULT_MIN_ALLOC_SIZE; +unsigned long max_hash_buckets_size = (1UL << 20); +unsigned long init_populate; +int opt_auto_resize; +int add_only, add_unique, add_replace; +const struct cds_lfht_mm_type *memory_backend; + +unsigned long init_pool_offset, lookup_pool_offset, write_pool_offset; +unsigned long init_pool_size = DEFAULT_RAND_POOL, + lookup_pool_size = DEFAULT_RAND_POOL, + write_pool_size = DEFAULT_RAND_POOL; +int validate_lookup; +unsigned long nr_hash_chains; /* 0: normal table, other: number of hash chains */ + +int count_pipe[2]; + +int verbose_mode; + +unsigned int cpu_affinities[NR_CPUS]; +unsigned int next_aff = 0; +int use_affinity = 0; + +pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; + +DEFINE_URCU_TLS(unsigned long long, nr_writes); +DEFINE_URCU_TLS(unsigned long long, nr_reads); + +unsigned int nr_readers; +unsigned int nr_writers; + +static pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; + +void set_affinity(void) +{ +#if HAVE_SCHED_SETAFFINITY + cpu_set_t mask; + int cpu, ret; +#endif /* HAVE_SCHED_SETAFFINITY */ + + if (!use_affinity) + return; + +#if HAVE_SCHED_SETAFFINITY + ret = pthread_mutex_lock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } + cpu = cpu_affinities[next_aff++]; + ret = pthread_mutex_unlock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } + CPU_ZERO(&mask); + CPU_SET(cpu, &mask); +#if SCHED_SETAFFINITY_ARGS == 2 + sched_setaffinity(0, &mask); +#else + sched_setaffinity(0, sizeof(mask), &mask); +#endif +#endif /* HAVE_SCHED_SETAFFINITY */ +} + +void rcu_copy_mutex_lock(void) +{ + int ret; + ret = pthread_mutex_lock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } +} + +void rcu_copy_mutex_unlock(void) +{ + int ret; + + ret = pthread_mutex_unlock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } +} + +unsigned long test_compare(const void *key1, size_t key1_len, + const void *key2, size_t key2_len) +{ + if (caa_unlikely(key1_len != key2_len)) + return -1; + assert(key1_len == sizeof(unsigned long)); + if (key1 == key2) + return 0; + else + return 1; +} + +void *thr_count(void *arg) +{ + printf_verbose("thread_begin %s, tid %lu\n", + "counter", urcu_get_thread_id()); + + rcu_register_thread(); + + for (;;) { + unsigned long count; + long approx_before, approx_after; + ssize_t len; + char buf[1]; + + rcu_thread_offline(); + len = read(count_pipe[0], buf, 1); + rcu_thread_online(); + if (caa_unlikely(!test_duration_read())) + break; + if (len != 1) + continue; + /* Accounting */ + printf("Counting nodes... "); + fflush(stdout); + rcu_read_lock(); + cds_lfht_count_nodes(test_ht, &approx_before, &count, + &approx_after); + rcu_read_unlock(); + printf("done.\n"); + printf("Approximation before node accounting: %ld nodes.\n", + approx_before); + printf("Accounting of nodes in the hash table: " + "%lu nodes.\n", + count); + printf("Approximation after node accounting: %ld nodes.\n", + approx_after); + } + rcu_unregister_thread(); + return NULL; +} + +void free_node_cb(struct rcu_head *head) +{ + struct lfht_test_node *node = + caa_container_of(head, struct lfht_test_node, head); + free(node); +} + +static +void test_delete_all_nodes(struct cds_lfht *ht) +{ + struct cds_lfht_iter iter; + struct lfht_test_node *node; + unsigned long count = 0; + + cds_lfht_for_each_entry(ht, &iter, node, node) { + int ret; + + ret = cds_lfht_del(test_ht, cds_lfht_iter_get_node(&iter)); + assert(!ret); + call_rcu(&node->head, free_node_cb); + count++; + } + printf("deleted %lu nodes.\n", count); +} + +void show_usage(int argc, char **argv) +{ + printf("Usage : %s nr_readers nr_writers duration (s) \n", + argv[0]); + printf("OPTIONS:\n"); +#ifdef DEBUG_YIELD + printf(" [-r] [-w] (yield reader and/or writer)\n"); +#endif + printf(" [-d delay] (writer period (us))\n"); + printf(" [-c duration] (reader C.S. duration (in loops))\n"); + printf(" [-v] (verbose output)\n"); + printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); + printf(" [-h size] (initial number of buckets)\n"); + printf(" [-m size] (minimum number of allocated buckets)\n"); + printf(" [-n size] (maximum number of buckets)\n"); +printf(" [not -u nor -s] Add entries (supports redundant keys).\n"); + printf(" [-u] Uniquify add (no redundant keys).\n"); + printf(" [-s] Replace (swap) entries.\n"); + printf(" [-i] Add only (no removal).\n"); + printf(" [-k nr_nodes] Number of nodes to insert initially.\n"); + printf(" [-A] Automatically resize hash table.\n"); + printf(" [-B order|chunk|mmap] Specify the memory backend.\n"); + printf(" [-R offset] Lookup pool offset.\n"); + printf(" [-S offset] Write pool offset.\n"); + printf(" [-T offset] Init pool offset.\n"); + printf(" [-M size] Lookup pool size.\n"); + printf(" [-N size] Write pool size.\n"); + printf(" [-O size] Init pool size.\n"); + printf(" [-V] Validate lookups of init values.\n"); + printf(" (use with filled init pool, same lookup range,\n"); + printf(" with different write range)\n"); + printf(" [-U] Uniqueness test.\n"); + printf(" [-C] Number of hash chains.\n"); + printf("\n"); +} + +int main(int argc, char **argv) +{ + pthread_t *tid_reader, *tid_writer; + pthread_t tid_count; + void *tret; + unsigned long long *count_reader; + struct wr_count *count_writer; + unsigned long long tot_reads = 0, tot_writes = 0, + tot_add = 0, tot_add_exist = 0, tot_remove = 0; + unsigned long count; + long approx_before, approx_after; + int i, a, ret, err, mainret = 0; + struct sigaction act; + unsigned int remain; + unsigned int nr_readers_created = 0, nr_writers_created = 0; + long long nr_leaked; + + if (argc < 4) { + show_usage(argc, argv); + mainret = 1; + goto end; + } + + err = sscanf(argv[1], "%u", &nr_readers); + if (err != 1) { + show_usage(argc, argv); + mainret = 1; + goto end; + } + + err = sscanf(argv[2], "%u", &nr_writers); + if (err != 1) { + show_usage(argc, argv); + mainret = 1; + goto end; + } + + err = sscanf(argv[3], "%lu", &duration); + if (err != 1) { + show_usage(argc, argv); + mainret = 1; + goto end; + } + + for (i = 4; i < argc; i++) { + if (argv[i][0] != '-') + continue; + switch (argv[i][1]) { +#ifdef DEBUG_YIELD + case 'r': + yield_active |= YIELD_READ; + break; + case 'w': + yield_active |= YIELD_WRITE; + break; +#endif + case 'a': + if (argc < i + 2) { + show_usage(argc, argv); + mainret = 1; + goto end; + } + a = atoi(argv[++i]); + cpu_affinities[next_aff++] = a; + use_affinity = 1; + printf_verbose("Adding CPU %d affinity\n", a); + break; + case 'c': + if (argc < i + 2) { + show_usage(argc, argv); + mainret = 1; + goto end; + } + rduration = atol(argv[++i]); + break; + case 'd': + if (argc < i + 2) { + show_usage(argc, argv); + mainret = 1; + goto end; + } + wdelay = atol(argv[++i]); + break; + case 'v': + verbose_mode = 1; + break; + case 'h': + if (argc < i + 2) { + show_usage(argc, argv); + mainret = 1; + goto end; + } + init_hash_size = atol(argv[++i]); + break; + case 'm': + if (argc < i + 2) { + show_usage(argc, argv); + mainret = 1; + goto end; + } + min_hash_alloc_size = atol(argv[++i]); + break; + case 'n': + if (argc < i + 2) { + show_usage(argc, argv); + mainret = 1; + goto end; + } + max_hash_buckets_size = atol(argv[++i]); + break; + case 'u': + if (add_replace) { + printf("Please specify at most one of -s or -u.\n"); + exit(-1); + } + add_unique = 1; + break; + case 's': + if (add_unique) { + printf("Please specify at most one of -s or -u.\n"); + exit(-1); + } + add_replace = 1; + break; + case 'i': + add_only = 1; + break; + case 'k': + init_populate = atol(argv[++i]); + break; + case 'A': + opt_auto_resize = 1; + break; + case 'B': + if (argc < i + 2) { + show_usage(argc, argv); + mainret = 1; + goto end; + } + i++; + if (!strcmp("order", argv[i])) + memory_backend = &cds_lfht_mm_order; + else if (!strcmp("chunk", argv[i])) + memory_backend = &cds_lfht_mm_chunk; + else if (!strcmp("mmap", argv[i])) + memory_backend = &cds_lfht_mm_mmap; + else { + printf("Please specify memory backend with order|chunk|mmap.\n"); + mainret = 1; + goto end; + } + break; + case 'R': + lookup_pool_offset = atol(argv[++i]); + break; + case 'S': + write_pool_offset = atol(argv[++i]); + break; + case 'T': + init_pool_offset = atol(argv[++i]); + break; + case 'M': + lookup_pool_size = atol(argv[++i]); + break; + case 'N': + write_pool_size = atol(argv[++i]); + break; + case 'O': + init_pool_size = atol(argv[++i]); + break; + case 'V': + validate_lookup = 1; + break; + case 'U': + test_choice = TEST_HASH_UNIQUE; + break; + case 'C': + nr_hash_chains = atol(argv[++i]); + break; + } + } + + /* Check if hash size is power of 2 */ + if (init_hash_size && init_hash_size & (init_hash_size - 1)) { + printf("Error: Initial number of buckets (%lu) is not a power of 2.\n", + init_hash_size); + mainret = 1; + goto end; + } + + if (min_hash_alloc_size && min_hash_alloc_size & (min_hash_alloc_size - 1)) { + printf("Error: Minimum number of allocated buckets (%lu) is not a power of 2.\n", + min_hash_alloc_size); + mainret = 1; + goto end; + } + + if (max_hash_buckets_size && max_hash_buckets_size & (max_hash_buckets_size - 1)) { + printf("Error: Maximum number of buckets (%lu) is not a power of 2.\n", + max_hash_buckets_size); + mainret = 1; + goto end; + } + + memset(&act, 0, sizeof(act)); + ret = sigemptyset(&act.sa_mask); + if (ret == -1) { + perror("sigemptyset"); + mainret = 1; + goto end; + } + act.sa_handler = get_sigusr1_cb(); + act.sa_flags = SA_RESTART; + ret = sigaction(SIGUSR1, &act, NULL); + if (ret == -1) { + perror("sigaction"); + mainret = 1; + goto end; + } + + act.sa_handler = get_sigusr2_cb(); + act.sa_flags = SA_RESTART; + ret = sigaction(SIGUSR2, &act, NULL); + if (ret == -1) { + perror("sigaction"); + mainret = 1; + goto end; + } + + printf_verbose("running test for %lu seconds, %u readers, %u writers.\n", + duration, nr_readers, nr_writers); + printf_verbose("Writer delay : %lu loops.\n", wdelay); + printf_verbose("Reader duration : %lu loops.\n", rduration); + printf_verbose("Mode:%s%s.\n", + add_only ? " add only" : " add/remove", + add_unique ? " uniquify" : ( add_replace ? " replace" : " insert")); + printf_verbose("Initial number of buckets: %lu buckets.\n", init_hash_size); + printf_verbose("Minimum number of allocated buckets: %lu buckets.\n", min_hash_alloc_size); + printf_verbose("Maximum number of buckets: %lu buckets.\n", max_hash_buckets_size); + printf_verbose("Init pool size offset %lu size %lu.\n", + init_pool_offset, init_pool_size); + printf_verbose("Lookup pool size offset %lu size %lu.\n", + lookup_pool_offset, lookup_pool_size); + printf_verbose("Update pool size offset %lu size %lu.\n", + write_pool_offset, write_pool_size); + printf_verbose("Number of hash chains: %lu.\n", + nr_hash_chains); + printf_verbose("thread %-6s, tid %lu\n", + "main", urcu_get_thread_id()); + + tid_reader = calloc(nr_readers, sizeof(*tid_reader)); + if (!tid_reader) { + mainret = 1; + goto end; + } + tid_writer = calloc(nr_writers, sizeof(*tid_writer)); + if (!tid_writer) { + mainret = 1; + goto end_free_tid_reader; + } + count_reader = calloc(nr_readers, sizeof(*count_reader)); + if (!count_reader) { + mainret = 1; + goto end_free_tid_writer; + } + count_writer = calloc(nr_writers, sizeof(*count_writer)); + if (!count_writer) { + mainret = 1; + goto end_free_count_reader; + } + + err = create_all_cpu_call_rcu_data(0); + if (err) { + printf("Per-CPU call_rcu() worker threads unavailable. Using default global worker thread.\n"); + } + + if (memory_backend) { + test_ht = _cds_lfht_new(init_hash_size, min_hash_alloc_size, + max_hash_buckets_size, + (opt_auto_resize ? CDS_LFHT_AUTO_RESIZE : 0) | + CDS_LFHT_ACCOUNTING, memory_backend, + &rcu_flavor, NULL); + } else { + test_ht = cds_lfht_new(init_hash_size, min_hash_alloc_size, + max_hash_buckets_size, + (opt_auto_resize ? CDS_LFHT_AUTO_RESIZE : 0) | + CDS_LFHT_ACCOUNTING, NULL); + } + if (!test_ht) { + printf("Error allocating hash table.\n"); + mainret = 1; + goto end_free_call_rcu_data; + } + + /* + * Hash Population needs to be seen as a RCU reader + * thread from the point of view of resize. + */ + rcu_register_thread(); + ret = (get_populate_hash_cb())(); + assert(!ret); + + rcu_thread_offline(); + + next_aff = 0; + + ret = pipe(count_pipe); + if (ret == -1) { + perror("pipe"); + mainret = 1; + goto end_online; + } + + /* spawn counter thread */ + err = pthread_create(&tid_count, NULL, thr_count, + NULL); + if (err != 0) { + errno = err; + mainret = 1; + perror("pthread_create"); + goto end_close_pipe; + } + + for (i = 0; i < nr_readers; i++) { + err = pthread_create(&tid_reader[i], + NULL, get_thr_reader_cb(), + &count_reader[i]); + if (err != 0) { + errno = err; + mainret = 1; + perror("pthread_create"); + goto end_pthread_join; + } + nr_readers_created++; + } + for (i = 0; i < nr_writers; i++) { + err = pthread_create(&tid_writer[i], + NULL, get_thr_writer_cb(), + &count_writer[i]); + if (err != 0) { + errno = err; + mainret = 1; + perror("pthread_create"); + goto end_pthread_join; + } + nr_writers_created++; + } + + cmm_smp_mb(); + + test_go = 1; + + remain = duration; + do { + remain = sleep(remain); + } while (remain > 0); + + test_stop = 1; + +end_pthread_join: + for (i = 0; i < nr_readers_created; i++) { + err = pthread_join(tid_reader[i], &tret); + if (err != 0) { + errno = err; + mainret = 1; + perror("pthread_join"); + } + tot_reads += count_reader[i]; + } + for (i = 0; i < nr_writers_created; i++) { + err = pthread_join(tid_writer[i], &tret); + if (err != 0) { + errno = err; + mainret = 1; + perror("pthread_join"); + } + tot_writes += count_writer[i].update_ops; + tot_add += count_writer[i].add; + tot_add_exist += count_writer[i].add_exist; + tot_remove += count_writer[i].remove; + } + + /* teardown counter thread */ + act.sa_handler = SIG_IGN; + act.sa_flags = SA_RESTART; + ret = sigaction(SIGUSR2, &act, NULL); + if (ret == -1) { + mainret = 1; + perror("sigaction"); + } + { + char msg[1] = { 0x42 }; + ssize_t ret; + + do { + ret = write(count_pipe[1], msg, 1); /* wakeup thread */ + } while (ret == -1L && errno == EINTR); + } + err = pthread_join(tid_count, &tret); + if (err != 0) { + errno = err; + mainret = 1; + perror("pthread_join"); + } + +end_close_pipe: + for (i = 0; i < 2; i++) { + err = close(count_pipe[i]); + if (err) { + mainret = 1; + perror("close pipe"); + } + } + fflush(stdout); +end_online: + rcu_thread_online(); + rcu_read_lock(); + printf("Counting nodes... "); + cds_lfht_count_nodes(test_ht, &approx_before, &count, &approx_after); + printf("done.\n"); + test_delete_all_nodes(test_ht); + rcu_read_unlock(); + rcu_thread_offline(); + if (count) { + printf("Approximation before node accounting: %ld nodes.\n", + approx_before); + printf("Nodes deleted from hash table before destroy: " + "%lu nodes.\n", + count); + printf("Approximation after node accounting: %ld nodes.\n", + approx_after); + } + + ret = cds_lfht_destroy(test_ht, NULL); + if (ret) { + printf_verbose("final delete aborted\n"); + mainret = 1; + } else { + printf_verbose("final delete success\n"); + } + printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads, + tot_writes); + nr_leaked = (long long) tot_add + init_populate - tot_remove - count; + printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu " + "nr_writers %3u " + "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu " + "nr_add %12llu nr_add_fail %12llu nr_remove %12llu nr_leaked %12lld\n", + argv[0], duration, nr_readers, rduration, + nr_writers, wdelay, tot_reads, tot_writes, + tot_reads + tot_writes, tot_add, tot_add_exist, tot_remove, + nr_leaked); + if (nr_leaked != 0) { + mainret = 1; + printf("WARNING: %lld nodes were leaked!\n", nr_leaked); + } + + rcu_unregister_thread(); +end_free_call_rcu_data: + free_all_cpu_call_rcu_data(); + free(count_writer); +end_free_count_reader: + free(count_reader); +end_free_tid_writer: + free(tid_writer); +end_free_tid_reader: + free(tid_reader); +end: + if (!mainret) + exit(EXIT_SUCCESS); + else + exit(EXIT_FAILURE); +} diff --git a/tests/benchmark/test_urcu_hash.h b/tests/benchmark/test_urcu_hash.h new file mode 100644 index 0000000..c0727b7 --- /dev/null +++ b/tests/benchmark/test_urcu_hash.h @@ -0,0 +1,390 @@ +#ifndef _TEST_URCU_HASH_H +#define _TEST_URCU_HASH_H + +/* + * test_urcu_hash.h + * + * Userspace RCU library - test program + * + * Copyright 2009-2012 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "cpuset.h" +#include "thread-id.h" + +#define DEFAULT_HASH_SIZE 32 +#define DEFAULT_MIN_ALLOC_SIZE 1 +#define DEFAULT_RAND_POOL 1000000 + +/* + * Note: the hash seed should be a random value for hash tables + * targeting production environments to provide protection against + * denial of service attacks. We keep it a static value within this test + * program to compare identical benchmark runs. + */ +#define TEST_HASH_SEED 0x42UL + +/* hardcoded number of CPUs */ +#define NR_CPUS 16384 + +#ifdef POISON_FREE +#define poison_free(ptr) \ + do { \ + memset(ptr, 0x42, sizeof(*(ptr))); \ + free(ptr); \ + } while (0) +#else +#define poison_free(ptr) free(ptr) +#endif + +#ifndef DYNAMIC_LINK_TEST +#define _LGPL_SOURCE +#else +#define debug_yield_read() +#endif +#include +#include +#include + +struct wr_count { + unsigned long update_ops; + unsigned long add; + unsigned long add_exist; + unsigned long remove; +}; + +extern DECLARE_URCU_TLS(unsigned int, rand_lookup); +extern DECLARE_URCU_TLS(unsigned long, nr_add); +extern DECLARE_URCU_TLS(unsigned long, nr_addexist); +extern DECLARE_URCU_TLS(unsigned long, nr_del); +extern DECLARE_URCU_TLS(unsigned long, nr_delnoent); +extern DECLARE_URCU_TLS(unsigned long, lookup_fail); +extern DECLARE_URCU_TLS(unsigned long, lookup_ok); + +extern struct cds_lfht *test_ht; + +struct test_data { + int a; + int b; +}; + +struct lfht_test_node { + struct cds_lfht_node node; + void *key; + unsigned int key_len; + /* cache-cold for iteration */ + struct rcu_head head; +}; + +static inline struct lfht_test_node * +to_test_node(struct cds_lfht_node *node) +{ + return caa_container_of(node, struct lfht_test_node, node); +} + +static inline +void lfht_test_node_init(struct lfht_test_node *node, void *key, + size_t key_len) +{ + cds_lfht_node_init(&node->node); + node->key = key; + node->key_len = key_len; +} + +static inline struct lfht_test_node * +cds_lfht_iter_get_test_node(struct cds_lfht_iter *iter) +{ + return to_test_node(cds_lfht_iter_get_node(iter)); +} + +extern volatile int test_go, test_stop; + +extern unsigned long wdelay; + +extern unsigned long duration; + +/* read-side C.S. duration, in loops */ +extern unsigned long rduration; + +extern unsigned long init_hash_size; +extern unsigned long min_hash_alloc_size; +extern unsigned long max_hash_buckets_size; +extern unsigned long init_populate; +extern int opt_auto_resize; +extern int add_only, add_unique, add_replace; +extern const struct cds_lfht_mm_type *memory_backend; + +extern unsigned long init_pool_offset, lookup_pool_offset, write_pool_offset; +extern unsigned long init_pool_size, + lookup_pool_size, + write_pool_size; +extern int validate_lookup; + +extern unsigned long nr_hash_chains; + +extern int count_pipe[2]; + +static inline void loop_sleep(unsigned long loops) +{ + while (loops-- != 0) + caa_cpu_relax(); +} + +extern int verbose_mode; + +#define printf_verbose(fmt, args...) \ + do { \ + if (verbose_mode) \ + printf(fmt, ## args); \ + } while (0) + +extern unsigned int cpu_affinities[NR_CPUS]; +extern unsigned int next_aff; +extern int use_affinity; + +extern pthread_mutex_t affinity_mutex; + +void set_affinity(void); + +/* + * returns 0 if test should end. + */ +static inline int test_duration_write(void) +{ + return !test_stop; +} + +static inline int test_duration_read(void) +{ + return !test_stop; +} + +extern DECLARE_URCU_TLS(unsigned long long, nr_writes); +extern DECLARE_URCU_TLS(unsigned long long, nr_reads); + +extern unsigned int nr_readers; +extern unsigned int nr_writers; + +void rcu_copy_mutex_lock(void); +void rcu_copy_mutex_unlock(void); + +/* + * Hash function + * Source: http://burtleburtle.net/bob/c/lookup3.c + * Originally Public Domain + */ + +#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k)))) + +#define mix(a, b, c) \ +do { \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c, 16); c += b; \ + b -= a; b ^= rot(a, 19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} while (0) + +#define final(a, b, c) \ +{ \ + c ^= b; c -= rot(b, 14); \ + a ^= c; a -= rot(c, 11); \ + b ^= a; b -= rot(a, 25); \ + c ^= b; c -= rot(b, 16); \ + a ^= c; a -= rot(c, 4);\ + b ^= a; b -= rot(a, 14); \ + c ^= b; c -= rot(b, 24); \ +} + +static inline __attribute__((unused)) +uint32_t hash_u32( + const uint32_t *k, /* the key, an array of uint32_t values */ + size_t length, /* the length of the key, in uint32_ts */ + uint32_t initval) /* the previous hash, or an arbitrary value */ +{ + uint32_t a, b, c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + (((uint32_t) length) << 2) + initval; + + /*----------------------------------------- handle most of the key */ + while (length > 3) { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a, b, c); + length -= 3; + k += 3; + } + + /*----------------------------------- handle the last 3 uint32_t's */ + switch (length) { /* all the case statements fall through */ + case 3: c += k[2]; + case 2: b += k[1]; + case 1: a += k[0]; + final(a, b, c); + case 0: /* case 0: nothing left to add */ + break; + } + /*---------------------------------------------- report the result */ + return c; +} + +static inline +void hashword2( + const uint32_t *k, /* the key, an array of uint32_t values */ + size_t length, /* the length of the key, in uint32_ts */ + uint32_t *pc, /* IN: seed OUT: primary hash value */ + uint32_t *pb) /* IN: more seed OUT: secondary hash value */ +{ + uint32_t a, b, c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t) (length << 2)) + *pc; + c += *pb; + + /*----------------------------------------- handle most of the key */ + while (length > 3) { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a, b, c); + length -= 3; + k += 3; + } + + /*----------------------------------- handle the last 3 uint32_t's */ + switch (length) { /* all the case statements fall through */ + case 3: c += k[2]; + case 2: b += k[1]; + case 1: a += k[0]; + final(a, b, c); + case 0: /* case 0: nothing left to add */ + break; + } + /*---------------------------------------------- report the result */ + *pc = c; + *pb = b; +} + +#if (CAA_BITS_PER_LONG == 32) +static inline +unsigned long test_hash_mix(const void *_key, size_t length, unsigned long seed) +{ + unsigned int key = (unsigned int) _key; + + assert(length == sizeof(unsigned int)); + return hash_u32(&key, 1, seed); +} +#else +static inline +unsigned long test_hash_mix(const void *_key, size_t length, unsigned long seed) +{ + union { + uint64_t v64; + uint32_t v32[2]; + } v; + union { + uint64_t v64; + uint32_t v32[2]; + } key; + + assert(length == sizeof(unsigned long)); + v.v64 = (uint64_t) seed; + key.v64 = (uint64_t) _key; + hashword2(key.v32, 2, &v.v32[0], &v.v32[1]); + return v.v64; +} +#endif + +/* + * Hash function with nr_hash_chains != 0 for testing purpose only! + * Creates very long hash chains, deteriorating the hash table into a + * few linked lists, depending on the nr_hash_chains value. The purpose + * of this test is to check how the hash table behaves with hash chains + * containing different values, which is a rare case in a normal hash + * table. + */ +static inline +unsigned long test_hash(const void *_key, size_t length, + unsigned long seed) +{ + if (nr_hash_chains == 0) { + return test_hash_mix(_key, length, seed); + } else { + unsigned long v; + + assert(length == sizeof(unsigned long)); + v = (unsigned long) _key; + return v % nr_hash_chains; + } +} + +unsigned long test_compare(const void *key1, size_t key1_len, + const void *key2, size_t key2_len); + +static inline +int test_match(struct cds_lfht_node *node, const void *key) +{ + struct lfht_test_node *test_node = to_test_node(node); + + return !test_compare(test_node->key, test_node->key_len, + key, sizeof(unsigned long)); +} + +static inline +void cds_lfht_test_lookup(struct cds_lfht *ht, void *key, size_t key_len, + struct cds_lfht_iter *iter) +{ + assert(key_len == sizeof(unsigned long)); + + cds_lfht_lookup(ht, test_hash(key, key_len, TEST_HASH_SEED), + test_match, key, iter); +} + +void free_node_cb(struct rcu_head *head); + +/* rw test */ +void test_hash_rw_sigusr1_handler(int signo); +void test_hash_rw_sigusr2_handler(int signo); +void *test_hash_rw_thr_reader(void *_count); +void *test_hash_rw_thr_writer(void *_count); +int test_hash_rw_populate_hash(void); + +/* unique test */ +void test_hash_unique_sigusr1_handler(int signo); +void test_hash_unique_sigusr2_handler(int signo); +void *test_hash_unique_thr_reader(void *_count); +void *test_hash_unique_thr_writer(void *_count); +int test_hash_unique_populate_hash(void); + +#endif /* _TEST_URCU_HASH_H */ diff --git a/tests/benchmark/test_urcu_hash_rw.c b/tests/benchmark/test_urcu_hash_rw.c new file mode 100644 index 0000000..8802b9c --- /dev/null +++ b/tests/benchmark/test_urcu_hash_rw.c @@ -0,0 +1,284 @@ +/* + * test_urcu_hash_rw.c + * + * Userspace RCU library - test program + * + * Copyright 2009-2012 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include "test_urcu_hash.h" + +enum urcu_hash_addremove { + AR_RANDOM = 0, + AR_ADD = 1, + AR_REMOVE = -1, +}; /* 1: add, -1 remove, 0: random */ + +static enum urcu_hash_addremove addremove; /* 1: add, -1 remove, 0: random */ + +void test_hash_rw_sigusr1_handler(int signo) +{ + switch (addremove) { + case AR_ADD: + printf("Add/Remove: random.\n"); + addremove = AR_RANDOM; + break; + case AR_RANDOM: + printf("Add/Remove: remove only.\n"); + addremove = AR_REMOVE; + break; + case AR_REMOVE: + printf("Add/Remove: add only.\n"); + addremove = AR_ADD; + break; + } +} + +void test_hash_rw_sigusr2_handler(int signo) +{ + char msg[1] = { 0x42 }; + ssize_t ret; + + do { + ret = write(count_pipe[1], msg, 1); /* wakeup thread */ + } while (ret == -1L && errno == EINTR); +} + +void *test_hash_rw_thr_reader(void *_count) +{ + unsigned long long *count = _count; + struct lfht_test_node *node; + struct cds_lfht_iter iter; + + printf_verbose("thread_begin %s, tid %lu\n", + "reader", urcu_get_thread_id()); + + URCU_TLS(rand_lookup) = urcu_get_thread_id() ^ time(NULL); + + set_affinity(); + + rcu_register_thread(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + rcu_read_lock(); + cds_lfht_test_lookup(test_ht, + (void *)(((unsigned long) rand_r(&URCU_TLS(rand_lookup)) % lookup_pool_size) + lookup_pool_offset), + sizeof(void *), &iter); + node = cds_lfht_iter_get_test_node(&iter); + if (node == NULL) { + if (validate_lookup) { + printf("[ERROR] Lookup cannot find initial node.\n"); + exit(-1); + } + URCU_TLS(lookup_fail)++; + } else { + URCU_TLS(lookup_ok)++; + } + rcu_debug_yield_read(); + if (caa_unlikely(rduration)) + loop_sleep(rduration); + rcu_read_unlock(); + URCU_TLS(nr_reads)++; + if (caa_unlikely(!test_duration_read())) + break; + if (caa_unlikely((URCU_TLS(nr_reads) & ((1 << 10) - 1)) == 0)) + rcu_quiescent_state(); + } + + rcu_unregister_thread(); + + *count = URCU_TLS(nr_reads); + printf_verbose("thread_end %s, tid %lu\n", + "reader", urcu_get_thread_id()); + printf_verbose("read tid : %lx, lookupfail %lu, lookupok %lu\n", + urcu_get_thread_id(), + URCU_TLS(lookup_fail), + URCU_TLS(lookup_ok)); + return ((void*)1); + +} + +void *test_hash_rw_thr_writer(void *_count) +{ + struct lfht_test_node *node; + struct cds_lfht_node *ret_node; + struct cds_lfht_iter iter; + struct wr_count *count = _count; + int ret; + + printf_verbose("thread_begin %s, tid %lu\n", + "writer", urcu_get_thread_id()); + + URCU_TLS(rand_lookup) = urcu_get_thread_id() ^ time(NULL); + + set_affinity(); + + rcu_register_thread(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + if ((addremove == AR_ADD || add_only) + || (addremove == AR_RANDOM && rand_r(&URCU_TLS(rand_lookup)) & 1)) { + node = malloc(sizeof(struct lfht_test_node)); + lfht_test_node_init(node, + (void *)(((unsigned long) rand_r(&URCU_TLS(rand_lookup)) % write_pool_size) + write_pool_offset), + sizeof(void *)); + rcu_read_lock(); + if (add_unique) { + ret_node = cds_lfht_add_unique(test_ht, + test_hash(node->key, node->key_len, TEST_HASH_SEED), + test_match, node->key, &node->node); + } else { + if (add_replace) + ret_node = cds_lfht_add_replace(test_ht, + test_hash(node->key, node->key_len, TEST_HASH_SEED), + test_match, node->key, &node->node); + else + cds_lfht_add(test_ht, + test_hash(node->key, node->key_len, TEST_HASH_SEED), + &node->node); + } + rcu_read_unlock(); + if (add_unique && ret_node != &node->node) { + free(node); + URCU_TLS(nr_addexist)++; + } else { + if (add_replace && ret_node) { + call_rcu(&to_test_node(ret_node)->head, + free_node_cb); + URCU_TLS(nr_addexist)++; + } else { + URCU_TLS(nr_add)++; + } + } + } else { + /* May delete */ + rcu_read_lock(); + cds_lfht_test_lookup(test_ht, + (void *)(((unsigned long) rand_r(&URCU_TLS(rand_lookup)) % write_pool_size) + write_pool_offset), + sizeof(void *), &iter); + ret = cds_lfht_del(test_ht, cds_lfht_iter_get_node(&iter)); + rcu_read_unlock(); + if (ret == 0) { + node = cds_lfht_iter_get_test_node(&iter); + call_rcu(&node->head, free_node_cb); + URCU_TLS(nr_del)++; + } else + URCU_TLS(nr_delnoent)++; + } +#if 0 + //if (URCU_TLS(nr_writes) % 100000 == 0) { + if (URCU_TLS(nr_writes) % 1000 == 0) { + rcu_read_lock(); + if (rand_r(&URCU_TLS(rand_lookup)) & 1) { + ht_resize(test_ht, 1); + } else { + ht_resize(test_ht, -1); + } + rcu_read_unlock(); + } +#endif //0 + URCU_TLS(nr_writes)++; + if (caa_unlikely(!test_duration_write())) + break; + if (caa_unlikely(wdelay)) + loop_sleep(wdelay); + if (caa_unlikely((URCU_TLS(nr_writes) & ((1 << 10) - 1)) == 0)) + rcu_quiescent_state(); + } + + rcu_unregister_thread(); + + printf_verbose("thread_end %s, tid %lu\n", + "writer", urcu_get_thread_id()); + printf_verbose("info tid %lu: nr_add %lu, nr_addexist %lu, nr_del %lu, " + "nr_delnoent %lu\n", urcu_get_thread_id(), + URCU_TLS(nr_add), + URCU_TLS(nr_addexist), + URCU_TLS(nr_del), + URCU_TLS(nr_delnoent)); + count->update_ops = URCU_TLS(nr_writes); + count->add = URCU_TLS(nr_add); + count->add_exist = URCU_TLS(nr_addexist); + count->remove = URCU_TLS(nr_del); + return ((void*)2); +} + +int test_hash_rw_populate_hash(void) +{ + struct lfht_test_node *node; + struct cds_lfht_node *ret_node; + + if (!init_populate) + return 0; + + printf("Starting rw test\n"); + + URCU_TLS(rand_lookup) = urcu_get_thread_id() ^ time(NULL); + + if ((add_unique || add_replace) && init_populate * 10 > init_pool_size) { + printf("WARNING: required to populate %lu nodes (-k), but random " +"pool is quite small (%lu values) and we are in add_unique (-u) or add_replace (-s) mode. Try with a " +"larger random pool (-p option). This may take a while...\n", init_populate, init_pool_size); + } + + while (URCU_TLS(nr_add) < init_populate) { + node = malloc(sizeof(struct lfht_test_node)); + lfht_test_node_init(node, + (void *)(((unsigned long) rand_r(&URCU_TLS(rand_lookup)) % init_pool_size) + init_pool_offset), + sizeof(void *)); + rcu_read_lock(); + if (add_unique) { + ret_node = cds_lfht_add_unique(test_ht, + test_hash(node->key, node->key_len, TEST_HASH_SEED), + test_match, node->key, &node->node); + } else { + if (add_replace) + ret_node = cds_lfht_add_replace(test_ht, + test_hash(node->key, node->key_len, TEST_HASH_SEED), + test_match, node->key, &node->node); + else + cds_lfht_add(test_ht, + test_hash(node->key, node->key_len, TEST_HASH_SEED), + &node->node); + } + rcu_read_unlock(); + if (add_unique && ret_node != &node->node) { + free(node); + URCU_TLS(nr_addexist)++; + } else { + if (add_replace && ret_node) { + call_rcu(&to_test_node(ret_node)->head, free_node_cb); + URCU_TLS(nr_addexist)++; + } else { + URCU_TLS(nr_add)++; + } + } + URCU_TLS(nr_writes)++; + } + return 0; +} diff --git a/tests/benchmark/test_urcu_hash_unique.c b/tests/benchmark/test_urcu_hash_unique.c new file mode 100644 index 0000000..adbbde0 --- /dev/null +++ b/tests/benchmark/test_urcu_hash_unique.c @@ -0,0 +1,279 @@ +/* + * test_urcu_hash_unique.c + * + * Userspace RCU library - test program + * + * Copyright 2009-2012 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include "test_urcu_hash.h" + +enum urcu_hash_addremove { + AR_RANDOM = 0, + AR_ADD = 1, + AR_REMOVE = -1, +}; /* 1: add, -1 remove, 0: random */ + +static enum urcu_hash_addremove addremove; /* 1: add, -1 remove, 0: random */ + +void test_hash_unique_sigusr1_handler(int signo) +{ + switch (addremove) { + case AR_ADD: + printf("Add/Remove: random.\n"); + addremove = AR_RANDOM; + break; + case AR_RANDOM: + printf("Add/Remove: remove only.\n"); + addremove = AR_REMOVE; + break; + case AR_REMOVE: + printf("Add/Remove: add only.\n"); + addremove = AR_ADD; + break; + } +} + +void test_hash_unique_sigusr2_handler(int signo) +{ + char msg[1] = { 0x42 }; + ssize_t ret; + + do { + ret = write(count_pipe[1], msg, 1); /* wakeup thread */ + } while (ret == -1L && errno == EINTR); +} + +void *test_hash_unique_thr_reader(void *_count) +{ + unsigned long long *count = _count; + + printf_verbose("thread_begin %s, tid %lu\n", + "reader", urcu_get_thread_id()); + + URCU_TLS(rand_lookup) = urcu_get_thread_id() ^ time(NULL); + + set_affinity(); + + rcu_register_thread(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + struct lfht_test_node *node; + struct cds_lfht_iter iter; + /* + * iterate on whole table, ensuring that no duplicate is + * found. + */ + rcu_read_lock(); + cds_lfht_for_each_entry(test_ht, &iter, node, node) { + struct cds_lfht_iter dup_iter; + + dup_iter = iter; + cds_lfht_next_duplicate(test_ht, test_match, + node->key, &dup_iter); + if (dup_iter.node != NULL) { + printf("[ERROR] Duplicate key %p found\n", node->key); + } + } + rcu_read_unlock(); + + rcu_debug_yield_read(); + if (caa_unlikely(rduration)) + loop_sleep(rduration); + URCU_TLS(nr_reads)++; + if (caa_unlikely(!test_duration_read())) + break; + if (caa_unlikely((URCU_TLS(nr_reads) & ((1 << 10) - 1)) == 0)) + rcu_quiescent_state(); + } + + rcu_unregister_thread(); + + *count = URCU_TLS(nr_reads); + printf_verbose("thread_end %s, tid %lu\n", + "reader", urcu_get_thread_id()); + printf_verbose("read tid : %lu, lookupfail %lu, lookupok %lu\n", + urcu_get_thread_id(), URCU_TLS(lookup_fail), + URCU_TLS(lookup_ok)); + return ((void*)1); + +} + +void *test_hash_unique_thr_writer(void *_count) +{ + struct lfht_test_node *node; + struct cds_lfht_node *ret_node; + struct cds_lfht_iter iter; + struct wr_count *count = _count; + int ret; + int loc_add_unique; + + printf_verbose("thread_begin %s, tid %lu\n", + "writer", urcu_get_thread_id()); + + URCU_TLS(rand_lookup) = urcu_get_thread_id() ^ time(NULL); + + set_affinity(); + + rcu_register_thread(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + /* + * add unique/add replace with new node key from range. + */ + if (1 || (addremove == AR_ADD || add_only) + || (addremove == AR_RANDOM && rand_r(&URCU_TLS(rand_lookup)) & 1)) { + node = malloc(sizeof(struct lfht_test_node)); + lfht_test_node_init(node, + (void *)(((unsigned long) rand_r(&URCU_TLS(rand_lookup)) % write_pool_size) + write_pool_offset), + sizeof(void *)); + rcu_read_lock(); + loc_add_unique = rand_r(&URCU_TLS(rand_lookup)) & 1; + if (loc_add_unique) { + ret_node = cds_lfht_add_unique(test_ht, + test_hash(node->key, node->key_len, TEST_HASH_SEED), + test_match, node->key, &node->node); + } else { + ret_node = cds_lfht_add_replace(test_ht, + test_hash(node->key, node->key_len, TEST_HASH_SEED), + test_match, node->key, &node->node); +#if 0 //generate an error on purpose + cds_lfht_add(test_ht, + test_hash(node->key, node->key_len, TEST_HASH_SEED), + &node->node); + ret_node = NULL; +#endif //0 + } + rcu_read_unlock(); + if (loc_add_unique) { + if (ret_node != &node->node) { + free(node); + URCU_TLS(nr_addexist)++; + } else { + URCU_TLS(nr_add)++; + } + } else { + if (ret_node) { + call_rcu(&to_test_node(ret_node)->head, + free_node_cb); + URCU_TLS(nr_addexist)++; + } else { + URCU_TLS(nr_add)++; + } + } + } else { + /* May delete */ + rcu_read_lock(); + cds_lfht_test_lookup(test_ht, + (void *)(((unsigned long) rand_r(&URCU_TLS(rand_lookup)) % write_pool_size) + write_pool_offset), + sizeof(void *), &iter); + ret = cds_lfht_del(test_ht, cds_lfht_iter_get_node(&iter)); + rcu_read_unlock(); + if (ret == 0) { + node = cds_lfht_iter_get_test_node(&iter); + call_rcu(&node->head, free_node_cb); + URCU_TLS(nr_del)++; + } else + URCU_TLS(nr_delnoent)++; + } +#if 0 + //if (URCU_TLS(nr_writes) % 100000 == 0) { + if (URCU_TLS(nr_writes) % 1000 == 0) { + rcu_read_lock(); + if (rand_r(&URCU_TLS(rand_lookup)) & 1) { + ht_resize(test_ht, 1); + } else { + ht_resize(test_ht, -1); + } + rcu_read_unlock(); + } +#endif //0 + URCU_TLS(nr_writes)++; + if (caa_unlikely(!test_duration_write())) + break; + if (caa_unlikely(wdelay)) + loop_sleep(wdelay); + if (caa_unlikely((URCU_TLS(nr_writes) & ((1 << 10) - 1)) == 0)) + rcu_quiescent_state(); + } + + rcu_unregister_thread(); + + printf_verbose("thread_end %s, tid %lu\n", + "writer", urcu_get_thread_id()); + printf_verbose("info tid %lu: nr_add %lu, nr_addexist %lu, nr_del %lu, " + "nr_delnoent %lu\n", urcu_get_thread_id(), + URCU_TLS(nr_add), + URCU_TLS(nr_addexist), + URCU_TLS(nr_del), + URCU_TLS(nr_delnoent)); + count->update_ops = URCU_TLS(nr_writes); + count->add = URCU_TLS(nr_add); + count->add_exist = URCU_TLS(nr_addexist); + count->remove = URCU_TLS(nr_del); + return ((void*)2); +} + +int test_hash_unique_populate_hash(void) +{ + struct lfht_test_node *node; + struct cds_lfht_node *ret_node; + + printf("Starting uniqueness test.\n"); + + URCU_TLS(rand_lookup) = urcu_get_thread_id() ^ time(NULL); + + if (!init_populate) + return 0; + + if (init_populate * 10 > init_pool_size) { + printf("WARNING: required to populate %lu nodes (-k), but random " +"pool is quite small (%lu values) and we are in add_unique (-u) or add_replace (-s) mode. Try with a " +"larger random pool (-p option). This may take a while...\n", init_populate, init_pool_size); + } + + while (URCU_TLS(nr_add) < init_populate) { + node = malloc(sizeof(struct lfht_test_node)); + lfht_test_node_init(node, + (void *)(((unsigned long) rand_r(&URCU_TLS(rand_lookup)) % init_pool_size) + init_pool_offset), + sizeof(void *)); + rcu_read_lock(); + ret_node = cds_lfht_add_replace(test_ht, + test_hash(node->key, node->key_len, TEST_HASH_SEED), + test_match, node->key, &node->node); + rcu_read_unlock(); + if (ret_node) { + call_rcu(&to_test_node(ret_node)->head, free_node_cb); + URCU_TLS(nr_addexist)++; + } else { + URCU_TLS(nr_add)++; + } + URCU_TLS(nr_writes)++; + } + return 0; +} diff --git a/tests/benchmark/test_urcu_lfq.c b/tests/benchmark/test_urcu_lfq.c new file mode 100644 index 0000000..33d1e73 --- /dev/null +++ b/tests/benchmark/test_urcu_lfq.c @@ -0,0 +1,436 @@ +/* + * test_urcu_lfq.c + * + * Userspace RCU library - example RCU-based lock-free queue + * + * Copyright February 2010 - Mathieu Desnoyers + * Copyright February 2010 - Paolo Bonzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "cpuset.h" +#include "thread-id.h" + +/* hardcoded number of CPUs */ +#define NR_CPUS 16384 + +#ifndef DYNAMIC_LINK_TEST +#define _LGPL_SOURCE +#endif +#include +#include + +static volatile int test_go, test_stop; + +static unsigned long rduration; + +static unsigned long duration; + +/* read-side C.S. duration, in loops */ +static unsigned long wdelay; + +static inline void loop_sleep(unsigned long loops) +{ + while (loops-- != 0) + caa_cpu_relax(); +} + +static int verbose_mode; + +#define printf_verbose(fmt, args...) \ + do { \ + if (verbose_mode) \ + printf(fmt, args); \ + } while (0) + +static unsigned int cpu_affinities[NR_CPUS]; +static unsigned int next_aff = 0; +static int use_affinity = 0; + +pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void set_affinity(void) +{ +#if HAVE_SCHED_SETAFFINITY + cpu_set_t mask; + int cpu, ret; +#endif /* HAVE_SCHED_SETAFFINITY */ + + if (!use_affinity) + return; + +#if HAVE_SCHED_SETAFFINITY + ret = pthread_mutex_lock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } + cpu = cpu_affinities[next_aff++]; + ret = pthread_mutex_unlock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } + + CPU_ZERO(&mask); + CPU_SET(cpu, &mask); +#if SCHED_SETAFFINITY_ARGS == 2 + sched_setaffinity(0, &mask); +#else + sched_setaffinity(0, sizeof(mask), &mask); +#endif +#endif /* HAVE_SCHED_SETAFFINITY */ +} + +/* + * returns 0 if test should end. + */ +static int test_duration_dequeue(void) +{ + return !test_stop; +} + +static int test_duration_enqueue(void) +{ + return !test_stop; +} + +static DEFINE_URCU_TLS(unsigned long long, nr_dequeues); +static DEFINE_URCU_TLS(unsigned long long, nr_enqueues); + +static DEFINE_URCU_TLS(unsigned long long, nr_successful_dequeues); +static DEFINE_URCU_TLS(unsigned long long, nr_successful_enqueues); + +static unsigned int nr_enqueuers; +static unsigned int nr_dequeuers; + +struct test { + struct cds_lfq_node_rcu list; + struct rcu_head rcu; +}; + +static struct cds_lfq_queue_rcu q; + +void *thr_enqueuer(void *_count) +{ + unsigned long long *count = _count; + + printf_verbose("thread_begin %s, tid %lu\n", + "enqueuer", urcu_get_thread_id()); + + set_affinity(); + + rcu_register_thread(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + struct test *node = malloc(sizeof(*node)); + if (!node) + goto fail; + cds_lfq_node_init_rcu(&node->list); + rcu_read_lock(); + cds_lfq_enqueue_rcu(&q, &node->list); + rcu_read_unlock(); + URCU_TLS(nr_successful_enqueues)++; + + if (caa_unlikely(wdelay)) + loop_sleep(wdelay); +fail: + URCU_TLS(nr_enqueues)++; + if (caa_unlikely(!test_duration_enqueue())) + break; + } + + rcu_unregister_thread(); + + count[0] = URCU_TLS(nr_enqueues); + count[1] = URCU_TLS(nr_successful_enqueues); + printf_verbose("enqueuer thread_end, tid %lu, " + "enqueues %llu successful_enqueues %llu\n", + urcu_get_thread_id(), + URCU_TLS(nr_enqueues), + URCU_TLS(nr_successful_enqueues)); + return ((void*)1); + +} + +static +void free_node_cb(struct rcu_head *head) +{ + struct test *node = + caa_container_of(head, struct test, rcu); + free(node); +} + +void *thr_dequeuer(void *_count) +{ + unsigned long long *count = _count; + + printf_verbose("thread_begin %s, tid %lu\n", + "dequeuer", urcu_get_thread_id()); + + set_affinity(); + + rcu_register_thread(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + struct cds_lfq_node_rcu *qnode; + + rcu_read_lock(); + qnode = cds_lfq_dequeue_rcu(&q); + rcu_read_unlock(); + + if (qnode) { + struct test *node; + + node = caa_container_of(qnode, struct test, list); + call_rcu(&node->rcu, free_node_cb); + URCU_TLS(nr_successful_dequeues)++; + } + + URCU_TLS(nr_dequeues)++; + if (caa_unlikely(!test_duration_dequeue())) + break; + if (caa_unlikely(rduration)) + loop_sleep(rduration); + } + + rcu_unregister_thread(); + printf_verbose("dequeuer thread_end, tid %lu, " + "dequeues %llu, successful_dequeues %llu\n", + urcu_get_thread_id(), + URCU_TLS(nr_dequeues), + URCU_TLS(nr_successful_dequeues)); + count[0] = URCU_TLS(nr_dequeues); + count[1] = URCU_TLS(nr_successful_dequeues); + return ((void*)2); +} + +void test_end(struct cds_lfq_queue_rcu *q, unsigned long long *nr_dequeues) +{ + struct cds_lfq_node_rcu *snode; + + do { + snode = cds_lfq_dequeue_rcu(q); + if (snode) { + struct test *node; + + node = caa_container_of(snode, struct test, list); + free(node); /* no more concurrent access */ + (*nr_dequeues)++; + } + } while (snode); +} + +void show_usage(int argc, char **argv) +{ + printf("Usage : %s nr_dequeuers nr_enqueuers duration (s) \n", + argv[0]); + printf("OPTIONS:\n"); + printf(" [-d delay] (enqueuer period (in loops))\n"); + printf(" [-c duration] (dequeuer period (in loops))\n"); + printf(" [-v] (verbose output)\n"); + printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); + printf("\n"); +} + +int main(int argc, char **argv) +{ + int err; + pthread_t *tid_enqueuer, *tid_dequeuer; + void *tret; + unsigned long long *count_enqueuer, *count_dequeuer; + unsigned long long tot_enqueues = 0, tot_dequeues = 0; + unsigned long long tot_successful_enqueues = 0, + tot_successful_dequeues = 0; + unsigned long long end_dequeues = 0; + int i, a; + + if (argc < 4) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[1], "%u", &nr_dequeuers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[2], "%u", &nr_enqueuers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[3], "%lu", &duration); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + for (i = 4; i < argc; i++) { + if (argv[i][0] != '-') + continue; + switch (argv[i][1]) { + case 'a': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + a = atoi(argv[++i]); + cpu_affinities[next_aff++] = a; + use_affinity = 1; + printf_verbose("Adding CPU %d affinity\n", a); + break; + case 'c': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + rduration = atol(argv[++i]); + break; + case 'd': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wdelay = atol(argv[++i]); + break; + case 'v': + verbose_mode = 1; + break; + } + } + + printf_verbose("running test for %lu seconds, %u enqueuers, " + "%u dequeuers.\n", + duration, nr_enqueuers, nr_dequeuers); + printf_verbose("Writer delay : %lu loops.\n", rduration); + printf_verbose("Reader duration : %lu loops.\n", wdelay); + printf_verbose("thread %-6s, tid %lu\n", + "main", urcu_get_thread_id()); + + tid_enqueuer = calloc(nr_enqueuers, sizeof(*tid_enqueuer)); + tid_dequeuer = calloc(nr_dequeuers, sizeof(*tid_dequeuer)); + count_enqueuer = calloc(nr_enqueuers, 2 * sizeof(*count_enqueuer)); + count_dequeuer = calloc(nr_dequeuers, 2 * sizeof(*count_dequeuer)); + cds_lfq_init_rcu(&q, call_rcu); + err = create_all_cpu_call_rcu_data(0); + if (err) { + printf("Per-CPU call_rcu() worker threads unavailable. Using default global worker thread.\n"); + } + + next_aff = 0; + + for (i = 0; i < nr_enqueuers; i++) { + err = pthread_create(&tid_enqueuer[i], NULL, thr_enqueuer, + &count_enqueuer[2 * i]); + if (err != 0) + exit(1); + } + for (i = 0; i < nr_dequeuers; i++) { + err = pthread_create(&tid_dequeuer[i], NULL, thr_dequeuer, + &count_dequeuer[2 * i]); + if (err != 0) + exit(1); + } + + cmm_smp_mb(); + + test_go = 1; + + for (i = 0; i < duration; i++) { + sleep(1); + if (verbose_mode) + write (1, ".", 1); + } + + test_stop = 1; + + for (i = 0; i < nr_enqueuers; i++) { + err = pthread_join(tid_enqueuer[i], &tret); + if (err != 0) + exit(1); + tot_enqueues += count_enqueuer[2 * i]; + tot_successful_enqueues += count_enqueuer[2 * i + 1]; + } + for (i = 0; i < nr_dequeuers; i++) { + err = pthread_join(tid_dequeuer[i], &tret); + if (err != 0) + exit(1); + tot_dequeues += count_dequeuer[2 * i]; + tot_successful_dequeues += count_dequeuer[2 * i + 1]; + } + + test_end(&q, &end_dequeues); + err = cds_lfq_destroy_rcu(&q); + assert(!err); + + printf_verbose("total number of enqueues : %llu, dequeues %llu\n", + tot_enqueues, tot_dequeues); + printf_verbose("total number of successful enqueues : %llu, " + "successful dequeues %llu\n", + tot_successful_enqueues, tot_successful_dequeues); + printf("SUMMARY %-25s testdur %4lu nr_enqueuers %3u wdelay %6lu " + "nr_dequeuers %3u " + "rdur %6lu nr_enqueues %12llu nr_dequeues %12llu " + "successful enqueues %12llu successful dequeues %12llu " + "end_dequeues %llu nr_ops %12llu\n", + argv[0], duration, nr_enqueuers, wdelay, + nr_dequeuers, rduration, tot_enqueues, tot_dequeues, + tot_successful_enqueues, + tot_successful_dequeues, end_dequeues, + tot_enqueues + tot_dequeues); + if (tot_successful_enqueues != tot_successful_dequeues + end_dequeues) + printf("WARNING! Discrepancy between nr succ. enqueues %llu vs " + "succ. dequeues + end dequeues %llu.\n", + tot_successful_enqueues, + tot_successful_dequeues + end_dequeues); + + free_all_cpu_call_rcu_data(); + free(count_enqueuer); + free(count_dequeuer); + free(tid_enqueuer); + free(tid_dequeuer); + return 0; +} diff --git a/tests/benchmark/test_urcu_lfs.c b/tests/benchmark/test_urcu_lfs.c new file mode 100644 index 0000000..33ba069 --- /dev/null +++ b/tests/benchmark/test_urcu_lfs.c @@ -0,0 +1,518 @@ +/* + * test_urcu_lfs.c + * + * Userspace RCU library - example lock-free stack + * + * Copyright 2010-2012 - Mathieu Desnoyers + * Copyright February 2010 - Paolo Bonzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "cpuset.h" +#include "thread-id.h" + +/* hardcoded number of CPUs */ +#define NR_CPUS 16384 + +#ifndef DYNAMIC_LINK_TEST +#define _LGPL_SOURCE +#endif +#include +#include + +/* + * External synchronization used. + */ +enum test_sync { + TEST_SYNC_NONE = 0, + TEST_SYNC_RCU, +}; + +static enum test_sync test_sync; + +static volatile int test_go, test_stop; + +static unsigned long rduration; + +static unsigned long duration; + +/* read-side C.S. duration, in loops */ +static unsigned long wdelay; + +static inline void loop_sleep(unsigned long loops) +{ + while (loops-- != 0) + caa_cpu_relax(); +} + +static int verbose_mode; + +static int test_pop, test_pop_all; + +#define printf_verbose(fmt, args...) \ + do { \ + if (verbose_mode) \ + printf(fmt, ## args); \ + } while (0) + +static unsigned int cpu_affinities[NR_CPUS]; +static unsigned int next_aff = 0; +static int use_affinity = 0; + +pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void set_affinity(void) +{ +#if HAVE_SCHED_SETAFFINITY + cpu_set_t mask; + int cpu, ret; +#endif /* HAVE_SCHED_SETAFFINITY */ + + if (!use_affinity) + return; + +#if HAVE_SCHED_SETAFFINITY + ret = pthread_mutex_lock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } + cpu = cpu_affinities[next_aff++]; + ret = pthread_mutex_unlock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } + + CPU_ZERO(&mask); + CPU_SET(cpu, &mask); +#if SCHED_SETAFFINITY_ARGS == 2 + sched_setaffinity(0, &mask); +#else + sched_setaffinity(0, sizeof(mask), &mask); +#endif +#endif /* HAVE_SCHED_SETAFFINITY */ +} + +/* + * returns 0 if test should end. + */ +static int test_duration_dequeue(void) +{ + return !test_stop; +} + +static int test_duration_enqueue(void) +{ + return !test_stop; +} + +static DEFINE_URCU_TLS(unsigned long long, nr_dequeues); +static DEFINE_URCU_TLS(unsigned long long, nr_enqueues); + +static DEFINE_URCU_TLS(unsigned long long, nr_successful_dequeues); +static DEFINE_URCU_TLS(unsigned long long, nr_successful_enqueues); + +static unsigned int nr_enqueuers; +static unsigned int nr_dequeuers; + +struct test { + struct cds_lfs_node list; + struct rcu_head rcu; +}; + +static struct cds_lfs_stack s; + +static void *thr_enqueuer(void *_count) +{ + unsigned long long *count = _count; + + printf_verbose("thread_begin %s, tid %lu\n", + "enqueuer", urcu_get_thread_id()); + + set_affinity(); + + rcu_register_thread(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + struct test *node = malloc(sizeof(*node)); + if (!node) + goto fail; + cds_lfs_node_init(&node->list); + cds_lfs_push(&s, &node->list); + URCU_TLS(nr_successful_enqueues)++; + + if (caa_unlikely(wdelay)) + loop_sleep(wdelay); +fail: + URCU_TLS(nr_enqueues)++; + if (caa_unlikely(!test_duration_enqueue())) + break; + } + + rcu_unregister_thread(); + + count[0] = URCU_TLS(nr_enqueues); + count[1] = URCU_TLS(nr_successful_enqueues); + printf_verbose("enqueuer thread_end, tid %lu, " + "enqueues %llu successful_enqueues %llu\n", + urcu_get_thread_id(), + URCU_TLS(nr_enqueues), + URCU_TLS(nr_successful_enqueues)); + return ((void*)1); + +} + +static +void free_node_cb(struct rcu_head *head) +{ + struct test *node = + caa_container_of(head, struct test, rcu); + free(node); +} + +static +void do_test_pop(enum test_sync sync) +{ + struct cds_lfs_node *snode; + + if (sync == TEST_SYNC_RCU) + rcu_read_lock(); + snode = __cds_lfs_pop(&s); + if (sync == TEST_SYNC_RCU) + rcu_read_unlock(); + if (snode) { + struct test *node; + + node = caa_container_of(snode, + struct test, list); + if (sync == TEST_SYNC_RCU) + call_rcu(&node->rcu, free_node_cb); + else + free(node); + URCU_TLS(nr_successful_dequeues)++; + } + URCU_TLS(nr_dequeues)++; +} + +static +void do_test_pop_all(enum test_sync sync) +{ + struct cds_lfs_node *snode; + struct cds_lfs_head *head; + struct cds_lfs_node *n; + + head = __cds_lfs_pop_all(&s); + cds_lfs_for_each_safe(head, snode, n) { + struct test *node; + + node = caa_container_of(snode, struct test, list); + if (sync == TEST_SYNC_RCU) + call_rcu(&node->rcu, free_node_cb); + else + free(node); + URCU_TLS(nr_successful_dequeues)++; + URCU_TLS(nr_dequeues)++; + } + +} + +static void *thr_dequeuer(void *_count) +{ + unsigned long long *count = _count; + unsigned int counter = 0; + + printf_verbose("thread_begin %s, tid %lu\n", + "dequeuer", urcu_get_thread_id()); + + set_affinity(); + + rcu_register_thread(); + + while (!test_go) + { + } + cmm_smp_mb(); + + assert(test_pop || test_pop_all); + + for (;;) { + if (test_pop && test_pop_all) { + /* both pop and pop all */ + if (counter & 1) + do_test_pop(test_sync); + else + do_test_pop_all(test_sync); + counter++; + } else { + if (test_pop) + do_test_pop(test_sync); + else + do_test_pop_all(test_sync); + } + + if (caa_unlikely(!test_duration_dequeue())) + break; + if (caa_unlikely(rduration)) + loop_sleep(rduration); + } + + rcu_unregister_thread(); + + printf_verbose("dequeuer thread_end, tid %lu, " + "dequeues %llu, successful_dequeues %llu\n", + urcu_get_thread_id(), + URCU_TLS(nr_dequeues), + URCU_TLS(nr_successful_dequeues)); + count[0] = URCU_TLS(nr_dequeues); + count[1] = URCU_TLS(nr_successful_dequeues); + return ((void*)2); +} + +static void test_end(struct cds_lfs_stack *s, unsigned long long *nr_dequeues) +{ + struct cds_lfs_node *snode; + + do { + snode = __cds_lfs_pop(s); + if (snode) { + struct test *node; + + node = caa_container_of(snode, struct test, list); + free(node); + (*nr_dequeues)++; + } + } while (snode); +} + +static void show_usage(int argc, char **argv) +{ + printf("Usage : %s nr_dequeuers nr_enqueuers duration (s) \n", + argv[0]); + printf("OPTIONS:\n"); + printf(" [-d delay] (enqueuer period (in loops))\n"); + printf(" [-c duration] (dequeuer period (in loops))\n"); + printf(" [-v] (verbose output)\n"); + printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); + printf(" [-p] (test pop)\n"); + printf(" [-P] (test pop_all, enabled by default)\n"); + printf(" [-R] (use RCU external synchronization)\n"); + printf(" Note: default: no external synchronization used.\n"); + printf("\n"); +} + +int main(int argc, char **argv) +{ + int err; + pthread_t *tid_enqueuer, *tid_dequeuer; + void *tret; + unsigned long long *count_enqueuer, *count_dequeuer; + unsigned long long tot_enqueues = 0, tot_dequeues = 0; + unsigned long long tot_successful_enqueues = 0, + tot_successful_dequeues = 0; + unsigned long long end_dequeues = 0; + int i, a; + + if (argc < 4) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[1], "%u", &nr_dequeuers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[2], "%u", &nr_enqueuers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[3], "%lu", &duration); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + for (i = 4; i < argc; i++) { + if (argv[i][0] != '-') + continue; + switch (argv[i][1]) { + case 'a': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + a = atoi(argv[++i]); + cpu_affinities[next_aff++] = a; + use_affinity = 1; + printf_verbose("Adding CPU %d affinity\n", a); + break; + case 'c': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + rduration = atol(argv[++i]); + break; + case 'd': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wdelay = atol(argv[++i]); + break; + case 'v': + verbose_mode = 1; + break; + case 'p': + test_pop = 1; + break; + case 'P': + test_pop_all = 1; + break; + case 'R': + test_sync = TEST_SYNC_RCU; + break; + } + } + + /* activate pop_all test by default */ + if (!test_pop && !test_pop_all) + test_pop_all = 1; + + printf_verbose("running test for %lu seconds, %u enqueuers, " + "%u dequeuers.\n", + duration, nr_enqueuers, nr_dequeuers); + if (test_pop) + printf_verbose("pop test activated.\n"); + if (test_pop_all) + printf_verbose("pop_all test activated.\n"); + if (test_sync == TEST_SYNC_RCU) + printf_verbose("External sync: RCU.\n"); + else + printf_verbose("External sync: none.\n"); + printf_verbose("Writer delay : %lu loops.\n", rduration); + printf_verbose("Reader duration : %lu loops.\n", wdelay); + printf_verbose("thread %-6s, tid %lu\n", + "main", urcu_get_thread_id()); + + tid_enqueuer = calloc(nr_enqueuers, sizeof(*tid_enqueuer)); + tid_dequeuer = calloc(nr_dequeuers, sizeof(*tid_dequeuer)); + count_enqueuer = calloc(nr_enqueuers, 2 * sizeof(*count_enqueuer)); + count_dequeuer = calloc(nr_dequeuers, 2 * sizeof(*count_dequeuer)); + cds_lfs_init(&s); + err = create_all_cpu_call_rcu_data(0); + if (err) { + printf("Per-CPU call_rcu() worker threads unavailable. Using default global worker thread.\n"); + } + + next_aff = 0; + + for (i = 0; i < nr_enqueuers; i++) { + err = pthread_create(&tid_enqueuer[i], NULL, thr_enqueuer, + &count_enqueuer[2 * i]); + if (err != 0) + exit(1); + } + for (i = 0; i < nr_dequeuers; i++) { + err = pthread_create(&tid_dequeuer[i], NULL, thr_dequeuer, + &count_dequeuer[2 * i]); + if (err != 0) + exit(1); + } + + cmm_smp_mb(); + + test_go = 1; + + for (i = 0; i < duration; i++) { + sleep(1); + if (verbose_mode) + write (1, ".", 1); + } + + test_stop = 1; + + for (i = 0; i < nr_enqueuers; i++) { + err = pthread_join(tid_enqueuer[i], &tret); + if (err != 0) + exit(1); + tot_enqueues += count_enqueuer[2 * i]; + tot_successful_enqueues += count_enqueuer[2 * i + 1]; + } + for (i = 0; i < nr_dequeuers; i++) { + err = pthread_join(tid_dequeuer[i], &tret); + if (err != 0) + exit(1); + tot_dequeues += count_dequeuer[2 * i]; + tot_successful_dequeues += count_dequeuer[2 * i + 1]; + } + + test_end(&s, &end_dequeues); + + printf_verbose("total number of enqueues : %llu, dequeues %llu\n", + tot_enqueues, tot_dequeues); + printf_verbose("total number of successful enqueues : %llu, " + "successful dequeues %llu\n", + tot_successful_enqueues, tot_successful_dequeues); + printf("SUMMARY %-25s testdur %4lu nr_enqueuers %3u wdelay %6lu " + "nr_dequeuers %3u " + "rdur %6lu nr_enqueues %12llu nr_dequeues %12llu " + "successful enqueues %12llu successful dequeues %12llu " + "end_dequeues %llu nr_ops %12llu\n", + argv[0], duration, nr_enqueuers, wdelay, + nr_dequeuers, rduration, tot_enqueues, tot_dequeues, + tot_successful_enqueues, + tot_successful_dequeues, end_dequeues, + tot_enqueues + tot_dequeues); + if (tot_successful_enqueues != tot_successful_dequeues + end_dequeues) + printf("WARNING! Discrepancy between nr succ. enqueues %llu vs " + "succ. dequeues + end dequeues %llu.\n", + tot_successful_enqueues, + tot_successful_dequeues + end_dequeues); + + free_all_cpu_call_rcu_data(); + free(count_enqueuer); + free(count_dequeuer); + free(tid_enqueuer); + free(tid_dequeuer); + return 0; +} diff --git a/tests/benchmark/test_urcu_lfs_rcu.c b/tests/benchmark/test_urcu_lfs_rcu.c new file mode 100644 index 0000000..dc07485 --- /dev/null +++ b/tests/benchmark/test_urcu_lfs_rcu.c @@ -0,0 +1,435 @@ +/* + * test_urcu_lfs_rcu.c + * + * Userspace RCU library - example RCU-based lock-free stack + * + * Copyright February 2010 - Mathieu Desnoyers + * Copyright February 2010 - Paolo Bonzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "cpuset.h" +#include "thread-id.h" + +/* hardcoded number of CPUs */ +#define NR_CPUS 16384 + +#ifndef DYNAMIC_LINK_TEST +#define _LGPL_SOURCE +#endif +#include + +/* Remove deprecation warnings from test build. */ +#define CDS_LFS_RCU_DEPRECATED + +#include + +static volatile int test_go, test_stop; + +static unsigned long rduration; + +static unsigned long duration; + +/* read-side C.S. duration, in loops */ +static unsigned long wdelay; + +static inline void loop_sleep(unsigned long loops) +{ + while (loops-- != 0) + caa_cpu_relax(); +} + +static int verbose_mode; + +#define printf_verbose(fmt, args...) \ + do { \ + if (verbose_mode) \ + printf(fmt, args); \ + } while (0) + +static unsigned int cpu_affinities[NR_CPUS]; +static unsigned int next_aff = 0; +static int use_affinity = 0; + +pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void set_affinity(void) +{ + cpu_set_t mask; + int cpu; + int ret; + + if (!use_affinity) + return; + +#if HAVE_SCHED_SETAFFINITY + ret = pthread_mutex_lock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } + cpu = cpu_affinities[next_aff++]; + ret = pthread_mutex_unlock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } + + CPU_ZERO(&mask); + CPU_SET(cpu, &mask); +#if SCHED_SETAFFINITY_ARGS == 2 + sched_setaffinity(0, &mask); +#else + sched_setaffinity(0, sizeof(mask), &mask); +#endif +#endif /* HAVE_SCHED_SETAFFINITY */ +} + +/* + * returns 0 if test should end. + */ +static int test_duration_dequeue(void) +{ + return !test_stop; +} + +static int test_duration_enqueue(void) +{ + return !test_stop; +} + +static DEFINE_URCU_TLS(unsigned long long, nr_dequeues); +static DEFINE_URCU_TLS(unsigned long long, nr_enqueues); + +static DEFINE_URCU_TLS(unsigned long long, nr_successful_dequeues); +static DEFINE_URCU_TLS(unsigned long long, nr_successful_enqueues); + +static unsigned int nr_enqueuers; +static unsigned int nr_dequeuers; + +struct test { + struct cds_lfs_node_rcu list; + struct rcu_head rcu; +}; + +static struct cds_lfs_stack_rcu s; + +void *thr_enqueuer(void *_count) +{ + unsigned long long *count = _count; + + printf_verbose("thread_begin %s, tid %lu\n", + "enqueuer", urcu_get_thread_id()); + + set_affinity(); + + rcu_register_thread(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + struct test *node = malloc(sizeof(*node)); + if (!node) + goto fail; + cds_lfs_node_init_rcu(&node->list); + /* No rcu read-side is needed for push */ + cds_lfs_push_rcu(&s, &node->list); + URCU_TLS(nr_successful_enqueues)++; + + if (caa_unlikely(wdelay)) + loop_sleep(wdelay); +fail: + URCU_TLS(nr_enqueues)++; + if (caa_unlikely(!test_duration_enqueue())) + break; + } + + rcu_unregister_thread(); + + count[0] = URCU_TLS(nr_enqueues); + count[1] = URCU_TLS(nr_successful_enqueues); + printf_verbose("enqueuer thread_end, tid %lu, " + "enqueues %llu successful_enqueues %llu\n", + urcu_get_thread_id(), + URCU_TLS(nr_enqueues), + URCU_TLS(nr_successful_enqueues)); + return ((void*)1); + +} + +static +void free_node_cb(struct rcu_head *head) +{ + struct test *node = + caa_container_of(head, struct test, rcu); + free(node); +} + +void *thr_dequeuer(void *_count) +{ + unsigned long long *count = _count; + + printf_verbose("thread_begin %s, tid %lu\n", + "dequeuer", urcu_get_thread_id()); + + set_affinity(); + + rcu_register_thread(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + struct cds_lfs_node_rcu *snode; + + rcu_read_lock(); + snode = cds_lfs_pop_rcu(&s); + rcu_read_unlock(); + if (snode) { + struct test *node; + + node = caa_container_of(snode, struct test, list); + call_rcu(&node->rcu, free_node_cb); + URCU_TLS(nr_successful_dequeues)++; + } + URCU_TLS(nr_dequeues)++; + if (caa_unlikely(!test_duration_dequeue())) + break; + if (caa_unlikely(rduration)) + loop_sleep(rduration); + } + + rcu_unregister_thread(); + + printf_verbose("dequeuer thread_end, tid %lu, " + "dequeues %llu, successful_dequeues %llu\n", + urcu_get_thread_id(), + URCU_TLS(nr_dequeues), + URCU_TLS(nr_successful_dequeues)); + count[0] = URCU_TLS(nr_dequeues); + count[1] = URCU_TLS(nr_successful_dequeues); + return ((void*)2); +} + +void test_end(struct cds_lfs_stack_rcu *s, unsigned long long *nr_dequeues) +{ + struct cds_lfs_node_rcu *snode; + + do { + snode = cds_lfs_pop_rcu(s); + if (snode) { + struct test *node; + + node = caa_container_of(snode, struct test, list); + free(node); + (*nr_dequeues)++; + } + } while (snode); +} + +void show_usage(int argc, char **argv) +{ + printf("Usage : %s nr_dequeuers nr_enqueuers duration (s) \n", + argv[0]); + printf("OPTIONS:\n"); + printf(" [-d delay] (enqueuer period (in loops))\n"); + printf(" [-c duration] (dequeuer period (in loops))\n"); + printf(" [-v] (verbose output)\n"); + printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); + printf("\n"); +} + +int main(int argc, char **argv) +{ + int err; + pthread_t *tid_enqueuer, *tid_dequeuer; + void *tret; + unsigned long long *count_enqueuer, *count_dequeuer; + unsigned long long tot_enqueues = 0, tot_dequeues = 0; + unsigned long long tot_successful_enqueues = 0, + tot_successful_dequeues = 0; + unsigned long long end_dequeues = 0; + int i, a; + + if (argc < 4) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[1], "%u", &nr_dequeuers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[2], "%u", &nr_enqueuers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[3], "%lu", &duration); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + for (i = 4; i < argc; i++) { + if (argv[i][0] != '-') + continue; + switch (argv[i][1]) { + case 'a': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + a = atoi(argv[++i]); + cpu_affinities[next_aff++] = a; + use_affinity = 1; + printf_verbose("Adding CPU %d affinity\n", a); + break; + case 'c': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + rduration = atol(argv[++i]); + break; + case 'd': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wdelay = atol(argv[++i]); + break; + case 'v': + verbose_mode = 1; + break; + } + } + + printf_verbose("running test for %lu seconds, %u enqueuers, " + "%u dequeuers.\n", + duration, nr_enqueuers, nr_dequeuers); + printf_verbose("Writer delay : %lu loops.\n", rduration); + printf_verbose("Reader duration : %lu loops.\n", wdelay); + printf_verbose("thread %-6s, tid %lu\n", + "main", urcu_get_thread_id()); + + tid_enqueuer = calloc(nr_enqueuers, sizeof(*tid_enqueuer)); + tid_dequeuer = calloc(nr_dequeuers, sizeof(*tid_dequeuer)); + count_enqueuer = calloc(nr_enqueuers, 2 * sizeof(*count_enqueuer)); + count_dequeuer = calloc(nr_dequeuers, 2 * sizeof(*count_dequeuer)); + cds_lfs_init_rcu(&s); + err = create_all_cpu_call_rcu_data(0); + if (err) { + printf("Per-CPU call_rcu() worker threads unavailable. Using default global worker thread.\n"); + } + + next_aff = 0; + + for (i = 0; i < nr_enqueuers; i++) { + err = pthread_create(&tid_enqueuer[i], NULL, thr_enqueuer, + &count_enqueuer[2 * i]); + if (err != 0) + exit(1); + } + for (i = 0; i < nr_dequeuers; i++) { + err = pthread_create(&tid_dequeuer[i], NULL, thr_dequeuer, + &count_dequeuer[2 * i]); + if (err != 0) + exit(1); + } + + cmm_smp_mb(); + + test_go = 1; + + for (i = 0; i < duration; i++) { + sleep(1); + if (verbose_mode) + write (1, ".", 1); + } + + test_stop = 1; + + for (i = 0; i < nr_enqueuers; i++) { + err = pthread_join(tid_enqueuer[i], &tret); + if (err != 0) + exit(1); + tot_enqueues += count_enqueuer[2 * i]; + tot_successful_enqueues += count_enqueuer[2 * i + 1]; + } + for (i = 0; i < nr_dequeuers; i++) { + err = pthread_join(tid_dequeuer[i], &tret); + if (err != 0) + exit(1); + tot_dequeues += count_dequeuer[2 * i]; + tot_successful_dequeues += count_dequeuer[2 * i + 1]; + } + + test_end(&s, &end_dequeues); + + printf_verbose("total number of enqueues : %llu, dequeues %llu\n", + tot_enqueues, tot_dequeues); + printf_verbose("total number of successful enqueues : %llu, " + "successful dequeues %llu\n", + tot_successful_enqueues, tot_successful_dequeues); + printf("SUMMARY %-25s testdur %4lu nr_enqueuers %3u wdelay %6lu " + "nr_dequeuers %3u " + "rdur %6lu nr_enqueues %12llu nr_dequeues %12llu " + "successful enqueues %12llu successful dequeues %12llu " + "end_dequeues %llu nr_ops %12llu\n", + argv[0], duration, nr_enqueuers, wdelay, + nr_dequeuers, rduration, tot_enqueues, tot_dequeues, + tot_successful_enqueues, + tot_successful_dequeues, end_dequeues, + tot_enqueues + tot_dequeues); + if (tot_successful_enqueues != tot_successful_dequeues + end_dequeues) + printf("WARNING! Discrepancy between nr succ. enqueues %llu vs " + "succ. dequeues + end dequeues %llu.\n", + tot_successful_enqueues, + tot_successful_dequeues + end_dequeues); + + free_all_cpu_call_rcu_data(); + free(count_enqueuer); + free(count_dequeuer); + free(tid_enqueuer); + free(tid_dequeuer); + return 0; +} diff --git a/tests/benchmark/test_urcu_qsbr.c b/tests/benchmark/test_urcu_qsbr.c new file mode 100644 index 0000000..e8ba5f6 --- /dev/null +++ b/tests/benchmark/test_urcu_qsbr.c @@ -0,0 +1,410 @@ +/* + * test_urcu.c + * + * Userspace RCU library - test program + * + * Copyright February 2009 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "cpuset.h" +#include "thread-id.h" + +/* hardcoded number of CPUs */ +#define NR_CPUS 16384 + +#ifndef DYNAMIC_LINK_TEST +#define _LGPL_SOURCE +#else +#define rcu_debug_yield_read() +#endif +#include "urcu-qsbr.h" + +static volatile int test_go, test_stop; + +static unsigned long wdelay; + +static int *test_rcu_pointer; + +static unsigned long duration; + +/* read-side C.S. duration, in loops */ +static unsigned long rduration; + +/* write-side C.S. duration, in loops */ +static unsigned long wduration; + +static inline void loop_sleep(unsigned long loops) +{ + while (loops-- != 0) + caa_cpu_relax(); +} + +static int verbose_mode; + +#define printf_verbose(fmt, args...) \ + do { \ + if (verbose_mode) \ + printf(fmt, args); \ + } while (0) + +static unsigned int cpu_affinities[NR_CPUS]; +static unsigned int next_aff = 0; +static int use_affinity = 0; + +pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void set_affinity(void) +{ +#if HAVE_SCHED_SETAFFINITY + cpu_set_t mask; + int cpu, ret; +#endif /* HAVE_SCHED_SETAFFINITY */ + + if (!use_affinity) + return; + +#if HAVE_SCHED_SETAFFINITY + ret = pthread_mutex_lock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } + cpu = cpu_affinities[next_aff++]; + ret = pthread_mutex_unlock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } + CPU_ZERO(&mask); + CPU_SET(cpu, &mask); +#if SCHED_SETAFFINITY_ARGS == 2 + sched_setaffinity(0, &mask); +#else + sched_setaffinity(0, sizeof(mask), &mask); +#endif +#endif /* HAVE_SCHED_SETAFFINITY */ +} + +/* + * returns 0 if test should end. + */ +static int test_duration_write(void) +{ + return !test_stop; +} + +static int test_duration_read(void) +{ + return !test_stop; +} + +static DEFINE_URCU_TLS(unsigned long long, nr_writes); +static DEFINE_URCU_TLS(unsigned long long, nr_reads); + +static unsigned int nr_readers; +static unsigned int nr_writers; + +pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; + +void rcu_copy_mutex_lock(void) +{ + int ret; + ret = pthread_mutex_lock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } +} + +void rcu_copy_mutex_unlock(void) +{ + int ret; + + ret = pthread_mutex_unlock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } +} + +void *thr_reader(void *_count) +{ + unsigned long long *count = _count; + int *local_ptr; + + printf_verbose("thread_begin %s, tid %lu\n", + "reader", urcu_get_thread_id()); + + set_affinity(); + + rcu_register_thread(); + + assert(rcu_read_ongoing()); + rcu_thread_offline(); + assert(!rcu_read_ongoing()); + rcu_thread_online(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + rcu_read_lock(); + assert(rcu_read_ongoing()); + local_ptr = rcu_dereference(test_rcu_pointer); + rcu_debug_yield_read(); + if (local_ptr) + assert(*local_ptr == 8); + if (caa_unlikely(rduration)) + loop_sleep(rduration); + rcu_read_unlock(); + URCU_TLS(nr_reads)++; + /* QS each 1024 reads */ + if (caa_unlikely((URCU_TLS(nr_reads) & ((1 << 10) - 1)) == 0)) + rcu_quiescent_state(); + if (caa_unlikely(!test_duration_read())) + break; + } + + rcu_unregister_thread(); + + /* test extra thread registration */ + rcu_register_thread(); + rcu_unregister_thread(); + + *count = URCU_TLS(nr_reads); + printf_verbose("thread_end %s, tid %lu\n", + "reader", urcu_get_thread_id()); + return ((void*)1); + +} + +void *thr_writer(void *_count) +{ + unsigned long long *count = _count; + int *new, *old; + + printf_verbose("thread_begin %s, tid %lu\n", + "writer", urcu_get_thread_id()); + + set_affinity(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + new = malloc(sizeof(int)); + assert(new); + *new = 8; + old = rcu_xchg_pointer(&test_rcu_pointer, new); + if (caa_unlikely(wduration)) + loop_sleep(wduration); + synchronize_rcu(); + if (old) + *old = 0; + free(old); + URCU_TLS(nr_writes)++; + if (caa_unlikely(!test_duration_write())) + break; + if (caa_unlikely(wdelay)) + loop_sleep(wdelay); + } + + printf_verbose("thread_end %s, tid %lu\n", + "writer", urcu_get_thread_id()); + *count = URCU_TLS(nr_writes); + return ((void*)2); +} + +void show_usage(int argc, char **argv) +{ + printf("Usage : %s nr_readers nr_writers duration (s) \n", + argv[0]); + printf("OPTIONS:\n"); +#ifdef DEBUG_YIELD + printf(" [-r] [-w] (yield reader and/or writer)\n"); +#endif + printf(" [-d delay] (writer period (us))\n"); + printf(" [-c duration] (reader C.S. duration (in loops))\n"); + printf(" [-e duration] (writer C.S. duration (in loops))\n"); + printf(" [-v] (verbose output)\n"); + printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); + printf("\n"); +} + +int main(int argc, char **argv) +{ + int err; + pthread_t *tid_reader, *tid_writer; + void *tret; + unsigned long long *count_reader, *count_writer; + unsigned long long tot_reads = 0, tot_writes = 0; + int i, a; + + if (argc < 4) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[1], "%u", &nr_readers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[2], "%u", &nr_writers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[3], "%lu", &duration); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + for (i = 4; i < argc; i++) { + if (argv[i][0] != '-') + continue; + switch (argv[i][1]) { +#ifdef DEBUG_YIELD + case 'r': + rcu_yield_active |= RCU_RCU_YIELD_READ; + break; + case 'w': + rcu_yield_active |= RCU_RCU_YIELD_WRITE; + break; +#endif + case 'a': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + a = atoi(argv[++i]); + cpu_affinities[next_aff++] = a; + use_affinity = 1; + printf_verbose("Adding CPU %d affinity\n", a); + break; + case 'c': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + rduration = atol(argv[++i]); + break; + case 'd': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wdelay = atol(argv[++i]); + break; + case 'e': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wduration = atol(argv[++i]); + break; + case 'v': + verbose_mode = 1; + break; + } + } + + printf_verbose("running test for %lu seconds, %u readers, %u writers.\n", + duration, nr_readers, nr_writers); + printf_verbose("Writer delay : %lu loops.\n", wdelay); + printf_verbose("Reader duration : %lu loops.\n", rduration); + printf_verbose("thread %-6s, tid %lu\n", + "main", urcu_get_thread_id()); + + tid_reader = calloc(nr_readers, sizeof(*tid_reader)); + tid_writer = calloc(nr_writers, sizeof(*tid_writer)); + count_reader = calloc(nr_readers, sizeof(*count_reader)); + count_writer = calloc(nr_writers, sizeof(*count_writer)); + + next_aff = 0; + + for (i = 0; i < nr_readers; i++) { + err = pthread_create(&tid_reader[i], NULL, thr_reader, + &count_reader[i]); + if (err != 0) + exit(1); + } + for (i = 0; i < nr_writers; i++) { + err = pthread_create(&tid_writer[i], NULL, thr_writer, + &count_writer[i]); + if (err != 0) + exit(1); + } + + cmm_smp_mb(); + + test_go = 1; + + sleep(duration); + + test_stop = 1; + + for (i = 0; i < nr_readers; i++) { + err = pthread_join(tid_reader[i], &tret); + if (err != 0) + exit(1); + tot_reads += count_reader[i]; + } + for (i = 0; i < nr_writers; i++) { + err = pthread_join(tid_writer[i], &tret); + if (err != 0) + exit(1); + tot_writes += count_writer[i]; + } + + printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads, + tot_writes); + printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu " + "nr_writers %3u " + "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n", + argv[0], duration, nr_readers, rduration, wduration, + nr_writers, wdelay, tot_reads, tot_writes, + tot_reads + tot_writes); + free(test_rcu_pointer); + free(tid_reader); + free(tid_writer); + free(count_reader); + free(count_writer); + return 0; +} diff --git a/tests/benchmark/test_urcu_qsbr_gc.c b/tests/benchmark/test_urcu_qsbr_gc.c new file mode 100644 index 0000000..5fc15c0 --- /dev/null +++ b/tests/benchmark/test_urcu_qsbr_gc.c @@ -0,0 +1,470 @@ +/* + * test_urcu_gc.c + * + * Userspace RCU library - test program (with baatch reclamation) + * + * Copyright February 2009 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "cpuset.h" +#include "thread-id.h" + +/* hardcoded number of CPUs */ +#define NR_CPUS 16384 + +#define _LGPL_SOURCE +#include + +struct test_array { + int a; +}; + +static volatile int test_go, test_stop; + +static unsigned long wdelay; + +static struct test_array *test_rcu_pointer; + +static unsigned long duration; + +/* read-side C.S. duration, in loops */ +static unsigned long rduration; +static unsigned int reclaim_batch = 1; + +struct reclaim_queue { + void **queue; /* Beginning of queue */ + void **head; /* Insert position */ +}; + +static struct reclaim_queue *pending_reclaims; + + +/* write-side C.S. duration, in loops */ +static unsigned long wduration; + +static inline void loop_sleep(unsigned long loops) +{ + while (loops-- != 0) + caa_cpu_relax(); +} + +static int verbose_mode; + +#define printf_verbose(fmt, args...) \ + do { \ + if (verbose_mode) \ + printf(fmt, args); \ + } while (0) + +static unsigned int cpu_affinities[NR_CPUS]; +static unsigned int next_aff = 0; +static int use_affinity = 0; + +pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void set_affinity(void) +{ +#if HAVE_SCHED_SETAFFINITY + cpu_set_t mask; + int cpu, ret; +#endif /* HAVE_SCHED_SETAFFINITY */ + + if (!use_affinity) + return; + +#if HAVE_SCHED_SETAFFINITY + ret = pthread_mutex_lock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } + cpu = cpu_affinities[next_aff++]; + ret = pthread_mutex_unlock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } + + CPU_ZERO(&mask); + CPU_SET(cpu, &mask); +#if SCHED_SETAFFINITY_ARGS == 2 + sched_setaffinity(0, &mask); +#else + sched_setaffinity(0, sizeof(mask), &mask); +#endif +#endif /* HAVE_SCHED_SETAFFINITY */ +} + +/* + * returns 0 if test should end. + */ +static int test_duration_write(void) +{ + return !test_stop; +} + +static int test_duration_read(void) +{ + return !test_stop; +} + +static DEFINE_URCU_TLS(unsigned long long, nr_writes); +static DEFINE_URCU_TLS(unsigned long long, nr_reads); + +static unsigned int nr_readers; +static unsigned int nr_writers; + +pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; +static +unsigned long long __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *tot_nr_writes; + + +void rcu_copy_mutex_lock(void) +{ + int ret; + ret = pthread_mutex_lock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } +} + +void rcu_copy_mutex_unlock(void) +{ + int ret; + + ret = pthread_mutex_unlock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } +} + +void *thr_reader(void *_count) +{ + unsigned long long *count = _count; + struct test_array *local_ptr; + + printf_verbose("thread_begin %s, tid %lu\n", + "reader", urcu_get_thread_id()); + + set_affinity(); + + rcu_register_thread(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + _rcu_read_lock(); + local_ptr = _rcu_dereference(test_rcu_pointer); + rcu_debug_yield_read(); + if (local_ptr) + assert(local_ptr->a == 8); + if (caa_unlikely(rduration)) + loop_sleep(rduration); + _rcu_read_unlock(); + URCU_TLS(nr_reads)++; + /* QS each 1024 reads */ + if (caa_unlikely((URCU_TLS(nr_reads) & ((1 << 10) - 1)) == 0)) + _rcu_quiescent_state(); + if (caa_unlikely(!test_duration_read())) + break; + } + + rcu_unregister_thread(); + + *count = URCU_TLS(nr_reads); + printf_verbose("thread_end %s, tid %lu\n", + "reader", urcu_get_thread_id()); + return ((void*)1); + +} + +static void rcu_gc_clear_queue(unsigned long wtidx) +{ + void **p; + + /* Wait for Q.S and empty queue */ + synchronize_rcu(); + + for (p = pending_reclaims[wtidx].queue; + p < pending_reclaims[wtidx].head; p++) { + /* poison */ + if (*p) + ((struct test_array *)*p)->a = 0; + free(*p); + } + pending_reclaims[wtidx].head = pending_reclaims[wtidx].queue; +} + +/* Using per-thread queue */ +static void rcu_gc_reclaim(unsigned long wtidx, void *old) +{ + /* Queue pointer */ + *pending_reclaims[wtidx].head = old; + pending_reclaims[wtidx].head++; + + if (caa_likely(pending_reclaims[wtidx].head - pending_reclaims[wtidx].queue + < reclaim_batch)) + return; + + rcu_gc_clear_queue(wtidx); +} + +void *thr_writer(void *data) +{ + unsigned long wtidx = (unsigned long)data; +#ifdef TEST_LOCAL_GC + struct test_array *old = NULL; +#else + struct test_array *new, *old; +#endif + + printf_verbose("thread_begin %s, tid %lu\n", + "writer", urcu_get_thread_id()); + + set_affinity(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { +#ifndef TEST_LOCAL_GC + new = malloc(sizeof(*new)); + new->a = 8; + old = _rcu_xchg_pointer(&test_rcu_pointer, new); +#endif + if (caa_unlikely(wduration)) + loop_sleep(wduration); + rcu_gc_reclaim(wtidx, old); + URCU_TLS(nr_writes)++; + if (caa_unlikely(!test_duration_write())) + break; + if (caa_unlikely(wdelay)) + loop_sleep(wdelay); + } + + printf_verbose("thread_end %s, tid %lu\n", + "writer", urcu_get_thread_id()); + tot_nr_writes[wtidx] = URCU_TLS(nr_writes); + return ((void*)2); +} + +void show_usage(int argc, char **argv) +{ + printf("Usage : %s nr_readers nr_writers duration (s) \n", + argv[0]); + printf("OPTIONS:\n"); +#ifdef DEBUG_YIELD + printf(" [-r] [-w] (yield reader and/or writer)\n"); +#endif + printf(" [-b batch] (batch reclaim)\n"); + printf(" [-d delay] (writer period (us))\n"); + printf(" [-c duration] (reader C.S. duration (in loops))\n"); + printf(" [-e duration] (writer C.S. duration (in loops))\n"); + printf(" [-v] (verbose output)\n"); + printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); + printf("\n"); +} + +int main(int argc, char **argv) +{ + int err; + pthread_t *tid_reader, *tid_writer; + void *tret; + unsigned long long *count_reader; + unsigned long long tot_reads = 0, tot_writes = 0; + int i, a; + + if (argc < 4) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[1], "%u", &nr_readers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[2], "%u", &nr_writers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[3], "%lu", &duration); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + for (i = 4; i < argc; i++) { + if (argv[i][0] != '-') + continue; + switch (argv[i][1]) { +#ifdef DEBUG_YIELD + case 'r': + rcu_yield_active |= RCU_YIELD_READ; + break; + case 'w': + rcu_yield_active |= RCU_YIELD_WRITE; + break; +#endif + case 'a': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + a = atoi(argv[++i]); + cpu_affinities[next_aff++] = a; + use_affinity = 1; + printf_verbose("Adding CPU %d affinity\n", a); + break; + case 'b': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + reclaim_batch = atol(argv[++i]); + break; + case 'c': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + rduration = atol(argv[++i]); + break; + case 'd': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wdelay = atol(argv[++i]); + break; + case 'e': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wduration = atol(argv[++i]); + break; + case 'v': + verbose_mode = 1; + break; + } + } + + printf_verbose("running test for %lu seconds, %u readers, %u writers.\n", + duration, nr_readers, nr_writers); + printf_verbose("Writer delay : %lu loops.\n", wdelay); + printf_verbose("Reader duration : %lu loops.\n", rduration); + printf_verbose("thread %-6s, tid %lu\n", + "main", urcu_get_thread_id()); + + tid_reader = calloc(nr_readers, sizeof(*tid_reader)); + tid_writer = calloc(nr_writers, sizeof(*tid_writer)); + count_reader = calloc(nr_readers, sizeof(*count_reader)); + tot_nr_writes = calloc(nr_writers, sizeof(*tot_nr_writes)); + pending_reclaims = calloc(nr_writers, sizeof(*pending_reclaims)); + if (reclaim_batch * sizeof(*pending_reclaims[i].queue) + < CAA_CACHE_LINE_SIZE) + for (i = 0; i < nr_writers; i++) + pending_reclaims[i].queue = calloc(1, CAA_CACHE_LINE_SIZE); + else + for (i = 0; i < nr_writers; i++) + pending_reclaims[i].queue = calloc(reclaim_batch, + sizeof(*pending_reclaims[i].queue)); + for (i = 0; i < nr_writers; i++) + pending_reclaims[i].head = pending_reclaims[i].queue; + + next_aff = 0; + + for (i = 0; i < nr_readers; i++) { + err = pthread_create(&tid_reader[i], NULL, thr_reader, + &count_reader[i]); + if (err != 0) + exit(1); + } + for (i = 0; i < nr_writers; i++) { + err = pthread_create(&tid_writer[i], NULL, thr_writer, + (void *)(long)i); + if (err != 0) + exit(1); + } + + cmm_smp_mb(); + + test_go = 1; + + sleep(duration); + + test_stop = 1; + + for (i = 0; i < nr_readers; i++) { + err = pthread_join(tid_reader[i], &tret); + if (err != 0) + exit(1); + tot_reads += count_reader[i]; + } + for (i = 0; i < nr_writers; i++) { + err = pthread_join(tid_writer[i], &tret); + if (err != 0) + exit(1); + tot_writes += tot_nr_writes[i]; + rcu_gc_clear_queue(i); + } + + printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads, + tot_writes); + printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu " + "nr_writers %3u " + "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu " + "batch %u\n", + argv[0], duration, nr_readers, rduration, wduration, + nr_writers, wdelay, tot_reads, tot_writes, + tot_reads + tot_writes, reclaim_batch); + free(tid_reader); + free(tid_writer); + free(count_reader); + free(tot_nr_writes); + for (i = 0; i < nr_writers; i++) + free(pending_reclaims[i].queue); + free(pending_reclaims); + + return 0; +} diff --git a/tests/benchmark/test_urcu_qsbr_timing.c b/tests/benchmark/test_urcu_qsbr_timing.c new file mode 100644 index 0000000..9b5195d --- /dev/null +++ b/tests/benchmark/test_urcu_qsbr_timing.c @@ -0,0 +1,225 @@ +/* + * test_qsbr_timing.c + * + * Userspace QSBR - test program + * + * Copyright February 2009 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "thread-id.h" + +#define _LGPL_SOURCE +#include + +pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; + +void rcu_copy_mutex_lock(void) +{ + int ret; + ret = pthread_mutex_lock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } +} + +void rcu_copy_mutex_unlock(void) +{ + int ret; + + ret = pthread_mutex_unlock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } +} + +struct test_array { + int a; +}; + +static struct test_array *test_rcu_pointer; + +#define OUTER_READ_LOOP 2000U +#define INNER_READ_LOOP 100000U +#define READ_LOOP ((unsigned long long)OUTER_READ_LOOP * INNER_READ_LOOP) + +#define OUTER_WRITE_LOOP 10U +#define INNER_WRITE_LOOP 200U +#define WRITE_LOOP ((unsigned long long)OUTER_WRITE_LOOP * INNER_WRITE_LOOP) + +static int num_read; +static int num_write; + +#define NR_READ num_read +#define NR_WRITE num_write + +static cycles_t __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *reader_time; +static cycles_t __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *writer_time; + +void *thr_reader(void *arg) +{ + int i, j; + struct test_array *local_ptr; + cycles_t time1, time2; + + printf("thread_begin %s, tid %lu\n", + "reader", urcu_get_thread_id()); + sleep(2); + + rcu_register_thread(); + + time1 = caa_get_cycles(); + for (i = 0; i < OUTER_READ_LOOP; i++) { + for (j = 0; j < INNER_READ_LOOP; j++) { + _rcu_read_lock(); + local_ptr = _rcu_dereference(test_rcu_pointer); + if (local_ptr) { + assert(local_ptr->a == 8); + } + _rcu_read_unlock(); + } + _rcu_quiescent_state(); + } + time2 = caa_get_cycles(); + + rcu_unregister_thread(); + + reader_time[(unsigned long)arg] = time2 - time1; + + sleep(2); + printf("thread_end %s, tid %lu\n", + "reader", urcu_get_thread_id()); + return ((void*)1); + +} + +void *thr_writer(void *arg) +{ + int i, j; + struct test_array *new, *old; + cycles_t time1, time2; + + printf("thread_begin %s, tid %lu\n", + "writer", urcu_get_thread_id()); + sleep(2); + + for (i = 0; i < OUTER_WRITE_LOOP; i++) { + for (j = 0; j < INNER_WRITE_LOOP; j++) { + time1 = caa_get_cycles(); + new = malloc(sizeof(struct test_array)); + rcu_copy_mutex_lock(); + old = test_rcu_pointer; + if (old) { + assert(old->a == 8); + } + new->a = 8; + old = rcu_xchg_pointer(&test_rcu_pointer, new); + rcu_copy_mutex_unlock(); + synchronize_rcu(); + /* can be done after unlock */ + if (old) { + old->a = 0; + } + free(old); + time2 = caa_get_cycles(); + writer_time[(unsigned long)arg] += time2 - time1; + usleep(1); + } + } + + printf("thread_end %s, tid %lu\n", + "writer", urcu_get_thread_id()); + return ((void*)2); +} + +int main(int argc, char **argv) +{ + int err; + pthread_t *tid_reader, *tid_writer; + void *tret; + int i; + cycles_t tot_rtime = 0; + cycles_t tot_wtime = 0; + + if (argc < 2) { + printf("Usage : %s nr_readers nr_writers\n", argv[0]); + exit(-1); + } + num_read = atoi(argv[1]); + num_write = atoi(argv[2]); + + reader_time = calloc(num_read, sizeof(*reader_time)); + writer_time = calloc(num_write, sizeof(*writer_time)); + tid_reader = calloc(num_read, sizeof(*tid_reader)); + tid_writer = calloc(num_write, sizeof(*tid_writer)); + + printf("thread %-6s, tid %lu\n", + "main", urcu_get_thread_id()); + + for (i = 0; i < NR_READ; i++) { + err = pthread_create(&tid_reader[i], NULL, thr_reader, + (void *)(long)i); + if (err != 0) + exit(1); + } + for (i = 0; i < NR_WRITE; i++) { + err = pthread_create(&tid_writer[i], NULL, thr_writer, + (void *)(long)i); + if (err != 0) + exit(1); + } + + sleep(10); + + for (i = 0; i < NR_READ; i++) { + err = pthread_join(tid_reader[i], &tret); + if (err != 0) + exit(1); + tot_rtime += reader_time[i]; + } + for (i = 0; i < NR_WRITE; i++) { + err = pthread_join(tid_writer[i], &tret); + if (err != 0) + exit(1); + tot_wtime += writer_time[i]; + } + free(test_rcu_pointer); + printf("Time per read : %g cycles\n", + (double)tot_rtime / ((double)NR_READ * (double)READ_LOOP)); + printf("Time per write : %g cycles\n", + (double)tot_wtime / ((double)NR_WRITE * (double)WRITE_LOOP)); + + free(reader_time); + free(writer_time); + free(tid_reader); + free(tid_writer); + + return 0; +} diff --git a/tests/benchmark/test_urcu_timing.c b/tests/benchmark/test_urcu_timing.c new file mode 100644 index 0000000..e31b676 --- /dev/null +++ b/tests/benchmark/test_urcu_timing.c @@ -0,0 +1,224 @@ +/* + * test_urcu.c + * + * Userspace RCU library - test program + * + * Copyright February 2009 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "thread-id.h" + +#define _LGPL_SOURCE +#include + +pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; + +void rcu_copy_mutex_lock(void) +{ + int ret; + ret = pthread_mutex_lock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } +} + +void rcu_copy_mutex_unlock(void) +{ + int ret; + + ret = pthread_mutex_unlock(&rcu_copy_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } +} + +struct test_array { + int a; +}; + +static struct test_array *test_rcu_pointer; + +#define OUTER_READ_LOOP 2000U +#define INNER_READ_LOOP 100000U +#define READ_LOOP ((unsigned long long)OUTER_READ_LOOP * INNER_READ_LOOP) + +#define OUTER_WRITE_LOOP 10U +#define INNER_WRITE_LOOP 200U +#define WRITE_LOOP ((unsigned long long)OUTER_WRITE_LOOP * INNER_WRITE_LOOP) + +static int num_read; +static int num_write; + +#define NR_READ num_read +#define NR_WRITE num_write + +static cycles_t __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *reader_time; +static cycles_t __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *writer_time; + +void *thr_reader(void *arg) +{ + int i, j; + struct test_array *local_ptr; + cycles_t time1, time2; + + printf("thread_begin %s, tid %lu\n", + "reader", urcu_get_thread_id()); + sleep(2); + + rcu_register_thread(); + + time1 = caa_get_cycles(); + for (i = 0; i < OUTER_READ_LOOP; i++) { + for (j = 0; j < INNER_READ_LOOP; j++) { + rcu_read_lock(); + local_ptr = rcu_dereference(test_rcu_pointer); + if (local_ptr) { + assert(local_ptr->a == 8); + } + rcu_read_unlock(); + } + } + time2 = caa_get_cycles(); + + rcu_unregister_thread(); + + reader_time[(unsigned long)arg] = time2 - time1; + + sleep(2); + printf("thread_end %s, tid %lu\n", + "reader", urcu_get_thread_id()); + return ((void*)1); + +} + +void *thr_writer(void *arg) +{ + int i, j; + struct test_array *new, *old; + cycles_t time1, time2; + + printf("thread_begin %s, tid %lu\n", + "writer", urcu_get_thread_id()); + sleep(2); + + for (i = 0; i < OUTER_WRITE_LOOP; i++) { + for (j = 0; j < INNER_WRITE_LOOP; j++) { + time1 = caa_get_cycles(); + new = malloc(sizeof(struct test_array)); + rcu_copy_mutex_lock(); + old = test_rcu_pointer; + if (old) { + assert(old->a == 8); + } + new->a = 8; + old = rcu_xchg_pointer(&test_rcu_pointer, new); + rcu_copy_mutex_unlock(); + synchronize_rcu(); + /* can be done after unlock */ + if (old) { + old->a = 0; + } + free(old); + time2 = caa_get_cycles(); + writer_time[(unsigned long)arg] += time2 - time1; + usleep(1); + } + } + + printf("thread_end %s, tid %lu\n", + "writer", urcu_get_thread_id()); + return ((void*)2); +} + +int main(int argc, char **argv) +{ + int err; + pthread_t *tid_reader, *tid_writer; + void *tret; + int i; + cycles_t tot_rtime = 0; + cycles_t tot_wtime = 0; + + if (argc < 2) { + printf("Usage : %s nr_readers nr_writers\n", argv[0]); + exit(-1); + } + num_read = atoi(argv[1]); + num_write = atoi(argv[2]); + + reader_time = calloc(num_read, sizeof(*reader_time)); + writer_time = calloc(num_write, sizeof(*writer_time)); + tid_reader = calloc(num_read, sizeof(*tid_reader)); + tid_writer = calloc(num_write, sizeof(*tid_writer)); + + printf("thread %-6s, tid %lu\n", + "main", urcu_get_thread_id()); + + for (i = 0; i < NR_READ; i++) { + err = pthread_create(&tid_reader[i], NULL, thr_reader, + (void *)(long)i); + if (err != 0) + exit(1); + } + for (i = 0; i < NR_WRITE; i++) { + err = pthread_create(&tid_writer[i], NULL, thr_writer, + (void *)(long)i); + if (err != 0) + exit(1); + } + + sleep(10); + + for (i = 0; i < NR_READ; i++) { + err = pthread_join(tid_reader[i], &tret); + if (err != 0) + exit(1); + tot_rtime += reader_time[i]; + } + for (i = 0; i < NR_WRITE; i++) { + err = pthread_join(tid_writer[i], &tret); + if (err != 0) + exit(1); + tot_wtime += writer_time[i]; + } + free(test_rcu_pointer); + printf("Time per read : %g cycles\n", + (double)tot_rtime / ((double)NR_READ * (double)READ_LOOP)); + printf("Time per write : %g cycles\n", + (double)tot_wtime / ((double)NR_WRITE * (double)WRITE_LOOP)); + + free(reader_time); + free(writer_time); + free(tid_reader); + free(tid_writer); + + return 0; +} diff --git a/tests/benchmark/test_urcu_wfcq.c b/tests/benchmark/test_urcu_wfcq.c new file mode 100644 index 0000000..d0814cd --- /dev/null +++ b/tests/benchmark/test_urcu_wfcq.c @@ -0,0 +1,581 @@ +/* + * test_urcu_wfcq.c + * + * Userspace RCU library - example RCU-based lock-free concurrent queue + * + * Copyright February 2010 - Mathieu Desnoyers + * Copyright February 2010 - Paolo Bonzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "cpuset.h" +#include "thread-id.h" + +/* hardcoded number of CPUs */ +#define NR_CPUS 16384 + +#ifndef DYNAMIC_LINK_TEST +#define _LGPL_SOURCE +#endif +#include + +enum test_sync { + TEST_SYNC_NONE = 0, + TEST_SYNC_MUTEX, +}; + +static enum test_sync test_sync; + +static int test_force_sync; + +static volatile int test_go, test_stop_enqueue, test_stop_dequeue; + +static unsigned long rduration; + +static unsigned long duration; + +/* read-side C.S. duration, in loops */ +static unsigned long wdelay; + +static inline void loop_sleep(unsigned long loops) +{ + while (loops-- != 0) + caa_cpu_relax(); +} + +static int verbose_mode; + +static int test_dequeue, test_splice, test_wait_empty; +static int test_enqueue_stopped; + +#define printf_verbose(fmt, args...) \ + do { \ + if (verbose_mode) \ + printf(fmt, ## args); \ + } while (0) + +static unsigned int cpu_affinities[NR_CPUS]; +static unsigned int next_aff = 0; +static int use_affinity = 0; + +pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void set_affinity(void) +{ +#if HAVE_SCHED_SETAFFINITY + cpu_set_t mask; + int cpu, ret; +#endif /* HAVE_SCHED_SETAFFINITY */ + + if (!use_affinity) + return; + +#if HAVE_SCHED_SETAFFINITY + ret = pthread_mutex_lock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } + cpu = cpu_affinities[next_aff++]; + ret = pthread_mutex_unlock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } + + CPU_ZERO(&mask); + CPU_SET(cpu, &mask); +#if SCHED_SETAFFINITY_ARGS == 2 + sched_setaffinity(0, &mask); +#else + sched_setaffinity(0, sizeof(mask), &mask); +#endif +#endif /* HAVE_SCHED_SETAFFINITY */ +} + +/* + * returns 0 if test should end. + */ +static int test_duration_dequeue(void) +{ + return !test_stop_dequeue; +} + +static int test_duration_enqueue(void) +{ + return !test_stop_enqueue; +} + +static DEFINE_URCU_TLS(unsigned long long, nr_dequeues); +static DEFINE_URCU_TLS(unsigned long long, nr_enqueues); + +static DEFINE_URCU_TLS(unsigned long long, nr_successful_dequeues); +static DEFINE_URCU_TLS(unsigned long long, nr_successful_enqueues); +static DEFINE_URCU_TLS(unsigned long long, nr_empty_dest_enqueues); +static DEFINE_URCU_TLS(unsigned long long, nr_splice); +static DEFINE_URCU_TLS(unsigned long long, nr_dequeue_last); + +static unsigned int nr_enqueuers; +static unsigned int nr_dequeuers; + +static struct cds_wfcq_head __attribute__((aligned(CAA_CACHE_LINE_SIZE))) head; +static struct cds_wfcq_tail __attribute__((aligned(CAA_CACHE_LINE_SIZE))) tail; + +static void *thr_enqueuer(void *_count) +{ + unsigned long long *count = _count; + bool was_nonempty; + + printf_verbose("thread_begin %s, tid %lu\n", + "enqueuer", urcu_get_thread_id()); + + set_affinity(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + struct cds_wfcq_node *node = malloc(sizeof(*node)); + if (!node) + goto fail; + cds_wfcq_node_init(node); + was_nonempty = cds_wfcq_enqueue(&head, &tail, node); + URCU_TLS(nr_successful_enqueues)++; + if (!was_nonempty) + URCU_TLS(nr_empty_dest_enqueues)++; + + if (caa_unlikely(wdelay)) + loop_sleep(wdelay); +fail: + URCU_TLS(nr_enqueues)++; + if (caa_unlikely(!test_duration_enqueue())) + break; + } + + uatomic_inc(&test_enqueue_stopped); + count[0] = URCU_TLS(nr_enqueues); + count[1] = URCU_TLS(nr_successful_enqueues); + count[2] = URCU_TLS(nr_empty_dest_enqueues); + printf_verbose("enqueuer thread_end, tid %lu, " + "enqueues %llu successful_enqueues %llu, " + "empty_dest_enqueues %llu\n", + urcu_get_thread_id(), + URCU_TLS(nr_enqueues), + URCU_TLS(nr_successful_enqueues), + URCU_TLS(nr_empty_dest_enqueues)); + return ((void*)1); + +} + +static void do_test_dequeue(enum test_sync sync) +{ + struct cds_wfcq_node *node; + int state; + + if (sync == TEST_SYNC_MUTEX) + node = cds_wfcq_dequeue_with_state_blocking(&head, &tail, + &state); + else + node = __cds_wfcq_dequeue_with_state_blocking(&head, &tail, + &state); + + if (state & CDS_WFCQ_STATE_LAST) + URCU_TLS(nr_dequeue_last)++; + + if (node) { + free(node); + URCU_TLS(nr_successful_dequeues)++; + } + URCU_TLS(nr_dequeues)++; +} + +static void do_test_splice(enum test_sync sync) +{ + struct cds_wfcq_head tmp_head; + struct cds_wfcq_tail tmp_tail; + struct cds_wfcq_node *node, *n; + enum cds_wfcq_ret ret; + + cds_wfcq_init(&tmp_head, &tmp_tail); + + if (sync == TEST_SYNC_MUTEX) + ret = cds_wfcq_splice_blocking(&tmp_head, &tmp_tail, + &head, &tail); + else + ret = __cds_wfcq_splice_blocking(&tmp_head, &tmp_tail, + &head, &tail); + + switch (ret) { + case CDS_WFCQ_RET_WOULDBLOCK: + assert(0); /* blocking call */ + break; + case CDS_WFCQ_RET_DEST_EMPTY: + URCU_TLS(nr_splice)++; + URCU_TLS(nr_dequeue_last)++; + /* ok */ + break; + case CDS_WFCQ_RET_DEST_NON_EMPTY: + assert(0); /* entirely unexpected */ + break; + case CDS_WFCQ_RET_SRC_EMPTY: + /* ok, we could even skip iteration on dest if we wanted */ + break; + } + + __cds_wfcq_for_each_blocking_safe(&tmp_head, &tmp_tail, node, n) { + free(node); + URCU_TLS(nr_successful_dequeues)++; + URCU_TLS(nr_dequeues)++; + } +} + +static void *thr_dequeuer(void *_count) +{ + unsigned long long *count = _count; + unsigned int counter = 0; + + printf_verbose("thread_begin %s, tid %lu\n", + "dequeuer", urcu_get_thread_id()); + + set_affinity(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + if (test_dequeue && test_splice) { + if (counter & 1) + do_test_dequeue(test_sync); + else + do_test_splice(test_sync); + counter++; + } else { + if (test_dequeue) + do_test_dequeue(test_sync); + else + do_test_splice(test_sync); + } + if (caa_unlikely(!test_duration_dequeue())) + break; + if (caa_unlikely(rduration)) + loop_sleep(rduration); + } + + printf_verbose("dequeuer thread_end, tid %lu, " + "dequeues %llu, successful_dequeues %llu, " + "nr_splice %llu\n", + urcu_get_thread_id(), + URCU_TLS(nr_dequeues), URCU_TLS(nr_successful_dequeues), + URCU_TLS(nr_splice)); + count[0] = URCU_TLS(nr_dequeues); + count[1] = URCU_TLS(nr_successful_dequeues); + count[2] = URCU_TLS(nr_splice); + count[3] = URCU_TLS(nr_dequeue_last); + return ((void*)2); +} + +static void test_end(unsigned long long *nr_dequeues, + unsigned long long *nr_dequeue_last) +{ + struct cds_wfcq_node *node; + int state; + + do { + node = cds_wfcq_dequeue_with_state_blocking(&head, &tail, + &state); + if (node) { + if (state & CDS_WFCQ_STATE_LAST) + (*nr_dequeue_last)++; + free(node); + (*nr_dequeues)++; + } + } while (node); +} + +static void show_usage(int argc, char **argv) +{ + printf("Usage : %s nr_dequeuers nr_enqueuers duration (s) \n", + argv[0]); + printf("OPTIONS:\n"); + printf(" [-d delay] (enqueuer period (in loops))\n"); + printf(" [-c duration] (dequeuer period (in loops))\n"); + printf(" [-v] (verbose output)\n"); + printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); + printf(" [-q] (test dequeue)\n"); + printf(" [-s] (test splice, enabled by default)\n"); + printf(" [-M] (use mutex external synchronization)\n"); + printf(" Note: default: no external synchronization used.\n"); + printf(" [-f] (force user-provided synchronization)\n"); + printf(" [-w] Wait for dequeuer to empty queue\n"); + printf("\n"); +} + +int main(int argc, char **argv) +{ + int err; + pthread_t *tid_enqueuer, *tid_dequeuer; + void *tret; + unsigned long long *count_enqueuer, *count_dequeuer; + unsigned long long tot_enqueues = 0, tot_dequeues = 0; + unsigned long long tot_successful_enqueues = 0, + tot_successful_dequeues = 0, + tot_empty_dest_enqueues = 0, + tot_splice = 0, tot_dequeue_last = 0; + unsigned long long end_dequeues = 0; + int i, a, retval = 0; + + if (argc < 4) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[1], "%u", &nr_dequeuers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[2], "%u", &nr_enqueuers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[3], "%lu", &duration); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + for (i = 4; i < argc; i++) { + if (argv[i][0] != '-') + continue; + switch (argv[i][1]) { + case 'a': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + a = atoi(argv[++i]); + cpu_affinities[next_aff++] = a; + use_affinity = 1; + printf_verbose("Adding CPU %d affinity\n", a); + break; + case 'c': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + rduration = atol(argv[++i]); + break; + case 'd': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wdelay = atol(argv[++i]); + break; + case 'v': + verbose_mode = 1; + break; + case 'q': + test_dequeue = 1; + break; + case 's': + test_splice = 1; + break; + case 'M': + test_sync = TEST_SYNC_MUTEX; + break; + case 'w': + test_wait_empty = 1; + break; + case 'f': + test_force_sync = 1; + break; + } + } + + /* activate splice test by default */ + if (!test_dequeue && !test_splice) + test_splice = 1; + + if (test_sync == TEST_SYNC_NONE && nr_dequeuers > 1 && test_dequeue) { + if (test_force_sync) { + fprintf(stderr, "[WARNING] Using dequeue concurrently " + "with other dequeue or splice without external " + "synchronization. Expect run-time failure.\n"); + } else { + printf("Enforcing mutex synchronization\n"); + test_sync = TEST_SYNC_MUTEX; + } + } + + printf_verbose("running test for %lu seconds, %u enqueuers, " + "%u dequeuers.\n", + duration, nr_enqueuers, nr_dequeuers); + if (test_dequeue) + printf_verbose("dequeue test activated.\n"); + else + printf_verbose("splice test activated.\n"); + if (test_sync == TEST_SYNC_MUTEX) + printf_verbose("External sync: mutex.\n"); + else + printf_verbose("External sync: none.\n"); + if (test_wait_empty) + printf_verbose("Wait for dequeuers to empty queue.\n"); + printf_verbose("Writer delay : %lu loops.\n", rduration); + printf_verbose("Reader duration : %lu loops.\n", wdelay); + printf_verbose("thread %-6s, tid %lu\n", + "main", urcu_get_thread_id()); + + tid_enqueuer = calloc(nr_enqueuers, sizeof(*tid_enqueuer)); + tid_dequeuer = calloc(nr_dequeuers, sizeof(*tid_dequeuer)); + count_enqueuer = calloc(nr_enqueuers, 3 * sizeof(*count_enqueuer)); + count_dequeuer = calloc(nr_dequeuers, 4 * sizeof(*count_dequeuer)); + cds_wfcq_init(&head, &tail); + + next_aff = 0; + + for (i = 0; i < nr_enqueuers; i++) { + err = pthread_create(&tid_enqueuer[i], NULL, thr_enqueuer, + &count_enqueuer[3 * i]); + if (err != 0) + exit(1); + } + for (i = 0; i < nr_dequeuers; i++) { + err = pthread_create(&tid_dequeuer[i], NULL, thr_dequeuer, + &count_dequeuer[4 * i]); + if (err != 0) + exit(1); + } + + cmm_smp_mb(); + + test_go = 1; + + for (i = 0; i < duration; i++) { + sleep(1); + if (verbose_mode) + write (1, ".", 1); + } + + test_stop_enqueue = 1; + + if (test_wait_empty) { + while (nr_enqueuers != uatomic_read(&test_enqueue_stopped)) { + sleep(1); + } + while (!cds_wfcq_empty(&head, &tail)) { + sleep(1); + } + } + + test_stop_dequeue = 1; + + for (i = 0; i < nr_enqueuers; i++) { + err = pthread_join(tid_enqueuer[i], &tret); + if (err != 0) + exit(1); + tot_enqueues += count_enqueuer[3 * i]; + tot_successful_enqueues += count_enqueuer[3 * i + 1]; + tot_empty_dest_enqueues += count_enqueuer[3 * i + 2]; + } + for (i = 0; i < nr_dequeuers; i++) { + err = pthread_join(tid_dequeuer[i], &tret); + if (err != 0) + exit(1); + tot_dequeues += count_dequeuer[4 * i]; + tot_successful_dequeues += count_dequeuer[4 * i + 1]; + tot_splice += count_dequeuer[4 * i + 2]; + tot_dequeue_last += count_dequeuer[4 * i + 3]; + } + + test_end(&end_dequeues, &tot_dequeue_last); + + printf_verbose("total number of enqueues : %llu, dequeues %llu\n", + tot_enqueues, tot_dequeues); + printf_verbose("total number of successful enqueues : %llu, " + "enqueues to empty dest : %llu, " + "successful dequeues %llu, " + "splice : %llu, dequeue_last : %llu\n", + tot_successful_enqueues, + tot_empty_dest_enqueues, + tot_successful_dequeues, + tot_splice, tot_dequeue_last); + printf("SUMMARY %-25s testdur %4lu nr_enqueuers %3u wdelay %6lu " + "nr_dequeuers %3u " + "rdur %6lu nr_enqueues %12llu nr_dequeues %12llu " + "successful enqueues %12llu enqueues to empty dest %12llu " + "successful dequeues %12llu splice %12llu " + "dequeue_last %llu " + "end_dequeues %llu nr_ops %12llu\n", + argv[0], duration, nr_enqueuers, wdelay, + nr_dequeuers, rduration, tot_enqueues, tot_dequeues, + tot_successful_enqueues, + tot_empty_dest_enqueues, + tot_successful_dequeues, tot_splice, tot_dequeue_last, + end_dequeues, + tot_enqueues + tot_dequeues); + + if (tot_successful_enqueues != tot_successful_dequeues + end_dequeues) { + printf("WARNING! Discrepancy between nr succ. enqueues %llu vs " + "succ. dequeues + end dequeues %llu.\n", + tot_successful_enqueues, + tot_successful_dequeues + end_dequeues); + retval = 1; + } + + /* + * If only using splice to dequeue, the enqueuer should see + * exactly as many empty queues than the number of non-empty + * src splice. + */ + if (tot_empty_dest_enqueues != tot_dequeue_last) { + printf("WARNING! Discrepancy between empty enqueue (%llu) and " + "number of dequeue of last element (%llu)\n", + tot_empty_dest_enqueues, + tot_dequeue_last); + retval = 1; + } + free(count_enqueuer); + free(count_dequeuer); + free(tid_enqueuer); + free(tid_dequeuer); + return retval; +} diff --git a/tests/benchmark/test_urcu_wfq.c b/tests/benchmark/test_urcu_wfq.c new file mode 100644 index 0000000..07ab72b --- /dev/null +++ b/tests/benchmark/test_urcu_wfq.c @@ -0,0 +1,401 @@ +/* + * test_urcu_wfq.c + * + * Userspace RCU library - example RCU-based lock-free queue + * + * Copyright February 2010 - Mathieu Desnoyers + * Copyright February 2010 - Paolo Bonzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "cpuset.h" +#include "thread-id.h" + +/* hardcoded number of CPUs */ +#define NR_CPUS 16384 + +#ifndef DYNAMIC_LINK_TEST +#define _LGPL_SOURCE +#endif + +/* Remove deprecation warnings from test build. */ +#define CDS_WFQ_DEPRECATED + +#include +#include + +static volatile int test_go, test_stop; + +static unsigned long rduration; + +static unsigned long duration; + +/* read-side C.S. duration, in loops */ +static unsigned long wdelay; + +static inline void loop_sleep(unsigned long loops) +{ + while (loops-- != 0) + caa_cpu_relax(); +} + +static int verbose_mode; + +#define printf_verbose(fmt, args...) \ + do { \ + if (verbose_mode) \ + printf(fmt, args); \ + } while (0) + +static unsigned int cpu_affinities[NR_CPUS]; +static unsigned int next_aff = 0; +static int use_affinity = 0; + +pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void set_affinity(void) +{ +#if HAVE_SCHED_SETAFFINITY + cpu_set_t mask; + int cpu, ret; +#endif /* HAVE_SCHED_SETAFFINITY */ + + if (!use_affinity) + return; + +#if HAVE_SCHED_SETAFFINITY + ret = pthread_mutex_lock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } + cpu = cpu_affinities[next_aff++]; + ret = pthread_mutex_unlock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } + + CPU_ZERO(&mask); + CPU_SET(cpu, &mask); +#if SCHED_SETAFFINITY_ARGS == 2 + sched_setaffinity(0, &mask); +#else + sched_setaffinity(0, sizeof(mask), &mask); +#endif +#endif /* HAVE_SCHED_SETAFFINITY */ +} + +/* + * returns 0 if test should end. + */ +static int test_duration_dequeue(void) +{ + return !test_stop; +} + +static int test_duration_enqueue(void) +{ + return !test_stop; +} + +static DEFINE_URCU_TLS(unsigned long long, nr_dequeues); +static DEFINE_URCU_TLS(unsigned long long, nr_enqueues); + +static DEFINE_URCU_TLS(unsigned long long, nr_successful_dequeues); +static DEFINE_URCU_TLS(unsigned long long, nr_successful_enqueues); + +static unsigned int nr_enqueuers; +static unsigned int nr_dequeuers; + +static struct cds_wfq_queue q; + +void *thr_enqueuer(void *_count) +{ + unsigned long long *count = _count; + + printf_verbose("thread_begin %s, tid %lu\n", + "enqueuer", urcu_get_thread_id()); + + set_affinity(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + struct cds_wfq_node *node = malloc(sizeof(*node)); + if (!node) + goto fail; + cds_wfq_node_init(node); + cds_wfq_enqueue(&q, node); + URCU_TLS(nr_successful_enqueues)++; + + if (caa_unlikely(wdelay)) + loop_sleep(wdelay); +fail: + URCU_TLS(nr_enqueues)++; + if (caa_unlikely(!test_duration_enqueue())) + break; + } + + count[0] = URCU_TLS(nr_enqueues); + count[1] = URCU_TLS(nr_successful_enqueues); + printf_verbose("enqueuer thread_end, tid %lu, " + "enqueues %llu successful_enqueues %llu\n", + urcu_get_thread_id(), + URCU_TLS(nr_enqueues), + URCU_TLS(nr_successful_enqueues)); + return ((void*)1); + +} + +void *thr_dequeuer(void *_count) +{ + unsigned long long *count = _count; + + printf_verbose("thread_begin %s, tid %lu\n", + "dequeuer", urcu_get_thread_id()); + + set_affinity(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + struct cds_wfq_node *node = cds_wfq_dequeue_blocking(&q); + + if (node) { + free(node); + URCU_TLS(nr_successful_dequeues)++; + } + + URCU_TLS(nr_dequeues)++; + if (caa_unlikely(!test_duration_dequeue())) + break; + if (caa_unlikely(rduration)) + loop_sleep(rduration); + } + + printf_verbose("dequeuer thread_end, tid %lu, " + "dequeues %llu, successful_dequeues %llu\n", + urcu_get_thread_id(), + URCU_TLS(nr_dequeues), + URCU_TLS(nr_successful_dequeues)); + count[0] = URCU_TLS(nr_dequeues); + count[1] = URCU_TLS(nr_successful_dequeues); + return ((void*)2); +} + +void test_end(struct cds_wfq_queue *q, unsigned long long *nr_dequeues) +{ + struct cds_wfq_node *node; + + do { + node = cds_wfq_dequeue_blocking(q); + if (node) { + free(node); + (*nr_dequeues)++; + } + } while (node); +} + +void show_usage(int argc, char **argv) +{ + printf("Usage : %s nr_dequeuers nr_enqueuers duration (s) \n", + argv[0]); + printf("OPTIONS:\n"); + printf(" [-d delay] (enqueuer period (in loops))\n"); + printf(" [-c duration] (dequeuer period (in loops))\n"); + printf(" [-v] (verbose output)\n"); + printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); + printf("\n"); +} + +int main(int argc, char **argv) +{ + int err; + pthread_t *tid_enqueuer, *tid_dequeuer; + void *tret; + unsigned long long *count_enqueuer, *count_dequeuer; + unsigned long long tot_enqueues = 0, tot_dequeues = 0; + unsigned long long tot_successful_enqueues = 0, + tot_successful_dequeues = 0; + unsigned long long end_dequeues = 0; + int i, a; + + if (argc < 4) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[1], "%u", &nr_dequeuers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[2], "%u", &nr_enqueuers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[3], "%lu", &duration); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + for (i = 4; i < argc; i++) { + if (argv[i][0] != '-') + continue; + switch (argv[i][1]) { + case 'a': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + a = atoi(argv[++i]); + cpu_affinities[next_aff++] = a; + use_affinity = 1; + printf_verbose("Adding CPU %d affinity\n", a); + break; + case 'c': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + rduration = atol(argv[++i]); + break; + case 'd': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wdelay = atol(argv[++i]); + break; + case 'v': + verbose_mode = 1; + break; + } + } + + printf_verbose("running test for %lu seconds, %u enqueuers, " + "%u dequeuers.\n", + duration, nr_enqueuers, nr_dequeuers); + printf_verbose("Writer delay : %lu loops.\n", rduration); + printf_verbose("Reader duration : %lu loops.\n", wdelay); + printf_verbose("thread %-6s, tid %lu\n", + "main", urcu_get_thread_id()); + + tid_enqueuer = calloc(nr_enqueuers, sizeof(*tid_enqueuer)); + tid_dequeuer = calloc(nr_dequeuers, sizeof(*tid_dequeuer)); + count_enqueuer = calloc(nr_enqueuers, 2 * sizeof(*count_enqueuer)); + count_dequeuer = calloc(nr_dequeuers, 2 * sizeof(*count_dequeuer)); + cds_wfq_init(&q); + + next_aff = 0; + + for (i = 0; i < nr_enqueuers; i++) { + err = pthread_create(&tid_enqueuer[i], NULL, thr_enqueuer, + &count_enqueuer[2 * i]); + if (err != 0) + exit(1); + } + for (i = 0; i < nr_dequeuers; i++) { + err = pthread_create(&tid_dequeuer[i], NULL, thr_dequeuer, + &count_dequeuer[2 * i]); + if (err != 0) + exit(1); + } + + cmm_smp_mb(); + + test_go = 1; + + for (i = 0; i < duration; i++) { + sleep(1); + if (verbose_mode) + write (1, ".", 1); + } + + test_stop = 1; + + for (i = 0; i < nr_enqueuers; i++) { + err = pthread_join(tid_enqueuer[i], &tret); + if (err != 0) + exit(1); + tot_enqueues += count_enqueuer[2 * i]; + tot_successful_enqueues += count_enqueuer[2 * i + 1]; + } + for (i = 0; i < nr_dequeuers; i++) { + err = pthread_join(tid_dequeuer[i], &tret); + if (err != 0) + exit(1); + tot_dequeues += count_dequeuer[2 * i]; + tot_successful_dequeues += count_dequeuer[2 * i + 1]; + } + + test_end(&q, &end_dequeues); + + printf_verbose("total number of enqueues : %llu, dequeues %llu\n", + tot_enqueues, tot_dequeues); + printf_verbose("total number of successful enqueues : %llu, " + "successful dequeues %llu\n", + tot_successful_enqueues, tot_successful_dequeues); + printf("SUMMARY %-25s testdur %4lu nr_enqueuers %3u wdelay %6lu " + "nr_dequeuers %3u " + "rdur %6lu nr_enqueues %12llu nr_dequeues %12llu " + "successful enqueues %12llu successful dequeues %12llu " + "end_dequeues %llu nr_ops %12llu\n", + argv[0], duration, nr_enqueuers, wdelay, + nr_dequeuers, rduration, tot_enqueues, tot_dequeues, + tot_successful_enqueues, + tot_successful_dequeues, end_dequeues, + tot_enqueues + tot_dequeues); + if (tot_successful_enqueues != tot_successful_dequeues + end_dequeues) + printf("WARNING! Discrepancy between nr succ. enqueues %llu vs " + "succ. dequeues + end dequeues %llu.\n", + tot_successful_enqueues, + tot_successful_dequeues + end_dequeues); + + free(count_enqueuer); + free(count_dequeuer); + free(tid_enqueuer); + free(tid_dequeuer); + return 0; +} diff --git a/tests/benchmark/test_urcu_wfs.c b/tests/benchmark/test_urcu_wfs.c new file mode 100644 index 0000000..c74f8b7 --- /dev/null +++ b/tests/benchmark/test_urcu_wfs.c @@ -0,0 +1,565 @@ +/* + * test_urcu_wfs.c + * + * Userspace RCU library - example RCU-based lock-free stack + * + * Copyright February 2010 - Mathieu Desnoyers + * Copyright February 2010 - Paolo Bonzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "cpuset.h" +#include "thread-id.h" + +/* hardcoded number of CPUs */ +#define NR_CPUS 16384 + +#ifndef DYNAMIC_LINK_TEST +#define _LGPL_SOURCE +#endif +#include + +/* + * External synchronization used. + */ +enum test_sync { + TEST_SYNC_NONE = 0, + TEST_SYNC_MUTEX, +}; + +static enum test_sync test_sync; + +static int test_force_sync; + +static volatile int test_go, test_stop_enqueue, test_stop_dequeue; + +static unsigned long rduration; + +static unsigned long duration; + +/* read-side C.S. duration, in loops */ +static unsigned long wdelay; + +static inline void loop_sleep(unsigned long loops) +{ + while (loops-- != 0) + caa_cpu_relax(); +} + +static int verbose_mode; + +static int test_pop, test_pop_all, test_wait_empty; +static int test_enqueue_stopped; + +#define printf_verbose(fmt, args...) \ + do { \ + if (verbose_mode) \ + printf(fmt, ## args); \ + } while (0) + +static unsigned int cpu_affinities[NR_CPUS]; +static unsigned int next_aff = 0; +static int use_affinity = 0; + +pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void set_affinity(void) +{ +#if HAVE_SCHED_SETAFFINITY + cpu_set_t mask; + int cpu, ret; +#endif /* HAVE_SCHED_SETAFFINITY */ + + if (!use_affinity) + return; + +#if HAVE_SCHED_SETAFFINITY + ret = pthread_mutex_lock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex lock"); + exit(-1); + } + cpu = cpu_affinities[next_aff++]; + ret = pthread_mutex_unlock(&affinity_mutex); + if (ret) { + perror("Error in pthread mutex unlock"); + exit(-1); + } + + CPU_ZERO(&mask); + CPU_SET(cpu, &mask); +#if SCHED_SETAFFINITY_ARGS == 2 + sched_setaffinity(0, &mask); +#else + sched_setaffinity(0, sizeof(mask), &mask); +#endif +#endif /* HAVE_SCHED_SETAFFINITY */ +} + +/* + * returns 0 if test should end. + */ +static int test_duration_dequeue(void) +{ + return !test_stop_dequeue; +} + +static int test_duration_enqueue(void) +{ + return !test_stop_enqueue; +} + +static DEFINE_URCU_TLS(unsigned long long, nr_dequeues); +static DEFINE_URCU_TLS(unsigned long long, nr_enqueues); + +static DEFINE_URCU_TLS(unsigned long long, nr_successful_dequeues); +static DEFINE_URCU_TLS(unsigned long long, nr_successful_enqueues); +static DEFINE_URCU_TLS(unsigned long long, nr_empty_dest_enqueues); +static DEFINE_URCU_TLS(unsigned long long, nr_pop_all); +static DEFINE_URCU_TLS(unsigned long long, nr_pop_last); + +static unsigned int nr_enqueuers; +static unsigned int nr_dequeuers; + +static struct cds_wfs_stack s; + +static void *thr_enqueuer(void *_count) +{ + unsigned long long *count = _count; + bool was_nonempty; + + printf_verbose("thread_begin %s, tid %lu\n", + "enqueuer", urcu_get_thread_id()); + + set_affinity(); + + while (!test_go) + { + } + cmm_smp_mb(); + + for (;;) { + struct cds_wfs_node *node = malloc(sizeof(*node)); + if (!node) + goto fail; + cds_wfs_node_init(node); + was_nonempty = cds_wfs_push(&s, node); + URCU_TLS(nr_successful_enqueues)++; + if (!was_nonempty) + URCU_TLS(nr_empty_dest_enqueues)++; + + if (caa_unlikely(wdelay)) + loop_sleep(wdelay); +fail: + URCU_TLS(nr_enqueues)++; + if (caa_unlikely(!test_duration_enqueue())) + break; + } + + uatomic_inc(&test_enqueue_stopped); + count[0] = URCU_TLS(nr_enqueues); + count[1] = URCU_TLS(nr_successful_enqueues); + count[2] = URCU_TLS(nr_empty_dest_enqueues); + printf_verbose("enqueuer thread_end, tid %lu, " + "enqueues %llu successful_enqueues %llu, " + "empty_dest_enqueues %llu\n", + urcu_get_thread_id(), + URCU_TLS(nr_enqueues), + URCU_TLS(nr_successful_enqueues), + URCU_TLS(nr_empty_dest_enqueues)); + return ((void*)1); + +} + +static void do_test_pop(enum test_sync sync) +{ + struct cds_wfs_node *node; + int state; + + if (sync == TEST_SYNC_MUTEX) + cds_wfs_pop_lock(&s); + node = __cds_wfs_pop_with_state_blocking(&s, &state); + if (sync == TEST_SYNC_MUTEX) + cds_wfs_pop_unlock(&s); + + if (node) { + if (state & CDS_WFS_STATE_LAST) + URCU_TLS(nr_pop_last)++; + free(node); + URCU_TLS(nr_successful_dequeues)++; + } + URCU_TLS(nr_dequeues)++; +} + +static void do_test_pop_all(enum test_sync sync) +{ + struct cds_wfs_head *head; + struct cds_wfs_node *node, *n; + + if (sync == TEST_SYNC_MUTEX) + cds_wfs_pop_lock(&s); + head = __cds_wfs_pop_all(&s); + if (sync == TEST_SYNC_MUTEX) + cds_wfs_pop_unlock(&s); + + /* Check if empty */ + if (cds_wfs_first(head) == NULL) + return; + + URCU_TLS(nr_pop_all)++; + URCU_TLS(nr_pop_last)++; + + cds_wfs_for_each_blocking_safe(head, node, n) { + free(node); + URCU_TLS(nr_successful_dequeues)++; + URCU_TLS(nr_dequeues)++; + } +} + +static void *thr_dequeuer(void *_count) +{ + unsigned long long *count = _count; + unsigned int counter = 0; + + printf_verbose("thread_begin %s, tid %lu\n", + "dequeuer", urcu_get_thread_id()); + + set_affinity(); + + while (!test_go) + { + } + cmm_smp_mb(); + + assert(test_pop || test_pop_all); + + for (;;) { + if (test_pop && test_pop_all) { + if (counter & 1) + do_test_pop(test_sync); + else + do_test_pop_all(test_sync); + counter++; + } else { + if (test_pop) + do_test_pop(test_sync); + else + do_test_pop_all(test_sync); + } + + if (caa_unlikely(!test_duration_dequeue())) + break; + if (caa_unlikely(rduration)) + loop_sleep(rduration); + } + + printf_verbose("dequeuer thread_end, tid %lu, " + "dequeues %llu, successful_dequeues %llu " + "pop_all %llu pop_last %llu\n", + urcu_get_thread_id(), + URCU_TLS(nr_dequeues), URCU_TLS(nr_successful_dequeues), + URCU_TLS(nr_pop_all), + URCU_TLS(nr_pop_last)); + count[0] = URCU_TLS(nr_dequeues); + count[1] = URCU_TLS(nr_successful_dequeues); + count[2] = URCU_TLS(nr_pop_all); + count[3] = URCU_TLS(nr_pop_last); + return ((void*)2); +} + +static void test_end(struct cds_wfs_stack *s, unsigned long long *nr_dequeues, + unsigned long long *nr_pop_last) +{ + struct cds_wfs_node *node; + int state; + + do { + node = cds_wfs_pop_with_state_blocking(s, &state); + if (node) { + if (state & CDS_WFS_STATE_LAST) + (*nr_pop_last)++; + free(node); + (*nr_dequeues)++; + } + } while (node); +} + +static void show_usage(int argc, char **argv) +{ + printf("Usage : %s nr_dequeuers nr_enqueuers duration (s) \n", + argv[0]); + printf("OPTIONS:\n"); + printf(" [-d delay] (enqueuer period (in loops))\n"); + printf(" [-c duration] (dequeuer period (in loops))\n"); + printf(" [-v] (verbose output)\n"); + printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); + printf(" [-p] (test pop)\n"); + printf(" [-P] (test pop_all, enabled by default)\n"); + printf(" [-M] (use mutex external synchronization)\n"); + printf(" Note: default: no external synchronization used.\n"); + printf(" [-f] (force user-provided synchronization)\n"); + printf(" [-w] Wait for dequeuer to empty stack\n"); + printf("\n"); +} + +int main(int argc, char **argv) +{ + int err; + pthread_t *tid_enqueuer, *tid_dequeuer; + void *tret; + unsigned long long *count_enqueuer, *count_dequeuer; + unsigned long long tot_enqueues = 0, tot_dequeues = 0; + unsigned long long tot_successful_enqueues = 0, + tot_successful_dequeues = 0, + tot_empty_dest_enqueues = 0, + tot_pop_all = 0, tot_pop_last = 0; + unsigned long long end_dequeues = 0; + int i, a, retval = 0; + + if (argc < 4) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[1], "%u", &nr_dequeuers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[2], "%u", &nr_enqueuers); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + err = sscanf(argv[3], "%lu", &duration); + if (err != 1) { + show_usage(argc, argv); + return -1; + } + + for (i = 4; i < argc; i++) { + if (argv[i][0] != '-') + continue; + switch (argv[i][1]) { + case 'a': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + a = atoi(argv[++i]); + cpu_affinities[next_aff++] = a; + use_affinity = 1; + printf_verbose("Adding CPU %d affinity\n", a); + break; + case 'c': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + rduration = atol(argv[++i]); + break; + case 'd': + if (argc < i + 2) { + show_usage(argc, argv); + return -1; + } + wdelay = atol(argv[++i]); + break; + case 'v': + verbose_mode = 1; + break; + case 'p': + test_pop = 1; + break; + case 'P': + test_pop_all = 1; + break; + case 'M': + test_sync = TEST_SYNC_MUTEX; + break; + case 'w': + test_wait_empty = 1; + break; + case 'f': + test_force_sync = 1; + break; + } + } + + /* activate pop_all test by default */ + if (!test_pop && !test_pop_all) + test_pop_all = 1; + + if (test_sync == TEST_SYNC_NONE && nr_dequeuers > 1 && test_pop) { + if (test_force_sync) { + fprintf(stderr, "[WARNING] Using pop concurrently " + "with other pop or pop_all without external " + "synchronization. Expect run-time failure.\n"); + } else { + printf("Enforcing mutex synchronization\n"); + test_sync = TEST_SYNC_MUTEX; + } + } + + printf_verbose("running test for %lu seconds, %u enqueuers, " + "%u dequeuers.\n", + duration, nr_enqueuers, nr_dequeuers); + if (test_pop) + printf_verbose("pop test activated.\n"); + if (test_pop_all) + printf_verbose("pop_all test activated.\n"); + if (test_sync == TEST_SYNC_MUTEX) + printf_verbose("External sync: mutex.\n"); + else + printf_verbose("External sync: none.\n"); + if (test_wait_empty) + printf_verbose("Wait for dequeuers to empty stack.\n"); + printf_verbose("Writer delay : %lu loops.\n", rduration); + printf_verbose("Reader duration : %lu loops.\n", wdelay); + printf_verbose("thread %-6s, tid %lu\n", + "main", urcu_get_thread_id()); + + tid_enqueuer = calloc(nr_enqueuers, sizeof(*tid_enqueuer)); + tid_dequeuer = calloc(nr_dequeuers, sizeof(*tid_dequeuer)); + count_enqueuer = calloc(nr_enqueuers, 3 * sizeof(*count_enqueuer)); + count_dequeuer = calloc(nr_dequeuers, 4 * sizeof(*count_dequeuer)); + cds_wfs_init(&s); + + next_aff = 0; + + for (i = 0; i < nr_enqueuers; i++) { + err = pthread_create(&tid_enqueuer[i], NULL, thr_enqueuer, + &count_enqueuer[3 * i]); + if (err != 0) + exit(1); + } + for (i = 0; i < nr_dequeuers; i++) { + err = pthread_create(&tid_dequeuer[i], NULL, thr_dequeuer, + &count_dequeuer[4 * i]); + if (err != 0) + exit(1); + } + + cmm_smp_mb(); + + test_go = 1; + + for (i = 0; i < duration; i++) { + sleep(1); + if (verbose_mode) + write (1, ".", 1); + } + + test_stop_enqueue = 1; + + if (test_wait_empty) { + while (nr_enqueuers != uatomic_read(&test_enqueue_stopped)) { + sleep(1); + } + while (!cds_wfs_empty(&s)) { + sleep(1); + } + } + + test_stop_dequeue = 1; + + for (i = 0; i < nr_enqueuers; i++) { + err = pthread_join(tid_enqueuer[i], &tret); + if (err != 0) + exit(1); + tot_enqueues += count_enqueuer[3 * i]; + tot_successful_enqueues += count_enqueuer[3 * i + 1]; + tot_empty_dest_enqueues += count_enqueuer[3 * i + 2]; + } + for (i = 0; i < nr_dequeuers; i++) { + err = pthread_join(tid_dequeuer[i], &tret); + if (err != 0) + exit(1); + tot_dequeues += count_dequeuer[4 * i]; + tot_successful_dequeues += count_dequeuer[4 * i + 1]; + tot_pop_all += count_dequeuer[4 * i + 2]; + tot_pop_last += count_dequeuer[4 * i + 3]; + } + + test_end(&s, &end_dequeues, &tot_pop_last); + + printf_verbose("total number of enqueues : %llu, dequeues %llu\n", + tot_enqueues, tot_dequeues); + printf_verbose("total number of successful enqueues : %llu, " + "enqueues to empty dest : %llu, " + "successful dequeues %llu, " + "pop_all : %llu, pop_last : %llu\n", + tot_successful_enqueues, + tot_empty_dest_enqueues, + tot_successful_dequeues, + tot_pop_all, tot_pop_last); + printf("SUMMARY %-25s testdur %4lu nr_enqueuers %3u wdelay %6lu " + "nr_dequeuers %3u " + "rdur %6lu nr_enqueues %12llu nr_dequeues %12llu " + "successful enqueues %12llu enqueues to empty dest %12llu " + "successful dequeues %12llu pop_all %12llu " + "pop_last %llu end_dequeues %llu nr_ops %12llu\n", + argv[0], duration, nr_enqueuers, wdelay, + nr_dequeuers, rduration, tot_enqueues, tot_dequeues, + tot_successful_enqueues, + tot_empty_dest_enqueues, + tot_successful_dequeues, tot_pop_all, tot_pop_last, + end_dequeues, + tot_enqueues + tot_dequeues); + if (tot_successful_enqueues != tot_successful_dequeues + end_dequeues) { + printf("WARNING! Discrepancy between nr succ. enqueues %llu vs " + "succ. dequeues + end dequeues %llu.\n", + tot_successful_enqueues, + tot_successful_dequeues + end_dequeues); + retval = 1; + } + /* + * The enqueuer should see exactly as many empty queues than the + * number of non-empty stacks dequeued. + */ + if (tot_empty_dest_enqueues != tot_pop_last) { + printf("WARNING! Discrepancy between empty enqueue (%llu) and " + "number of pop last (%llu)\n", + tot_empty_dest_enqueues, + tot_pop_last); + retval = 1; + } + free(count_enqueuer); + free(count_dequeuer); + free(tid_enqueuer); + free(tid_dequeuer); + return retval; +} diff --git a/tests/common.sh b/tests/common.sh deleted file mode 100644 index 9eb03f8..0000000 --- a/tests/common.sh +++ /dev/null @@ -1,12 +0,0 @@ -# -# This file is meant to be sourced from other tests scripts. -# - -if [ -x "$URCU_TEST_TIME_BIN" ]; then - test_time_bin="$URCU_TEST_TIME_BIN" -elif [ -x "/usr/bin/time" ]; then - test_time_bin="/usr/bin/time" -else - test_time_bin="" -fi - diff --git a/tests/common/api.h b/tests/common/api.h new file mode 100644 index 0000000..1403359 --- /dev/null +++ b/tests/common/api.h @@ -0,0 +1,311 @@ +#ifndef _INCLUDE_API_H +#define _INCLUDE_API_H + +#define _GNU_SOURCE +#include "config.h" + +/* + * common.h: Common Linux kernel-isms. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; but version 2 of the License only due + * to code included from the Linux kernel. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (c) 2006 Paul E. McKenney, IBM. + * + * Much code taken from the Linux kernel. For such code, the option + * to redistribute under later versions of GPL might not be available. + */ + +#include +#include +#include "cpuset.h" + +/* + * Machine parameters. + */ + +#define ____cacheline_internodealigned_in_smp \ + __attribute__((__aligned__(CAA_CACHE_LINE_SIZE))) + +/* + * api_pthreads.h: API mapping to pthreads environment. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. However, please note that much + * of the code in this file derives from the Linux kernel, and that such + * code may not be available except under GPLv2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (c) 2006 Paul E. McKenney, IBM. + */ + +#include +#include +#include +#include +#include +#include +#include +/* #include "atomic.h" */ + +/* + * Exclusive locking primitives. + */ + +typedef pthread_mutex_t spinlock_t; + +#define DEFINE_SPINLOCK(lock) spinlock_t lock = PTHREAD_MUTEX_INITIALIZER; +#define __SPIN_LOCK_UNLOCKED(lockp) PTHREAD_MUTEX_INITIALIZER + +static void spin_lock_init(spinlock_t *sp) +{ + if (pthread_mutex_init(sp, NULL) != 0) { + perror("spin_lock_init:pthread_mutex_init"); + exit(-1); + } +} + +static void spin_lock(spinlock_t *sp) +{ + if (pthread_mutex_lock(sp) != 0) { + perror("spin_lock:pthread_mutex_lock"); + exit(-1); + } +} + +static void spin_unlock(spinlock_t *sp) +{ + if (pthread_mutex_unlock(sp) != 0) { + perror("spin_unlock:pthread_mutex_unlock"); + exit(-1); + } +} + +#define spin_lock_irqsave(l, f) do { f = 1; spin_lock(l); } while (0) +#define spin_unlock_irqrestore(l, f) do { f = 0; spin_unlock(l); } while (0) + +/* + * Thread creation/destruction primitives. + */ + +typedef pthread_t thread_id_t; + +#define NR_THREADS 128 + +#define __THREAD_ID_MAP_EMPTY ((thread_id_t) 0) +#define __THREAD_ID_MAP_WAITING ((thread_id_t) 1) +thread_id_t __thread_id_map[NR_THREADS]; +spinlock_t __thread_id_map_mutex; + +#define for_each_thread(t) \ + for (t = 0; t < NR_THREADS; t++) + +#define for_each_running_thread(t) \ + for (t = 0; t < NR_THREADS; t++) \ + if ((__thread_id_map[t] != __THREAD_ID_MAP_EMPTY) && \ + (__thread_id_map[t] != __THREAD_ID_MAP_WAITING)) + +#define for_each_tid(t, tid) \ + for (t = 0; t < NR_THREADS; t++) \ + if ((((tid) = __thread_id_map[t]) != __THREAD_ID_MAP_EMPTY) && \ + ((tid) != __THREAD_ID_MAP_WAITING)) + +pthread_key_t thread_id_key; + +static int __smp_thread_id(void) +{ + int i; + thread_id_t tid = pthread_self(); + + for (i = 0; i < NR_THREADS; i++) { + if (__thread_id_map[i] == tid) { + long v = i + 1; /* must be non-NULL. */ + + if (pthread_setspecific(thread_id_key, (void *)v) != 0) { + perror("pthread_setspecific"); + exit(-1); + } + return i; + } + } + spin_lock(&__thread_id_map_mutex); + for (i = 0; i < NR_THREADS; i++) { + if (__thread_id_map[i] == tid) { + spin_unlock(&__thread_id_map_mutex); + return i; + } + } + spin_unlock(&__thread_id_map_mutex); + fprintf(stderr, "smp_thread_id: Rogue thread, id: %lu(%#lx)\n", + (unsigned long) tid, (unsigned long) tid); + exit(-1); +} + +static int smp_thread_id(void) +{ + void *id; + + id = pthread_getspecific(thread_id_key); + if (id == NULL) + return __smp_thread_id(); + return (long)(id - 1); +} + +static thread_id_t create_thread(void *(*func)(void *), void *arg) +{ + thread_id_t tid; + int i; + + spin_lock(&__thread_id_map_mutex); + for (i = 0; i < NR_THREADS; i++) { + if (__thread_id_map[i] == __THREAD_ID_MAP_EMPTY) + break; + } + if (i >= NR_THREADS) { + spin_unlock(&__thread_id_map_mutex); + fprintf(stderr, "Thread limit of %d exceeded!\n", NR_THREADS); + exit(-1); + } + __thread_id_map[i] = __THREAD_ID_MAP_WAITING; + spin_unlock(&__thread_id_map_mutex); + if (pthread_create(&tid, NULL, func, arg) != 0) { + perror("create_thread:pthread_create"); + exit(-1); + } + __thread_id_map[i] = tid; + return tid; +} + +static void *wait_thread(thread_id_t tid) +{ + int i; + void *vp; + + for (i = 0; i < NR_THREADS; i++) { + if (__thread_id_map[i] == tid) + break; + } + if (i >= NR_THREADS){ + fprintf(stderr, "wait_thread: bad tid = %lu(%#lx)\n", + (unsigned long)tid, (unsigned long)tid); + exit(-1); + } + if (pthread_join(tid, &vp) != 0) { + perror("wait_thread:pthread_join"); + exit(-1); + } + __thread_id_map[i] = __THREAD_ID_MAP_EMPTY; + return vp; +} + +static void wait_all_threads(void) +{ + int i; + thread_id_t tid; + + for (i = 1; i < NR_THREADS; i++) { + tid = __thread_id_map[i]; + if (tid != __THREAD_ID_MAP_EMPTY && + tid != __THREAD_ID_MAP_WAITING) + (void)wait_thread(tid); + } +} + +static void run_on(int cpu) +{ +#if HAVE_SCHED_SETAFFINITY + cpu_set_t mask; + + CPU_ZERO(&mask); + CPU_SET(cpu, &mask); +#if SCHED_SETAFFINITY_ARGS == 2 + sched_setaffinity(0, &mask); +#else + sched_setaffinity(0, sizeof(mask), &mask); +#endif +#endif /* HAVE_SCHED_SETAFFINITY */ +} + +/* + * timekeeping -- very crude -- should use MONOTONIC... + */ + +long long get_microseconds(void) +{ + struct timeval tv; + + if (gettimeofday(&tv, NULL) != 0) + abort(); + return ((long long)tv.tv_sec) * 1000000LL + (long long)tv.tv_usec; +} + +/* + * Per-thread variables. + */ + +#define DEFINE_PER_THREAD(type, name) \ + struct { \ + __typeof__(type) v \ + __attribute__((__aligned__(CAA_CACHE_LINE_SIZE))); \ + } __per_thread_##name[NR_THREADS]; +#define DECLARE_PER_THREAD(type, name) extern DEFINE_PER_THREAD(type, name) + +#define per_thread(name, thread) __per_thread_##name[thread].v +#define __get_thread_var(name) per_thread(name, smp_thread_id()) + +#define init_per_thread(name, v) \ + do { \ + int __i_p_t_i; \ + for (__i_p_t_i = 0; __i_p_t_i < NR_THREADS; __i_p_t_i++) \ + per_thread(name, __i_p_t_i) = v; \ + } while (0) + +DEFINE_PER_THREAD(int, smp_processor_id); + +/* + * Bug checks. + */ + +#define BUG_ON(c) do { if (!(c)) abort(); } while (0) + +/* + * Initialization -- Must be called before calling any primitives. + */ + +static void smp_init(void) +{ + int i; + + spin_lock_init(&__thread_id_map_mutex); + __thread_id_map[0] = pthread_self(); + for (i = 1; i < NR_THREADS; i++) + __thread_id_map[i] = __THREAD_ID_MAP_EMPTY; + init_per_thread(smp_processor_id, 0); + if (pthread_key_create(&thread_id_key, NULL) != 0) { + perror("pthread_key_create"); + exit(-1); + } +} + +#endif diff --git a/tests/common/cpuset.h b/tests/common/cpuset.h new file mode 100644 index 0000000..4d1f211 --- /dev/null +++ b/tests/common/cpuset.h @@ -0,0 +1,45 @@ +#ifndef _URCU_TESTS_CPUSET_H +#define _URCU_TESTS_CPUSET_H + +/* + * cpuset.h + * + * Userspace RCU library - test cpuset header + * + * Copyright 2009-2013 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#if defined(HAVE_SCHED_SETAFFINITY) || defined(HAVE_CPU_SET_T) \ + || defined(HAVE_CPU_ZERO) || defined(HAVE_CPU_SET) +# include +#endif + +#ifndef HAVE_CPU_SET_T +typedef unsigned long cpu_set_t; +#endif + +#ifndef HAVE_CPU_ZERO +# define CPU_ZERO(cpuset) do { *(cpuset) = 0; } while(0) +#endif + +#ifndef HAVE_CPU_SET +# define CPU_SET(cpu, cpuset) do { *(cpuset) |= (1UL << (cpu)); } while(0) +#endif + +#endif /* _URCU_TESTS_CPUSET_H */ diff --git a/tests/common/thread-id.h b/tests/common/thread-id.h new file mode 100644 index 0000000..9378edc --- /dev/null +++ b/tests/common/thread-id.h @@ -0,0 +1,55 @@ +#ifndef _TEST_THREAD_ID_H +#define _TEST_THREAD_ID_H + +/* + * thread-id.h + * + * Userspace RCU library - thread ID + * + * Copyright 2013 - Mathieu Desnoyers + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifdef __linux__ +# include + +# if defined(_syscall0) +_syscall0(pid_t, gettid) +# elif defined(__NR_gettid) +static inline pid_t gettid(void) +{ + return syscall(__NR_gettid); +} +# endif + +static inline +unsigned long urcu_get_thread_id(void) +{ + return (unsigned long) gettid(); +} +#elif defined(__FreeBSD__) +# include + +static inline +unsigned long urcu_get_thread_id(void) +{ + return (unsigned long) pthread_getthreadid_np(); +} +#else +# warning "use pid as thread ID" +static inline +unsigned long urcu_get_thread_id(void) +{ + return (unsigned long) getpid(); +} +#endif + +#endif /* _TEST_THREAD_ID_H */ diff --git a/tests/cpuset.h b/tests/cpuset.h deleted file mode 100644 index 3f1587d..0000000 --- a/tests/cpuset.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef _URCU_TESTS_CPUSET_H -#define _URCU_TESTS_CPUSET_H - -/* - * cpuset.h - * - * Userspace RCU library - test cpuset header - * - * Copyright 2009-2013 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "../config.h" - -#if defined(HAVE_SCHED_SETAFFINITY) || defined(HAVE_CPU_SET_T) \ - || defined(HAVE_CPU_ZERO) || defined(HAVE_CPU_SET) -# include -#endif - -#ifndef HAVE_CPU_SET_T -typedef unsigned long cpu_set_t; -#endif - -#ifndef HAVE_CPU_ZERO -# define CPU_ZERO(cpuset) do { *(cpuset) = 0; } while(0) -#endif - -#ifndef HAVE_CPU_SET -# define CPU_SET(cpu, cpuset) do { *(cpuset) |= (1UL << (cpu)); } while(0) -#endif - -#endif /* _URCU_TESTS_CPUSET_H */ diff --git a/tests/rcutorture.h b/tests/rcutorture.h deleted file mode 100644 index f8548d8..0000000 --- a/tests/rcutorture.h +++ /dev/null @@ -1,503 +0,0 @@ -/* - * rcutorture.h: simple user-level performance/stress test of RCU. - * - * Usage: - * ./rcu rperf [ ] - * Run a read-side performance test with the specified - * number of readers spaced by . - * Thus "./rcu 16 rperf 2" would run 16 readers on even-numbered - * CPUs from 0 to 30. - * ./rcu uperf [ ] - * Run an update-side performance test with the specified - * number of updaters and specified CPU spacing. - * ./rcu perf [ ] - * Run a combined read/update performance test with the specified - * number of readers and one updater and specified CPU spacing. - * The readers run on the low-numbered CPUs and the updater - * of the highest-numbered CPU. - * - * The above tests produce output as follows: - * - * n_reads: 46008000 n_updates: 146026 nreaders: 2 nupdaters: 1 duration: 1 - * ns/read: 43.4707 ns/update: 6848.1 - * - * The first line lists the total number of RCU reads and updates executed - * during the test, the number of reader threads, the number of updater - * threads, and the duration of the test in seconds. The second line - * lists the average duration of each type of operation in nanoseconds, - * or "nan" if the corresponding type of operation was not performed. - * - * ./rcu stress - * Run a stress test with the specified number of readers and - * one updater. None of the threads are affinitied to any - * particular CPU. - * - * This test produces output as follows: - * - * n_reads: 114633217 n_updates: 3903415 n_mberror: 0 - * rcu_stress_count: 114618391 14826 0 0 0 0 0 0 0 0 0 - * - * The first line lists the number of RCU read and update operations - * executed, followed by the number of memory-ordering violations - * (which will be zero in a correct RCU implementation). The second - * line lists the number of readers observing progressively more stale - * data. A correct RCU implementation will have all but the first two - * numbers non-zero. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (c) 2008 Paul E. McKenney, IBM Corporation. - */ - -/* - * Test variables. - */ - -#include - -DEFINE_PER_THREAD(long long, n_reads_pt); -DEFINE_PER_THREAD(long long, n_updates_pt); - -long long n_reads = 0LL; -long n_updates = 0L; -int nthreadsrunning; -char argsbuf[64]; - -#define GOFLAG_INIT 0 -#define GOFLAG_RUN 1 -#define GOFLAG_STOP 2 - -volatile int goflag __attribute__((__aligned__(CAA_CACHE_LINE_SIZE))) - = GOFLAG_INIT; - -#define RCU_READ_RUN 1000 - -//MD -#define RCU_READ_NESTABLE - -#ifdef RCU_READ_NESTABLE -#define rcu_read_lock_nest() rcu_read_lock() -#define rcu_read_unlock_nest() rcu_read_unlock() -#else /* #ifdef RCU_READ_NESTABLE */ -#define rcu_read_lock_nest() -#define rcu_read_unlock_nest() -#endif /* #else #ifdef RCU_READ_NESTABLE */ - -#ifdef TORTURE_QSBR -#define mark_rcu_quiescent_state rcu_quiescent_state -#define put_thread_offline rcu_thread_offline -#define put_thread_online rcu_thread_online -#endif - -#ifndef mark_rcu_quiescent_state -#define mark_rcu_quiescent_state() do ; while (0) -#endif /* #ifdef mark_rcu_quiescent_state */ - -#ifndef put_thread_offline -#define put_thread_offline() do ; while (0) -#define put_thread_online() do ; while (0) -#define put_thread_online_delay() do ; while (0) -#else /* #ifndef put_thread_offline */ -#define put_thread_online_delay() synchronize_rcu() -#endif /* #else #ifndef put_thread_offline */ - -/* - * Performance test. - */ - -void *rcu_read_perf_test(void *arg) -{ - struct call_rcu_data *crdp; - int i; - int me = (long)arg; - long long n_reads_local = 0; - - rcu_register_thread(); - run_on(me); - uatomic_inc(&nthreadsrunning); - put_thread_offline(); - while (goflag == GOFLAG_INIT) - poll(NULL, 0, 1); - put_thread_online(); - while (goflag == GOFLAG_RUN) { - for (i = 0; i < RCU_READ_RUN; i++) { - rcu_read_lock(); - /* rcu_read_lock_nest(); */ - /* rcu_read_unlock_nest(); */ - rcu_read_unlock(); - } - n_reads_local += RCU_READ_RUN; - mark_rcu_quiescent_state(); - } - __get_thread_var(n_reads_pt) += n_reads_local; - put_thread_offline(); - crdp = get_thread_call_rcu_data(); - set_thread_call_rcu_data(NULL); - call_rcu_data_free(crdp); - rcu_unregister_thread(); - - return (NULL); -} - -void *rcu_update_perf_test(void *arg) -{ - long long n_updates_local = 0; - - if ((random() & 0xf00) == 0) { - struct call_rcu_data *crdp; - - crdp = create_call_rcu_data(0, -1); - if (crdp != NULL) { - fprintf(stderr, - "Using per-thread call_rcu() worker.\n"); - set_thread_call_rcu_data(crdp); - } - } - uatomic_inc(&nthreadsrunning); - while (goflag == GOFLAG_INIT) - poll(NULL, 0, 1); - while (goflag == GOFLAG_RUN) { - synchronize_rcu(); - n_updates_local++; - } - __get_thread_var(n_updates_pt) += n_updates_local; - return NULL; -} - -void perftestinit(void) -{ - init_per_thread(n_reads_pt, 0LL); - init_per_thread(n_updates_pt, 0LL); - uatomic_set(&nthreadsrunning, 0); -} - -void perftestrun(int nthreads, int nreaders, int nupdaters) -{ - int t; - int duration = 1; - - cmm_smp_mb(); - while (uatomic_read(&nthreadsrunning) < nthreads) - poll(NULL, 0, 1); - goflag = GOFLAG_RUN; - cmm_smp_mb(); - sleep(duration); - cmm_smp_mb(); - goflag = GOFLAG_STOP; - cmm_smp_mb(); - wait_all_threads(); - for_each_thread(t) { - n_reads += per_thread(n_reads_pt, t); - n_updates += per_thread(n_updates_pt, t); - } - printf("n_reads: %lld n_updates: %ld nreaders: %d nupdaters: %d duration: %d\n", - n_reads, n_updates, nreaders, nupdaters, duration); - printf("ns/read: %g ns/update: %g\n", - ((duration * 1000*1000*1000.*(double)nreaders) / - (double)n_reads), - ((duration * 1000*1000*1000.*(double)nupdaters) / - (double)n_updates)); - if (get_cpu_call_rcu_data(0)) { - fprintf(stderr, "Deallocating per-CPU call_rcu threads.\n"); - free_all_cpu_call_rcu_data(); - } - exit(0); -} - -void perftest(int nreaders, int cpustride) -{ - int i; - long arg; - - perftestinit(); - for (i = 0; i < nreaders; i++) { - arg = (long)(i * cpustride); - create_thread(rcu_read_perf_test, (void *)arg); - } - arg = (long)(i * cpustride); - create_thread(rcu_update_perf_test, (void *)arg); - perftestrun(i + 1, nreaders, 1); -} - -void rperftest(int nreaders, int cpustride) -{ - int i; - long arg; - - perftestinit(); - init_per_thread(n_reads_pt, 0LL); - for (i = 0; i < nreaders; i++) { - arg = (long)(i * cpustride); - create_thread(rcu_read_perf_test, (void *)arg); - } - perftestrun(i, nreaders, 0); -} - -void uperftest(int nupdaters, int cpustride) -{ - int i; - long arg; - - perftestinit(); - init_per_thread(n_reads_pt, 0LL); - for (i = 0; i < nupdaters; i++) { - arg = (long)(i * cpustride); - create_thread(rcu_update_perf_test, (void *)arg); - } - perftestrun(i, 0, nupdaters); -} - -/* - * Stress test. - */ - -#define RCU_STRESS_PIPE_LEN 10 - -struct rcu_stress { - int pipe_count; - int mbtest; -}; - -struct rcu_stress rcu_stress_array[RCU_STRESS_PIPE_LEN] = { { 0 } }; -struct rcu_stress *rcu_stress_current; -int rcu_stress_idx = 0; - -int n_mberror = 0; -DEFINE_PER_THREAD(long long [RCU_STRESS_PIPE_LEN + 1], rcu_stress_count); - -int garbage = 0; - -void *rcu_read_stress_test(void *arg) -{ - int i; - int itercnt = 0; - struct rcu_stress *p; - int pc; - - rcu_register_thread(); - put_thread_offline(); - while (goflag == GOFLAG_INIT) - poll(NULL, 0, 1); - put_thread_online(); - while (goflag == GOFLAG_RUN) { - rcu_read_lock(); - p = rcu_dereference(rcu_stress_current); - if (p->mbtest == 0) - n_mberror++; - rcu_read_lock_nest(); - for (i = 0; i < 100; i++) - garbage++; - rcu_read_unlock_nest(); - pc = p->pipe_count; - rcu_read_unlock(); - if ((pc > RCU_STRESS_PIPE_LEN) || (pc < 0)) - pc = RCU_STRESS_PIPE_LEN; - __get_thread_var(rcu_stress_count)[pc]++; - __get_thread_var(n_reads_pt)++; - mark_rcu_quiescent_state(); - if ((++itercnt % 0x1000) == 0) { - put_thread_offline(); - put_thread_online_delay(); - put_thread_online(); - } - } - put_thread_offline(); - rcu_unregister_thread(); - - return (NULL); -} - -static pthread_mutex_t call_rcu_test_mutex = PTHREAD_MUTEX_INITIALIZER; -static pthread_cond_t call_rcu_test_cond = PTHREAD_COND_INITIALIZER; - -void rcu_update_stress_test_rcu(struct rcu_head *head) -{ - if (pthread_mutex_lock(&call_rcu_test_mutex) != 0) { - perror("pthread_mutex_lock"); - exit(-1); - } - if (pthread_cond_signal(&call_rcu_test_cond) != 0) { - perror("pthread_cond_signal"); - exit(-1); - } - if (pthread_mutex_unlock(&call_rcu_test_mutex) != 0) { - perror("pthread_mutex_unlock"); - exit(-1); - } -} - -void *rcu_update_stress_test(void *arg) -{ - int i; - struct rcu_stress *p; - struct rcu_head rh; - - while (goflag == GOFLAG_INIT) - poll(NULL, 0, 1); - while (goflag == GOFLAG_RUN) { - i = rcu_stress_idx + 1; - if (i >= RCU_STRESS_PIPE_LEN) - i = 0; - p = &rcu_stress_array[i]; - p->mbtest = 0; - cmm_smp_mb(); - p->pipe_count = 0; - p->mbtest = 1; - rcu_assign_pointer(rcu_stress_current, p); - rcu_stress_idx = i; - for (i = 0; i < RCU_STRESS_PIPE_LEN; i++) - if (i != rcu_stress_idx) - rcu_stress_array[i].pipe_count++; - if (n_updates & 0x1) - synchronize_rcu(); - else { - if (pthread_mutex_lock(&call_rcu_test_mutex) != 0) { - perror("pthread_mutex_lock"); - exit(-1); - } - call_rcu(&rh, rcu_update_stress_test_rcu); - if (pthread_cond_wait(&call_rcu_test_cond, - &call_rcu_test_mutex) != 0) { - perror("pthread_cond_wait"); - exit(-1); - } - if (pthread_mutex_unlock(&call_rcu_test_mutex) != 0) { - perror("pthread_mutex_unlock"); - exit(-1); - } - } - n_updates++; - } - return NULL; -} - -void *rcu_fake_update_stress_test(void *arg) -{ - if ((random() & 0xf00) == 0) { - struct call_rcu_data *crdp; - - crdp = create_call_rcu_data(0, -1); - if (crdp != NULL) { - fprintf(stderr, - "Using per-thread call_rcu() worker.\n"); - set_thread_call_rcu_data(crdp); - } - } - while (goflag == GOFLAG_INIT) - poll(NULL, 0, 1); - while (goflag == GOFLAG_RUN) { - synchronize_rcu(); - poll(NULL, 0, 1); - } - return NULL; -} - -void stresstest(int nreaders) -{ - int i; - int t; - long long *p; - long long sum; - - init_per_thread(n_reads_pt, 0LL); - for_each_thread(t) { - p = &per_thread(rcu_stress_count,t)[0]; - for (i = 0; i <= RCU_STRESS_PIPE_LEN; i++) - p[i] = 0LL; - } - rcu_stress_current = &rcu_stress_array[0]; - rcu_stress_current->pipe_count = 0; - rcu_stress_current->mbtest = 1; - for (i = 0; i < nreaders; i++) - create_thread(rcu_read_stress_test, NULL); - create_thread(rcu_update_stress_test, NULL); - for (i = 0; i < 5; i++) - create_thread(rcu_fake_update_stress_test, NULL); - cmm_smp_mb(); - goflag = GOFLAG_RUN; - cmm_smp_mb(); - sleep(10); - cmm_smp_mb(); - goflag = GOFLAG_STOP; - cmm_smp_mb(); - wait_all_threads(); - for_each_thread(t) - n_reads += per_thread(n_reads_pt, t); - printf("n_reads: %lld n_updates: %ld n_mberror: %d\n", - n_reads, n_updates, n_mberror); - printf("rcu_stress_count:"); - for (i = 0; i <= RCU_STRESS_PIPE_LEN; i++) { - sum = 0LL; - for_each_thread(t) { - sum += per_thread(rcu_stress_count, t)[i]; - } - printf(" %lld", sum); - } - printf("\n"); - if (get_cpu_call_rcu_data(0)) { - fprintf(stderr, "Deallocating per-CPU call_rcu threads.\n"); - free_all_cpu_call_rcu_data(); - } - exit(0); -} - -/* - * Mainprogram. - */ - -void usage(int argc, char *argv[]) -{ - fprintf(stderr, "Usage: %s [nreaders [ perf | stress ] ]\n", argv[0]); - exit(-1); -} - -int main(int argc, char *argv[]) -{ - int nreaders = 1; - int cpustride = 1; - - smp_init(); - //rcu_init(); - srandom(time(NULL)); - if (random() & 0x100) { - fprintf(stderr, "Allocating per-CPU call_rcu threads.\n"); - if (create_all_cpu_call_rcu_data(0)) - perror("create_all_cpu_call_rcu_data"); - } - -#ifdef DEBUG_YIELD - yield_active |= YIELD_READ; - yield_active |= YIELD_WRITE; -#endif - - if (argc > 1) { - nreaders = strtoul(argv[1], NULL, 0); - if (argc == 2) - perftest(nreaders, cpustride); - if (argc > 3) - cpustride = strtoul(argv[3], NULL, 0); - if (strcmp(argv[2], "perf") == 0) - perftest(nreaders, cpustride); - else if (strcmp(argv[2], "rperf") == 0) - rperftest(nreaders, cpustride); - else if (strcmp(argv[2], "uperf") == 0) - uperftest(nreaders, cpustride); - else if (strcmp(argv[2], "stress") == 0) - stresstest(nreaders); - usage(argc, argv); - } - perftest(nreaders, cpustride); - return 0; -} diff --git a/tests/regression/Makefile.am b/tests/regression/Makefile.am new file mode 100644 index 0000000..2c0542f --- /dev/null +++ b/tests/regression/Makefile.am @@ -0,0 +1,75 @@ +AM_LDFLAGS=-lpthread +AM_CFLAGS=-I$(top_srcdir) -I$(top_builddir) -I$(top_srcdir)/tests/common -g + +noinst_PROGRAMS = test_urcu_fork \ + rcutorture_urcu \ + rcutorture_urcu_signal \ + rcutorture_urcu_mb \ + rcutorture_urcu_bp \ + rcutorture_urcu_qsbr + +noinst_HEADERS = rcutorture.h + +if COMPAT_ARCH +COMPAT=$(top_srcdir)/compat_arch_@ARCHTYPE@.c +else +COMPAT= +endif + +if COMPAT_FUTEX +COMPAT+=$(top_srcdir)/compat_futex.c +endif + +URCU=$(top_srcdir)/urcu.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT) +URCU_QSBR=$(top_srcdir)/urcu-qsbr.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT) +# URCU_MB uses urcu.c but -DRCU_MB must be defined +URCU_MB=$(top_srcdir)/urcu.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT) +# URCU_SIGNAL uses urcu.c but -DRCU_SIGNAL must be defined +URCU_SIGNAL=$(top_srcdir)/urcu.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT) +URCU_BP=$(top_srcdir)/urcu-bp.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT) +URCU_DEFER=$(top_srcdir)/urcu.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT) + +URCU_COMMON_LIB=$(top_builddir)/liburcu-common.la +URCU_LIB=$(top_builddir)/liburcu.la +URCU_QSBR_LIB=$(top_builddir)/liburcu-qsbr.la +URCU_MB_LIB=$(top_builddir)/liburcu-mb.la +URCU_SIGNAL_LIB=$(top_builddir)/liburcu-signal.la +URCU_BP_LIB=$(top_builddir)/liburcu-bp.la +URCU_CDS_LIB=$(top_builddir)/liburcu-cds.la + +test_urcu_fork_SOURCES = test_urcu_fork.c $(URCU) + +rcutorture_urcu_SOURCES = urcutorture.c +rcutorture_urcu_CFLAGS = -DRCU_MEMBARRIER $(AM_CFLAGS) +rcutorture_urcu_LDADD = $(URCU) + +rcutorture_urcu_mb_SOURCES = urcutorture.c +rcutorture_urcu_mb_CFLAGS = -DRCU_MB $(AM_CFLAGS) +rcutorture_urcu_mb_LDADD = $(URCU_MB_LIB) + +rcutorture_urcu_qsbr_SOURCES = urcutorture.c +rcutorture_urcu_qsbr_CFLAGS = -DTORTURE_QSBR -DRCU_QSBR $(AM_CFLAGS) +rcutorture_urcu_qsbr_LDADD = $(URCU_QSBR_LIB) + +rcutorture_urcu_signal_SOURCES = urcutorture.c +rcutorture_urcu_signal_CFLAGS = -DRCU_SIGNAL $(AM_CFLAGS) +rcutorture_urcu_signal_LDADD = $(URCU_SIGNAL_LIB) + +rcutorture_urcu_bp_SOURCES = urcutorture.c +rcutorture_urcu_bp_CFLAGS = -DRCU_BP $(AM_CFLAGS) +rcutorture_urcu_bp_LDADD = $(URCU_BP_LIB) + +urcutorture.c: ../common/api.h + +.PHONY: regtest + +# For now, run the benchmarks too as regression tests. +# TODO: split benchmarks from regression tests +regtest: + ./test_urcu_fork + ./rcutorture_urcu + ./rcutorture_urcu_signal + ./rcutorture_urcu_mb + ./rcutorture_urcu_bp + ./rcutorture_urcu_qsbr + cd ../benchmark && ./runall.sh && cd .. diff --git a/tests/regression/rcutorture.h b/tests/regression/rcutorture.h new file mode 100644 index 0000000..f8548d8 --- /dev/null +++ b/tests/regression/rcutorture.h @@ -0,0 +1,503 @@ +/* + * rcutorture.h: simple user-level performance/stress test of RCU. + * + * Usage: + * ./rcu rperf [ ] + * Run a read-side performance test with the specified + * number of readers spaced by . + * Thus "./rcu 16 rperf 2" would run 16 readers on even-numbered + * CPUs from 0 to 30. + * ./rcu uperf [ ] + * Run an update-side performance test with the specified + * number of updaters and specified CPU spacing. + * ./rcu perf [ ] + * Run a combined read/update performance test with the specified + * number of readers and one updater and specified CPU spacing. + * The readers run on the low-numbered CPUs and the updater + * of the highest-numbered CPU. + * + * The above tests produce output as follows: + * + * n_reads: 46008000 n_updates: 146026 nreaders: 2 nupdaters: 1 duration: 1 + * ns/read: 43.4707 ns/update: 6848.1 + * + * The first line lists the total number of RCU reads and updates executed + * during the test, the number of reader threads, the number of updater + * threads, and the duration of the test in seconds. The second line + * lists the average duration of each type of operation in nanoseconds, + * or "nan" if the corresponding type of operation was not performed. + * + * ./rcu stress + * Run a stress test with the specified number of readers and + * one updater. None of the threads are affinitied to any + * particular CPU. + * + * This test produces output as follows: + * + * n_reads: 114633217 n_updates: 3903415 n_mberror: 0 + * rcu_stress_count: 114618391 14826 0 0 0 0 0 0 0 0 0 + * + * The first line lists the number of RCU read and update operations + * executed, followed by the number of memory-ordering violations + * (which will be zero in a correct RCU implementation). The second + * line lists the number of readers observing progressively more stale + * data. A correct RCU implementation will have all but the first two + * numbers non-zero. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Copyright (c) 2008 Paul E. McKenney, IBM Corporation. + */ + +/* + * Test variables. + */ + +#include + +DEFINE_PER_THREAD(long long, n_reads_pt); +DEFINE_PER_THREAD(long long, n_updates_pt); + +long long n_reads = 0LL; +long n_updates = 0L; +int nthreadsrunning; +char argsbuf[64]; + +#define GOFLAG_INIT 0 +#define GOFLAG_RUN 1 +#define GOFLAG_STOP 2 + +volatile int goflag __attribute__((__aligned__(CAA_CACHE_LINE_SIZE))) + = GOFLAG_INIT; + +#define RCU_READ_RUN 1000 + +//MD +#define RCU_READ_NESTABLE + +#ifdef RCU_READ_NESTABLE +#define rcu_read_lock_nest() rcu_read_lock() +#define rcu_read_unlock_nest() rcu_read_unlock() +#else /* #ifdef RCU_READ_NESTABLE */ +#define rcu_read_lock_nest() +#define rcu_read_unlock_nest() +#endif /* #else #ifdef RCU_READ_NESTABLE */ + +#ifdef TORTURE_QSBR +#define mark_rcu_quiescent_state rcu_quiescent_state +#define put_thread_offline rcu_thread_offline +#define put_thread_online rcu_thread_online +#endif + +#ifndef mark_rcu_quiescent_state +#define mark_rcu_quiescent_state() do ; while (0) +#endif /* #ifdef mark_rcu_quiescent_state */ + +#ifndef put_thread_offline +#define put_thread_offline() do ; while (0) +#define put_thread_online() do ; while (0) +#define put_thread_online_delay() do ; while (0) +#else /* #ifndef put_thread_offline */ +#define put_thread_online_delay() synchronize_rcu() +#endif /* #else #ifndef put_thread_offline */ + +/* + * Performance test. + */ + +void *rcu_read_perf_test(void *arg) +{ + struct call_rcu_data *crdp; + int i; + int me = (long)arg; + long long n_reads_local = 0; + + rcu_register_thread(); + run_on(me); + uatomic_inc(&nthreadsrunning); + put_thread_offline(); + while (goflag == GOFLAG_INIT) + poll(NULL, 0, 1); + put_thread_online(); + while (goflag == GOFLAG_RUN) { + for (i = 0; i < RCU_READ_RUN; i++) { + rcu_read_lock(); + /* rcu_read_lock_nest(); */ + /* rcu_read_unlock_nest(); */ + rcu_read_unlock(); + } + n_reads_local += RCU_READ_RUN; + mark_rcu_quiescent_state(); + } + __get_thread_var(n_reads_pt) += n_reads_local; + put_thread_offline(); + crdp = get_thread_call_rcu_data(); + set_thread_call_rcu_data(NULL); + call_rcu_data_free(crdp); + rcu_unregister_thread(); + + return (NULL); +} + +void *rcu_update_perf_test(void *arg) +{ + long long n_updates_local = 0; + + if ((random() & 0xf00) == 0) { + struct call_rcu_data *crdp; + + crdp = create_call_rcu_data(0, -1); + if (crdp != NULL) { + fprintf(stderr, + "Using per-thread call_rcu() worker.\n"); + set_thread_call_rcu_data(crdp); + } + } + uatomic_inc(&nthreadsrunning); + while (goflag == GOFLAG_INIT) + poll(NULL, 0, 1); + while (goflag == GOFLAG_RUN) { + synchronize_rcu(); + n_updates_local++; + } + __get_thread_var(n_updates_pt) += n_updates_local; + return NULL; +} + +void perftestinit(void) +{ + init_per_thread(n_reads_pt, 0LL); + init_per_thread(n_updates_pt, 0LL); + uatomic_set(&nthreadsrunning, 0); +} + +void perftestrun(int nthreads, int nreaders, int nupdaters) +{ + int t; + int duration = 1; + + cmm_smp_mb(); + while (uatomic_read(&nthreadsrunning) < nthreads) + poll(NULL, 0, 1); + goflag = GOFLAG_RUN; + cmm_smp_mb(); + sleep(duration); + cmm_smp_mb(); + goflag = GOFLAG_STOP; + cmm_smp_mb(); + wait_all_threads(); + for_each_thread(t) { + n_reads += per_thread(n_reads_pt, t); + n_updates += per_thread(n_updates_pt, t); + } + printf("n_reads: %lld n_updates: %ld nreaders: %d nupdaters: %d duration: %d\n", + n_reads, n_updates, nreaders, nupdaters, duration); + printf("ns/read: %g ns/update: %g\n", + ((duration * 1000*1000*1000.*(double)nreaders) / + (double)n_reads), + ((duration * 1000*1000*1000.*(double)nupdaters) / + (double)n_updates)); + if (get_cpu_call_rcu_data(0)) { + fprintf(stderr, "Deallocating per-CPU call_rcu threads.\n"); + free_all_cpu_call_rcu_data(); + } + exit(0); +} + +void perftest(int nreaders, int cpustride) +{ + int i; + long arg; + + perftestinit(); + for (i = 0; i < nreaders; i++) { + arg = (long)(i * cpustride); + create_thread(rcu_read_perf_test, (void *)arg); + } + arg = (long)(i * cpustride); + create_thread(rcu_update_perf_test, (void *)arg); + perftestrun(i + 1, nreaders, 1); +} + +void rperftest(int nreaders, int cpustride) +{ + int i; + long arg; + + perftestinit(); + init_per_thread(n_reads_pt, 0LL); + for (i = 0; i < nreaders; i++) { + arg = (long)(i * cpustride); + create_thread(rcu_read_perf_test, (void *)arg); + } + perftestrun(i, nreaders, 0); +} + +void uperftest(int nupdaters, int cpustride) +{ + int i; + long arg; + + perftestinit(); + init_per_thread(n_reads_pt, 0LL); + for (i = 0; i < nupdaters; i++) { + arg = (long)(i * cpustride); + create_thread(rcu_update_perf_test, (void *)arg); + } + perftestrun(i, 0, nupdaters); +} + +/* + * Stress test. + */ + +#define RCU_STRESS_PIPE_LEN 10 + +struct rcu_stress { + int pipe_count; + int mbtest; +}; + +struct rcu_stress rcu_stress_array[RCU_STRESS_PIPE_LEN] = { { 0 } }; +struct rcu_stress *rcu_stress_current; +int rcu_stress_idx = 0; + +int n_mberror = 0; +DEFINE_PER_THREAD(long long [RCU_STRESS_PIPE_LEN + 1], rcu_stress_count); + +int garbage = 0; + +void *rcu_read_stress_test(void *arg) +{ + int i; + int itercnt = 0; + struct rcu_stress *p; + int pc; + + rcu_register_thread(); + put_thread_offline(); + while (goflag == GOFLAG_INIT) + poll(NULL, 0, 1); + put_thread_online(); + while (goflag == GOFLAG_RUN) { + rcu_read_lock(); + p = rcu_dereference(rcu_stress_current); + if (p->mbtest == 0) + n_mberror++; + rcu_read_lock_nest(); + for (i = 0; i < 100; i++) + garbage++; + rcu_read_unlock_nest(); + pc = p->pipe_count; + rcu_read_unlock(); + if ((pc > RCU_STRESS_PIPE_LEN) || (pc < 0)) + pc = RCU_STRESS_PIPE_LEN; + __get_thread_var(rcu_stress_count)[pc]++; + __get_thread_var(n_reads_pt)++; + mark_rcu_quiescent_state(); + if ((++itercnt % 0x1000) == 0) { + put_thread_offline(); + put_thread_online_delay(); + put_thread_online(); + } + } + put_thread_offline(); + rcu_unregister_thread(); + + return (NULL); +} + +static pthread_mutex_t call_rcu_test_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t call_rcu_test_cond = PTHREAD_COND_INITIALIZER; + +void rcu_update_stress_test_rcu(struct rcu_head *head) +{ + if (pthread_mutex_lock(&call_rcu_test_mutex) != 0) { + perror("pthread_mutex_lock"); + exit(-1); + } + if (pthread_cond_signal(&call_rcu_test_cond) != 0) { + perror("pthread_cond_signal"); + exit(-1); + } + if (pthread_mutex_unlock(&call_rcu_test_mutex) != 0) { + perror("pthread_mutex_unlock"); + exit(-1); + } +} + +void *rcu_update_stress_test(void *arg) +{ + int i; + struct rcu_stress *p; + struct rcu_head rh; + + while (goflag == GOFLAG_INIT) + poll(NULL, 0, 1); + while (goflag == GOFLAG_RUN) { + i = rcu_stress_idx + 1; + if (i >= RCU_STRESS_PIPE_LEN) + i = 0; + p = &rcu_stress_array[i]; + p->mbtest = 0; + cmm_smp_mb(); + p->pipe_count = 0; + p->mbtest = 1; + rcu_assign_pointer(rcu_stress_current, p); + rcu_stress_idx = i; + for (i = 0; i < RCU_STRESS_PIPE_LEN; i++) + if (i != rcu_stress_idx) + rcu_stress_array[i].pipe_count++; + if (n_updates & 0x1) + synchronize_rcu(); + else { + if (pthread_mutex_lock(&call_rcu_test_mutex) != 0) { + perror("pthread_mutex_lock"); + exit(-1); + } + call_rcu(&rh, rcu_update_stress_test_rcu); + if (pthread_cond_wait(&call_rcu_test_cond, + &call_rcu_test_mutex) != 0) { + perror("pthread_cond_wait"); + exit(-1); + } + if (pthread_mutex_unlock(&call_rcu_test_mutex) != 0) { + perror("pthread_mutex_unlock"); + exit(-1); + } + } + n_updates++; + } + return NULL; +} + +void *rcu_fake_update_stress_test(void *arg) +{ + if ((random() & 0xf00) == 0) { + struct call_rcu_data *crdp; + + crdp = create_call_rcu_data(0, -1); + if (crdp != NULL) { + fprintf(stderr, + "Using per-thread call_rcu() worker.\n"); + set_thread_call_rcu_data(crdp); + } + } + while (goflag == GOFLAG_INIT) + poll(NULL, 0, 1); + while (goflag == GOFLAG_RUN) { + synchronize_rcu(); + poll(NULL, 0, 1); + } + return NULL; +} + +void stresstest(int nreaders) +{ + int i; + int t; + long long *p; + long long sum; + + init_per_thread(n_reads_pt, 0LL); + for_each_thread(t) { + p = &per_thread(rcu_stress_count,t)[0]; + for (i = 0; i <= RCU_STRESS_PIPE_LEN; i++) + p[i] = 0LL; + } + rcu_stress_current = &rcu_stress_array[0]; + rcu_stress_current->pipe_count = 0; + rcu_stress_current->mbtest = 1; + for (i = 0; i < nreaders; i++) + create_thread(rcu_read_stress_test, NULL); + create_thread(rcu_update_stress_test, NULL); + for (i = 0; i < 5; i++) + create_thread(rcu_fake_update_stress_test, NULL); + cmm_smp_mb(); + goflag = GOFLAG_RUN; + cmm_smp_mb(); + sleep(10); + cmm_smp_mb(); + goflag = GOFLAG_STOP; + cmm_smp_mb(); + wait_all_threads(); + for_each_thread(t) + n_reads += per_thread(n_reads_pt, t); + printf("n_reads: %lld n_updates: %ld n_mberror: %d\n", + n_reads, n_updates, n_mberror); + printf("rcu_stress_count:"); + for (i = 0; i <= RCU_STRESS_PIPE_LEN; i++) { + sum = 0LL; + for_each_thread(t) { + sum += per_thread(rcu_stress_count, t)[i]; + } + printf(" %lld", sum); + } + printf("\n"); + if (get_cpu_call_rcu_data(0)) { + fprintf(stderr, "Deallocating per-CPU call_rcu threads.\n"); + free_all_cpu_call_rcu_data(); + } + exit(0); +} + +/* + * Mainprogram. + */ + +void usage(int argc, char *argv[]) +{ + fprintf(stderr, "Usage: %s [nreaders [ perf | stress ] ]\n", argv[0]); + exit(-1); +} + +int main(int argc, char *argv[]) +{ + int nreaders = 1; + int cpustride = 1; + + smp_init(); + //rcu_init(); + srandom(time(NULL)); + if (random() & 0x100) { + fprintf(stderr, "Allocating per-CPU call_rcu threads.\n"); + if (create_all_cpu_call_rcu_data(0)) + perror("create_all_cpu_call_rcu_data"); + } + +#ifdef DEBUG_YIELD + yield_active |= YIELD_READ; + yield_active |= YIELD_WRITE; +#endif + + if (argc > 1) { + nreaders = strtoul(argv[1], NULL, 0); + if (argc == 2) + perftest(nreaders, cpustride); + if (argc > 3) + cpustride = strtoul(argv[3], NULL, 0); + if (strcmp(argv[2], "perf") == 0) + perftest(nreaders, cpustride); + else if (strcmp(argv[2], "rperf") == 0) + rperftest(nreaders, cpustride); + else if (strcmp(argv[2], "uperf") == 0) + uperftest(nreaders, cpustride); + else if (strcmp(argv[2], "stress") == 0) + stresstest(nreaders); + usage(argc, argv); + } + perftest(nreaders, cpustride); + return 0; +} diff --git a/tests/regression/test_urcu_fork.c b/tests/regression/test_urcu_fork.c new file mode 100644 index 0000000..ce0c9ca --- /dev/null +++ b/tests/regression/test_urcu_fork.c @@ -0,0 +1,147 @@ +/* + * test_urcu_fork.c + * + * Userspace RCU library - test program (fork) + * + * Copyright February 2012 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef DYNAMIC_LINK_TEST +#define _LGPL_SOURCE +#else +#define rcu_debug_yield_read() +#endif +#include + +struct test_node { + int somedata; + struct rcu_head head; +}; + +static void cb(struct rcu_head *head) +{ + struct test_node *node; + + fprintf(stderr, "rcu callback invoked in pid: %d\n", + (int) getpid()); + node = caa_container_of(head, struct test_node, head); + free(node); +} + +static void test_rcu(void) +{ + struct test_node *node; + + rcu_register_thread(); + + synchronize_rcu(); + + rcu_read_lock(); + rcu_read_unlock(); + + node = malloc(sizeof(*node)); + assert(node); + + call_rcu(&node->head, cb); + + synchronize_rcu(); + + rcu_unregister_thread(); +} + +int main(int argc, char **argv) +{ + pid_t pid; + int ret; + +#if 0 + /* pthread_atfork does not work with malloc/free in callbacks */ + ret = pthread_atfork(call_rcu_before_fork, + call_rcu_after_fork_parent, + call_rcu_after_fork_child); + if (ret) { + errno = ret; + perror("pthread_atfork"); + exit(EXIT_FAILURE); + } +#endif + + test_rcu(); + + synchronize_rcu(); + + fprintf(stderr, "%s parent pid: %d, before fork\n", + argv[0], (int) getpid()); + + call_rcu_before_fork(); + pid = fork(); + + if (pid == 0) { + /* child */ + call_rcu_after_fork_child(); + fprintf(stderr, "%s child pid: %d, after fork\n", + argv[0], (int) getpid()); + test_rcu(); + fprintf(stderr, "%s child pid: %d, after rcu test\n", + argv[0], (int) getpid()); + } else if (pid > 0) { + int status; + + /* parent */ + call_rcu_after_fork_parent(); + fprintf(stderr, "%s parent pid: %d, after fork\n", + argv[0], (int) getpid()); + test_rcu(); + fprintf(stderr, "%s parent pid: %d, after rcu test\n", + argv[0], (int) getpid()); + for (;;) { + pid = wait(&status); + if (WIFEXITED(status)) { + fprintf(stderr, "child %u exited normally with status %u\n", + pid, WEXITSTATUS(status)); + break; + } else if (WIFSIGNALED(status)) { + fprintf(stderr, "child %u was terminated by signal %u\n", + pid, WTERMSIG(status)); + break; + } else { + continue; + } + } + } else { + perror("fork"); + exit(EXIT_FAILURE); + } + exit(EXIT_SUCCESS); +} diff --git a/tests/regression/urcutorture.c b/tests/regression/urcutorture.c new file mode 100644 index 0000000..35096a6 --- /dev/null +++ b/tests/regression/urcutorture.c @@ -0,0 +1,30 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include "api.h" +#define _LGPL_SOURCE + +#ifdef RCU_MEMBARRIER +#include +#endif +#ifdef RCU_SIGNAL +#include +#endif +#ifdef RCU_MB +#include +#endif +#ifdef RCU_QSBR +#include +#endif +#ifdef RCU_BP +#include +#endif + +#include +#include +#include "rcutorture.h" diff --git a/tests/runall.sh b/tests/runall.sh deleted file mode 100755 index 244766b..0000000 --- a/tests/runall.sh +++ /dev/null @@ -1,108 +0,0 @@ -#!/bin/sh - -#run all tests - -#set to number of active CPUS -NUM_CPUS=8 - -#extra options, e.g. for setting affinity on even CPUs : -#EXTRA_OPTS=$(for a in $(seq 0 2 127); do echo -n "-a ${a} "; done) - -#ppc64 striding, use with NUM_CPUS=8 - -#stride 1 -#EXTRA_OPTS=$(for a in $(seq 0 2 15); do echo -n "-a ${a} "; done) -#stride 2 -#EXTRA_OPTS=$(for a in $(seq 0 4 31); do echo -n "-a ${a} "; done) -#stride 4 -#EXTRA_OPTS=$(for a in $(seq 0 8 63); do echo -n "-a ${a} "; done) -#stride 8 -#EXTRA_OPTS=$(for a in $(seq 0 16 127); do echo -n "-a ${a} "; done) - -#Vary update fraction -#x: vary update fraction from 0 to 0.0001 - #fix number of readers and reader C.S. length, vary delay between updates -#y: ops/s - -rm -f runall.log -rm -fr runall.detail.log - - -echo Executing batch RCU test - -DURATION=10 -BATCH_ARRAY="1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 - 131072 262144" -NR_WRITERS=$((${NUM_CPUS} / 2)) - -rm -f batch-rcu.log - -NR_READERS=$((${NUM_CPUS} - ${NR_WRITERS})) -for BATCH_SIZE in ${BATCH_ARRAY}; do - echo "./runtests-batch.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d 0 -b ${BATCH_SIZE} ${EXTRA_OPTS} | tee -a batch-rcu.log" >> runall.log - (./runtests-batch.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d 0 -b ${BATCH_SIZE} ${EXTRA_OPTS} | tee -a batch-rcu.log) || exit 1 -done - -#setting gc each 32768. ** UPDATE FOR YOUR ARCHITECTURE BASED ON TEST ABOVE ** -EXTRA_OPTS="${EXTRA_OPTS} -b 32768" - -echo Executing update fraction test - -DURATION=10 -WDELAY_ARRAY="0 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 - 65536 131072 262144 524288 1048576 2097152 4194304 8388608 - 16777216 33554432 67108864 134217728" -NR_WRITERS=$((${NUM_CPUS} / 2)) - -rm -f update-fraction.log - -NR_READERS=$((${NUM_CPUS} - ${NR_WRITERS})) -for WDELAY in ${WDELAY_ARRAY}; do - echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d ${WDELAY} ${EXTRA_OPTS} | tee -a update-fraction.log" >> runall.log - (./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d ${WDELAY} ${EXTRA_OPTS} | tee -a update-fraction.log) || exit 1 -done - -#Test scalability : -# x: vary number of readers from 0 to num cpus -# y: ops/s -# 0 writer. - -echo Executing scalability test - -NR_WRITERS=0 -DURATION=10 - -rm -f scalability.log - -for NR_READERS in $(seq 1 ${NUM_CPUS}); do - echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS}| tee -a scalability.log" >> runall.log - (./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS}| tee -a scalability.log) || exit 1 -done - - -# x: Vary reader C.S. length from 0 to 100 us -# y: ops/s -# 8 readers -# 0 writers - -echo Executing reader C.S. length test - -NR_READERS=${NUM_CPUS} -NR_WRITERS=0 -DURATION=10 -#in loops. -READERCSLEN_ARRAY="0 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 524288 1048576 2097152" - -rm -f readercslen.log - -for READERCSLEN in ${READERCSLEN_ARRAY}; do - echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS} -c ${READERCSLEN} | tee -a readercslen.log" >> runall.log - (./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS} -c ${READERCSLEN} | tee -a readercslen.log) || exit 1 -done - -echo Executing multi-flavor RCU test -./test_urcu_multiflavor || exit 1 -./test_urcu_multiflavor_dynlink || exit 1 - -echo Executing Hash table test -./runhash.sh || exit 1 diff --git a/tests/runhash.sh b/tests/runhash.sh deleted file mode 100755 index 0ba20ef..0000000 --- a/tests/runhash.sh +++ /dev/null @@ -1,136 +0,0 @@ -#!/bin/sh - -# TODO: missing tests: -# - send kill signals during tests to change the behavior between -# add/remove/random -# - validate that "nr_leaked" is always 0 in SUMMARY for all tests - -# 30 seconds per test -TIME_UNITS=30 - -TESTPROG=./test_urcu_hash - -#thread multiplier -THREAD_MUL=1 - -EXTRA_PARAMS=-v - -# ** test update coherency with single-value table - -# rw test, single key, replace and del randomly, 4 threads, auto resize. -# key range: init, lookup, and update: 0 to 0 -${TESTPROG} 0 $((4*${THREAD_MUL})) ${TIME_UNITS} -A -s -M 1 -N 1 -O 1 ${EXTRA_PARAMS} || exit 1 - -# rw test, single key, add unique and del randomly, 4 threads, auto resize. -# key range: init, lookup, and update: 0 to 0 -${TESTPROG} 0 $((4*${THREAD_MUL})) ${TIME_UNITS} -A -u -M 1 -N 1 -O 1 ${EXTRA_PARAMS} || exit 1 - -# rw test, single key, replace and del randomly, 2 lookup threads, 2 update threads, auto resize. -# key range: init, lookup, and update: 0 to 0 -${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A -s -M 1 -N 1 -O 1 ${EXTRA_PARAMS} || exit 1 - -# rw test, single key, add and del randomly, 2 lookup threads, 2 update threads, auto resize. -# key range: init, lookup, and update: 0 to 0 -${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A -M 1 -N 1 -O 1 ${EXTRA_PARAMS} || exit 1 - - -# ** test updates vs lookups with default table - -# rw test, 2 lookup, 2 update threads, add and del randomly, auto resize. -# max 1048576 buckets -# key range: init, lookup, and update: 0 to 999999 -${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A ${EXTRA_PARAMS} || exit 1 - -# rw test, 2 lookup, 2 update threads, add_replace and del randomly, auto resize. -# max 1048576 buckets -# key range: init, lookup, and update: 0 to 999999 -${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A -s ${EXTRA_PARAMS} || exit 1 - -# rw test, 2 lookup, 2 update threads, add_unique and del randomly, auto resize. -# max 1048576 buckets -# key range: init, lookup, and update: 0 to 999999 -${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A -u ${EXTRA_PARAMS} || exit 1 - - -# test memory management backends - -# rw test, 2 lookup, 2 update threads, add only, auto resize. -# max buckets: 1048576 -# key range: init, lookup, and update: 0 to 99999999 -# mm backend: "order" -${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A -m 1 -n 1048576 -i \ - -M 100000000 -N 100000000 -O 100000000 -B order ${EXTRA_PARAMS} || exit 1 - -# rw test, 2 lookup, 2 update threads, add only, auto resize. -# max buckets: 1048576 -# key range: init, lookup, and update: 0 to 99999999 -# mm backend: "chunk" -${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A -m 1 -n 1048576 -i \ - -M 100000000 -N 100000000 -O 100000000 -B chunk ${EXTRA_PARAMS} || exit 1 - -# rw test, 2 lookup, 2 update threads, add only, auto resize. -# max buckets: 1048576 -# key range: init, lookup, and update: 0 to 99999999 -# mm backend: "mmap" -${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A -m 1 -n 1048576 -i \ - -M 100000000 -N 100000000 -O 100000000 -B mmap ${EXTRA_PARAMS} || exit 1 - - -# ** key range tests - -# rw test, 2 lookup, 2 update threads, add and del randomly, auto resize. -# max 1048576 buckets -# key range: init, and update: 0 to 999999 -# key range: lookup: 1000000 to 1999999 -# NOTE: reader threads in this test should never have a successful -# lookup. TODO -${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A \ - -R 1000000 ${EXTRA_PARAMS} || exit 1 - -# ** small key range - -# rw test, 2 lookup, 2 update threads, add and del randomly, auto resize. -# max 1048576 buckets -# key range: init, update, and lookups: 0 to 9 -${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A \ - -M 10 -N 10 -O 10 ${EXTRA_PARAMS} || exit 1 - -# rw test, 2 lookup, 2 update threads, add_unique and del randomly, auto resize. -# max 1048576 buckets -# key range: init, update, and lookups: 0 to 9 -${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A \ - -M 10 -N 10 -O 10 -u ${EXTRA_PARAMS} || exit 1 - -# rw test, 2 lookup, 2 update threads, add_replace and del randomly, auto resize. -# max 1048576 buckets -# key range: init, update, and lookups: 0 to 9 -${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A \ - -M 10 -N 10 -O 10 -s ${EXTRA_PARAMS} || exit 1 - -# ** lookup for known keys - -# rw test, 2 lookup, 2 update threads, add_replace and del randomly, auto resize. -# max 1048576 buckets -# lookup range is entirely populated. -# key range: init, and lookups: 0 to 9 -# key range: updates: 10 to 19 -# NOTE: reader threads in this test should always have successful -# lookups. TODO -${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A \ - -M 10 -N 10 -O 10 -R 0 -T 0 -S 10 -k 10 -s ${EXTRA_PARAMS} || exit 1 - -# ** Uniqueness test - -# rw test, 2 lookup, 2 update threads, add_unique, add_replace and del randomly, auto resize. -# max 1048576 buckets -# asserts that no duplicates are observed by reader threads -# standard length hash chains -${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A \ - -U ${EXTRA_PARAMS} || exit 1 - -# rw test, 2 lookup, 2 update threads, add_unique, add_replace and del randomly, auto resize. -# max 1048576 buckets -# asserts that no duplicates are observed by reader threads -# create long hash chains: using modulo 4 on keys as hash -${TESTPROG} $((2*${THREAD_MUL})) $((2*${THREAD_MUL})) ${TIME_UNITS} -A \ - -U -C 4 ${EXTRA_PARAMS} || exit 1 diff --git a/tests/runpaul-phase1.sh b/tests/runpaul-phase1.sh deleted file mode 100755 index d2c8649..0000000 --- a/tests/runpaul-phase1.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/sh - -#run all tests - -#set to number of active CPUS -NUM_CPUS=64 - -#extra options, e.g. for setting affinity on even CPUs : -EXTRA_OPTS=$(for a in $(seq 0 2 127); do echo -n "-a ${a} "; done) - -#ppc64 striding, use with NUM_CPUS=8 - -#stride 1 -#EXTRA_OPTS=$(for a in $(seq 0 2 15); do echo -n "-a ${a} "; done) -#stride 2 -#EXTRA_OPTS=$(for a in $(seq 0 4 31); do echo -n "-a ${a} "; done) -#stride 4 -#EXTRA_OPTS=$(for a in $(seq 0 8 63); do echo -n "-a ${a} "; done) -#stride 8 -#EXTRA_OPTS=$(for a in $(seq 0 16 127); do echo -n "-a ${a} "; done) - -#Vary update fraction -#x: vary update fraction from 0 to 0.0001 - #fix number of readers and reader C.S. length, vary delay between updates -#y: ops/s - -rm -f runall.log -rm -fr runall.detail.log - - -echo Executing batch RCU test - -DURATION=10 -BATCH_ARRAY="1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 - 131072 262144" -NR_WRITERS=$((${NUM_CPUS} / 2)) - -rm -f batch-rcu.log - -NR_READERS=$((${NUM_CPUS} - ${NR_WRITERS})) -for BATCH_SIZE in ${BATCH_ARRAY}; do - echo "./runtests-batch.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d 0 -b ${BATCH_SIZE} ${EXTRA_OPTS} | tee -a batch-rcu.log" >> runall.log - ./runtests-batch.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d 0 -b ${BATCH_SIZE} ${EXTRA_OPTS} | tee -a batch-rcu.log -done diff --git a/tests/runpaul-phase2.sh b/tests/runpaul-phase2.sh deleted file mode 100755 index bba9e3e..0000000 --- a/tests/runpaul-phase2.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/sh - -#run all tests - -#set to number of active CPUS -NUM_CPUS=64 - -#extra options, e.g. for setting affinity on even CPUs : -EXTRA_OPTS=$(for a in $(seq 0 2 127); do echo -n "-a ${a} "; done) - -#ppc64 striding, use with NUM_CPUS=8 - -#stride 1 -#EXTRA_OPTS=$(for a in $(seq 0 2 15); do echo -n "-a ${a} "; done) -#stride 2 -#EXTRA_OPTS=$(for a in $(seq 0 4 31); do echo -n "-a ${a} "; done) -#stride 4 -#EXTRA_OPTS=$(for a in $(seq 0 8 63); do echo -n "-a ${a} "; done) -#stride 8 -#EXTRA_OPTS=$(for a in $(seq 0 16 127); do echo -n "-a ${a} "; done) - -#Vary update fraction -#x: vary update fraction from 0 to 0.0001 - #fix number of readers and reader C.S. length, vary delay between updates -#y: ops/s - -rm -f runall.log -rm -fr runall.detail.log - -#setting gc each 32768. ** UPDATE FOR YOUR ARCHITECTURE BASED ON PHASE 1 RESULT ** -EXTRA_OPTS="${EXTRA_OPTS} -b 32768" - -echo Executing update fraction test - -DURATION=10 -WDELAY_ARRAY="0 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 - 65536 131072 262144 524288 1048576 2097152 4194304 8388608 - 16777216 33554432 67108864 134217728" -NR_WRITERS=$((${NUM_CPUS} / 2)) - -rm -f update-fraction.log - -NR_READERS=$((${NUM_CPUS} - ${NR_WRITERS})) -for WDELAY in ${WDELAY_ARRAY}; do - echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d ${WDELAY} ${EXTRA_OPTS} | tee -a update-fraction.log" >> runall.log - ./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d ${WDELAY} ${EXTRA_OPTS} | tee -a update-fraction.log -done diff --git a/tests/runpaul-phase3.sh b/tests/runpaul-phase3.sh deleted file mode 100755 index 7c5f055..0000000 --- a/tests/runpaul-phase3.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/sh - -#run all tests - -#set to number of active CPUS -NUM_CPUS=64 - -#extra options, e.g. for setting affinity on even CPUs : -EXTRA_OPTS=$(for a in $(seq 0 2 127); do echo -n "-a ${a} "; done) - -#ppc64 striding, use with NUM_CPUS=8 - -#stride 1 -#EXTRA_OPTS=$(for a in $(seq 0 2 15); do echo -n "-a ${a} "; done) -#stride 2 -#EXTRA_OPTS=$(for a in $(seq 0 4 31); do echo -n "-a ${a} "; done) -#stride 4 -#EXTRA_OPTS=$(for a in $(seq 0 8 63); do echo -n "-a ${a} "; done) -#stride 8 -#EXTRA_OPTS=$(for a in $(seq 0 16 127); do echo -n "-a ${a} "; done) - -#Vary update fraction -#x: vary update fraction from 0 to 0.0001 - #fix number of readers and reader C.S. length, vary delay between updates -#y: ops/s - -rm -f runall.log -rm -fr runall.detail.log - -#setting gc each 32768. ** UPDATE FOR YOUR ARCHITECTURE BASED ON PHASE 1 RESULT ** -EXTRA_OPTS="${EXTRA_OPTS} -b 32768" - -#Test scalability : -# x: vary number of readers from 0 to num cpus -# y: ops/s -# 0 writer. - -echo Executing scalability test - -NR_WRITERS=0 -DURATION=10 - -rm -f scalability.log - -for NR_READERS in $(seq 1 ${NUM_CPUS}); do - echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS}| tee -a scalability.log" >> runall.log - ./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS}| tee -a scalability.log -done - - diff --git a/tests/runpaul-phase4.sh b/tests/runpaul-phase4.sh deleted file mode 100755 index ede402c..0000000 --- a/tests/runpaul-phase4.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/sh - -#run all tests - -#set to number of active CPUS -export NUM_CPUS=8 - -#extra options, e.g. for setting affinity on even CPUs : -#EXTRA_OPTS=$(for a in $(seq 0 2 127); do echo -n "-a ${a} "; done) - -#ppc64 striding, use with NUM_CPUS=8 - -rm -f *.log - -#stride 1 -export EXTRA_OPTS=$(for a in $(seq 0 2 15); do echo -n "-a ${a} "; done) -sh subphase4.sh $* -mkdir ppc64-8cores-stride1 -mv *.log ppc64-8cores-stride1/ - - -#stride 2 -export EXTRA_OPTS=$(for a in $(seq 0 4 31); do echo -n "-a ${a} "; done) -sh subphase4.sh $* -mkdir ppc64-8cores-stride2 -mv *.log ppc64-8cores-stride2/ - - -#stride 4 -export EXTRA_OPTS=$(for a in $(seq 0 8 63); do echo -n "-a ${a} "; done) -sh subphase4.sh $* -mkdir ppc64-8cores-stride4 -mv *.log ppc64-8cores-stride4/ - - -#stride 8 -export EXTRA_OPTS=$(for a in $(seq 0 16 127); do echo -n "-a ${a} "; done) -sh subphase4.sh $* -mkdir ppc64-8cores-stride8 -mv *.log ppc64-8cores-stride8/ diff --git a/tests/runpaul-phase5.sh b/tests/runpaul-phase5.sh deleted file mode 100644 index bb4bfe7..0000000 --- a/tests/runpaul-phase5.sh +++ /dev/null @@ -1,11 +0,0 @@ -# test run after write-size update - -sh runpaul-phase1.sh -mkdir runpaul-phase1 -mv *.log runpaul-phase1/ - -sh runpaul-phase2.sh -mkdir runpaul-phase2 -mv *.log runpaul-phase2/ - -sh runpaul-phase4.sh diff --git a/tests/runpaul-phase6.sh b/tests/runpaul-phase6.sh deleted file mode 100644 index 5f65072..0000000 --- a/tests/runpaul-phase6.sh +++ /dev/null @@ -1,7 +0,0 @@ -sh runpaul-phase1.sh -mkdir runpaul-phase1 -mv *.log runpaul-phase1/ - -sh runpaul-phase2.sh -mkdir runpaul-phase2 -mv *.log runpaul-phase2/ diff --git a/tests/runpaul-phase7.sh b/tests/runpaul-phase7.sh deleted file mode 100755 index 4c301da..0000000 --- a/tests/runpaul-phase7.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/sh - -#run all tests - -#set to number of active CPUS -export NUM_CPUS=64 -#export NUM_CPUS=8 - -#extra options, e.g. for setting affinity on even CPUs : -EXTRA_OPTS=$(for a in $(seq 0 2 127); do echo -n "-a ${a} "; done) -#EXTRA_OPTS=$(for a in $(seq 0 1 7); do echo -n "-a ${a} "; done) - -rm -f *.log - -# x: Vary writer C.S. length from 0 to 100 us -# y: reads/s -# 4 readers -# 4 writers - -echo Executing writer C.S. length test - -NR_READERS=$((${NUM_CPUS} / 2)) -NR_WRITERS=$((${NUM_CPUS} / 2)) -DURATION=10 -WDELAY=10 -#in loops. -WRITERCSLEN_ARRAY="0 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 524288 1048576 2097152" - -rm -f writercslen.log - -for WRITERCSLEN in ${WRITERCSLEN_ARRAY}; do - echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS} -d ${WDELAY} -e ${WRITERCSLEN} | tee -a writercslen.log" >> runall.log - ./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS} -d ${WDELAY} -e ${WRITERCSLEN} | tee -a writercslen.log -done - - - -mkdir ppc64-writercslen -mv *.log ppc64-writercslen/ -#mkdir xeon-writercslen -#mv *.log xeon-writercslen/ diff --git a/tests/runtests-batch.sh b/tests/runtests-batch.sh deleted file mode 100755 index 2da1401..0000000 --- a/tests/runtests-batch.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -. ./common.sh - -log_file="runall.detail.log" - -# Check if time bin is non-empty -if [ -n "$test_time_bin" ]; then - time_command="$test_time_bin -a -o $log_file" -else - time_command="" -fi - -#for a in test_urcu_gc test_urcu_gc_mb test_urcu_qsbr_gc; do -for a in test_urcu_gc; do - echo "./${a} $*" | tee -a "$log_file" - $time_command ./${a} $* -done - diff --git a/tests/runtests.sh b/tests/runtests.sh deleted file mode 100755 index 6001174..0000000 --- a/tests/runtests.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh - -. ./common.sh - -log_file="runall.detail.log" - -# Check if time bin is non-empty -if [ -n "$test_time_bin" ]; then - time_command="$test_time_bin -a -o $log_file" -else - time_command="" -fi - -for a in test_urcu_gc test_urcu_signal_gc test_urcu_mb_gc test_urcu_qsbr_gc \ - test_urcu_lgc test_urcu_signal_lgc test_urcu_mb_lgc test_urcu_qsbr_lgc \ - test_urcu test_urcu_signal test_urcu_mb test_urcu_qsbr \ - test_rwlock test_perthreadlock test_mutex; do - echo "./${a} $*" | tee -a "$log_file" - $time_command ./${a} $* -done - diff --git a/tests/subphase4.sh b/tests/subphase4.sh deleted file mode 100755 index d0d7587..0000000 --- a/tests/subphase4.sh +++ /dev/null @@ -1,101 +0,0 @@ -#!/bin/sh - -#run all tests - -#set to number of active CPUS -#NUM_CPUS=8 - -#extra options, e.g. for setting affinity on even CPUs : -#EXTRA_OPTS=$(for a in $(seq 0 2 127); do echo -n "-a ${a} "; done) - -#ppc64 striding, use with NUM_CPUS=8 - -#stride 1 -#EXTRA_OPTS=$(for a in $(seq 0 2 15); do echo -n "-a ${a} "; done) -#stride 2 -#EXTRA_OPTS=$(for a in $(seq 0 4 31); do echo -n "-a ${a} "; done) -#stride 4 -#EXTRA_OPTS=$(for a in $(seq 0 8 63); do echo -n "-a ${a} "; done) -#stride 8 -#EXTRA_OPTS=$(for a in $(seq 0 16 127); do echo -n "-a ${a} "; done) - -#Vary update fraction -#x: vary update fraction from 0 to 0.0001 - #fix number of readers and reader C.S. length, vary delay between updates -#y: ops/s - -rm -f runall.log -rm -fr runall.detail.log - - -echo Executing batch RCU test - -DURATION=10 -BATCH_ARRAY="1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 - 131072 262144" -NR_WRITERS=$((${NUM_CPUS} / 2)) - -rm -f batch-rcu.log - -NR_READERS=$((${NUM_CPUS} - ${NR_WRITERS})) -for BATCH_SIZE in ${BATCH_ARRAY}; do - echo "./runtests-batch.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d 0 -b ${BATCH_SIZE} ${EXTRA_OPTS} | tee -a batch-rcu.log" >> runall.log - ./runtests-batch.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d 0 -b ${BATCH_SIZE} ${EXTRA_OPTS} | tee -a batch-rcu.log -done - -#setting gc each 4096. ** UPDATE FOR YOUR ARCHITECTURE BASED ON TEST ABOVE ** -EXTRA_OPTS="${EXTRA_OPTS} -b 32768" - -echo Executing update fraction test - -DURATION=10 -WDELAY_ARRAY="0 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 - 65536 131072 262144 524288 1048576 2097152 4194304 8388608 - 16777216 33554432 67108864 134217728" -NR_WRITERS=$((${NUM_CPUS} / 2)) - -rm -f update-fraction.log - -NR_READERS=$((${NUM_CPUS} - ${NR_WRITERS})) -for WDELAY in ${WDELAY_ARRAY}; do - echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d ${WDELAY} ${EXTRA_OPTS} | tee -a update-fraction.log" >> runall.log - ./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} -d ${WDELAY} ${EXTRA_OPTS} | tee -a update-fraction.log -done - -#Test scalability : -# x: vary number of readers from 0 to num cpus -# y: ops/s -# 0 writer. - -echo Executing scalability test - -NR_WRITERS=0 -DURATION=10 - -rm -f scalability.log - -for NR_READERS in $(seq 1 ${NUM_CPUS}); do - echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS}| tee -a scalability.log" >> runall.log - ./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS}| tee -a scalability.log -done - - -# x: Vary reader C.S. length from 0 to 100 us -# y: ops/s -# 8 readers -# 0 writers - -echo Executing reader C.S. length test - -NR_READERS=${NUM_CPUS} -NR_WRITERS=0 -DURATION=10 -#in loops. -READERCSLEN_ARRAY="0 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 524288 1048576 2097152" - -rm -f readercslen.log - -for READERCSLEN in ${READERCSLEN_ARRAY}; do - echo "./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS} -c ${READERCSLEN} | tee -a readercslen.log" >> runall.log - ./runtests.sh ${NR_READERS} ${NR_WRITERS} ${DURATION} ${EXTRA_OPTS} -c ${READERCSLEN} | tee -a readercslen.log -done diff --git a/tests/test_cycles_per_loop.c b/tests/test_cycles_per_loop.c deleted file mode 100644 index 6ff100b..0000000 --- a/tests/test_cycles_per_loop.c +++ /dev/null @@ -1,44 +0,0 @@ -/* - * test_cycles_per_loop.c - * - * Userspace RCU library - test cycles per loop - * - * Copyright February 2009 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include - -#define NR_LOOPS 1000000UL - -static inline void loop_sleep(unsigned long loops) -{ - while (loops-- != 0) - caa_cpu_relax(); -} - -int main() -{ - cycles_t time1, time2; - - time1 = caa_get_cycles(); - loop_sleep(NR_LOOPS); - time2 = caa_get_cycles(); - printf("CPU clock cycles per loop: %g\n", (time2 - time1) / - (double)NR_LOOPS); - return 0; -} diff --git a/tests/test_looplen.c b/tests/test_looplen.c deleted file mode 100644 index 16674e7..0000000 --- a/tests/test_looplen.c +++ /dev/null @@ -1,74 +0,0 @@ -/* - * test_looplen.c - * - * Userspace RCU library - test program - * - * Copyright February 2009 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#ifndef DYNAMIC_LINK_TEST -#define _LGPL_SOURCE -#else -#define debug_yield_read() -#endif -#include - -static inline void loop_sleep(unsigned long loops) -{ - while (loops-- != 0) - caa_cpu_relax(); -} - -#define LOOPS 1048576 -#define TESTS 10 - -int main(int argc, char **argv) -{ - unsigned long i; - cycles_t time1, time2; - cycles_t time_tot = 0; - double cpl; - - for (i = 0; i < TESTS; i++) { - time1 = caa_get_cycles(); - loop_sleep(LOOPS); - time2 = caa_get_cycles(); - time_tot += time2 - time1; - } - cpl = ((double)time_tot) / (double)TESTS / (double)LOOPS; - - printf("CALIBRATION : %g cycles per loop\n", cpl); - printf("time_tot = %llu, LOOPS = %d, TESTS = %d\n", - time_tot, LOOPS, TESTS); - - return 0; -} diff --git a/tests/test_mutex.c b/tests/test_mutex.c deleted file mode 100644 index 2f25454..0000000 --- a/tests/test_mutex.c +++ /dev/null @@ -1,399 +0,0 @@ -/* - * test_urcu.c - * - * Userspace RCU library - test program - * - * Copyright February 2009 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include "../config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include "cpuset.h" -#include "thread-id.h" - -/* hardcoded number of CPUs */ -#define NR_CPUS 16384 - -#ifndef DYNAMIC_LINK_TEST -#define _LGPL_SOURCE -#else -#define debug_yield_read() -#endif -#include - -struct test_array { - int a; -}; - -static pthread_mutex_t lock; - -static volatile int test_go, test_stop; - -static unsigned long wdelay; - -static volatile struct test_array test_array = { 8 }; - -static unsigned long duration; - -/* read-side C.S. duration, in loops */ -static unsigned long rduration; - -/* write-side C.S. duration, in loops */ -static unsigned long wduration; - -static inline void loop_sleep(unsigned long loops) -{ - while (loops-- != 0) - caa_cpu_relax(); -} - -static int verbose_mode; - -#define printf_verbose(fmt, args...) \ - do { \ - if (verbose_mode) \ - printf(fmt, args); \ - } while (0) - -static unsigned int cpu_affinities[NR_CPUS]; -static unsigned int next_aff = 0; -static int use_affinity = 0; - -pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; - -static void set_affinity(void) -{ -#if HAVE_SCHED_SETAFFINITY - cpu_set_t mask; - int cpu, ret; -#endif /* HAVE_SCHED_SETAFFINITY */ - - if (!use_affinity) - return; - -#if HAVE_SCHED_SETAFFINITY - ret = pthread_mutex_lock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } - cpu = cpu_affinities[next_aff++]; - ret = pthread_mutex_unlock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } - CPU_ZERO(&mask); - CPU_SET(cpu, &mask); -#if SCHED_SETAFFINITY_ARGS == 2 - sched_setaffinity(0, &mask); -#else - sched_setaffinity(0, sizeof(mask), &mask); -#endif -#endif /* HAVE_SCHED_SETAFFINITY */ -} - -/* - * returns 0 if test should end. - */ -static int test_duration_write(void) -{ - return !test_stop; -} - -static int test_duration_read(void) -{ - return !test_stop; -} - -static DEFINE_URCU_TLS(unsigned long long, nr_writes); -static DEFINE_URCU_TLS(unsigned long long, nr_reads); - -static -unsigned long long __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *tot_nr_writes; -static -unsigned long long __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *tot_nr_reads; - -static unsigned int nr_readers; -static unsigned int nr_writers; - -pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; - -void rcu_copy_mutex_lock(void) -{ - int ret; - ret = pthread_mutex_lock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } -} - -void rcu_copy_mutex_unlock(void) -{ - int ret; - - ret = pthread_mutex_unlock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } -} - -void *thr_reader(void *data) -{ - unsigned long tidx = (unsigned long)data; - - printf_verbose("thread_begin %s, tid %lu\n", - "reader", urcu_get_thread_id()); - - set_affinity(); - - while (!test_go) - { - } - - for (;;) { - pthread_mutex_lock(&lock); - assert(test_array.a == 8); - if (caa_unlikely(rduration)) - loop_sleep(rduration); - pthread_mutex_unlock(&lock); - URCU_TLS(nr_reads)++; - if (caa_unlikely(!test_duration_read())) - break; - } - - tot_nr_reads[tidx] = URCU_TLS(nr_reads); - printf_verbose("thread_end %s, tid %lu\n", - "reader", urcu_get_thread_id()); - return ((void*)1); - -} - -void *thr_writer(void *data) -{ - unsigned long wtidx = (unsigned long)data; - - printf_verbose("thread_begin %s, tid %lu\n", - "writer", urcu_get_thread_id()); - - set_affinity(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - pthread_mutex_lock(&lock); - test_array.a = 0; - test_array.a = 8; - if (caa_unlikely(wduration)) - loop_sleep(wduration); - pthread_mutex_unlock(&lock); - URCU_TLS(nr_writes)++; - if (caa_unlikely(!test_duration_write())) - break; - if (caa_unlikely(wdelay)) - loop_sleep(wdelay); - } - - printf_verbose("thread_end %s, tid %lu\n", - "writer", urcu_get_thread_id()); - tot_nr_writes[wtidx] = URCU_TLS(nr_writes); - return ((void*)2); -} - -void show_usage(int argc, char **argv) -{ - printf("Usage : %s nr_readers nr_writers duration (s) \n", - argv[0]); - printf("OPTIONS:\n"); -#ifdef DEBUG_YIELD - printf(" [-r] [-w] (yield reader and/or writer)\n"); -#endif - printf(" [-d delay] (writer period (us))\n"); - printf(" [-c duration] (reader C.S. duration (in loops))\n"); - printf(" [-e duration] (writer C.S. duration (in loops))\n"); - printf(" [-v] (verbose output)\n"); - printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); - printf("\n"); -} - -int main(int argc, char **argv) -{ - int err; - pthread_t *tid_reader, *tid_writer; - void *tret; - unsigned long long *count_reader, *count_writer; - unsigned long long tot_reads = 0, tot_writes = 0; - int i, a; - - if (argc < 4) { - show_usage(argc, argv); - return -1; - } - cmm_smp_mb(); - - err = sscanf(argv[1], "%u", &nr_readers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[2], "%u", &nr_writers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[3], "%lu", &duration); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - for (i = 4; i < argc; i++) { - if (argv[i][0] != '-') - continue; - switch (argv[i][1]) { -#ifdef DEBUG_YIELD - case 'r': - yield_active |= YIELD_READ; - break; - case 'w': - yield_active |= YIELD_WRITE; - break; -#endif - case 'a': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - a = atoi(argv[++i]); - cpu_affinities[next_aff++] = a; - use_affinity = 1; - printf_verbose("Adding CPU %d affinity\n", a); - break; - case 'c': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - rduration = atol(argv[++i]); - break; - case 'd': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wdelay = atol(argv[++i]); - break; - case 'e': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wduration = atol(argv[++i]); - break; - case 'v': - verbose_mode = 1; - break; - } - } - - printf_verbose("running test for %lu seconds, %u readers, %u writers.\n", - duration, nr_readers, nr_writers); - printf_verbose("Writer delay : %lu loops.\n", wdelay); - printf_verbose("Reader duration : %lu loops.\n", rduration); - printf_verbose("thread %-6s, tid %lu\n", - "main", urcu_get_thread_id()); - - tid_reader = calloc(nr_readers, sizeof(*tid_reader)); - tid_writer = calloc(nr_writers, sizeof(*tid_writer)); - count_reader = calloc(nr_readers, sizeof(*count_reader)); - count_writer = calloc(nr_writers, sizeof(*count_writer)); - tot_nr_reads = calloc(nr_readers, sizeof(*tot_nr_reads)); - tot_nr_writes = calloc(nr_writers, sizeof(*tot_nr_writes)); - - next_aff = 0; - - for (i = 0; i < nr_readers; i++) { - err = pthread_create(&tid_reader[i], NULL, thr_reader, - (void *)(long)i); - if (err != 0) - exit(1); - } - for (i = 0; i < nr_writers; i++) { - err = pthread_create(&tid_writer[i], NULL, thr_writer, - (void *)(long)i); - if (err != 0) - exit(1); - } - - cmm_smp_mb(); - - test_go = 1; - - sleep(duration); - - test_stop = 1; - - for (i = 0; i < nr_readers; i++) { - err = pthread_join(tid_reader[i], &tret); - if (err != 0) - exit(1); - tot_reads += tot_nr_reads[i]; - } - for (i = 0; i < nr_writers; i++) { - err = pthread_join(tid_writer[i], &tret); - if (err != 0) - exit(1); - tot_writes += tot_nr_writes[i]; - } - - printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads, - tot_writes); - printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu " - "nr_writers %3u " - "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n", - argv[0], duration, nr_readers, rduration, wduration, - nr_writers, wdelay, tot_reads, tot_writes, - tot_reads + tot_writes); - - free(tid_reader); - free(tid_writer); - free(count_reader); - free(count_writer); - free(tot_nr_reads); - free(tot_nr_writes); - return 0; -} diff --git a/tests/test_perthreadlock.c b/tests/test_perthreadlock.c deleted file mode 100644 index 39e2feb..0000000 --- a/tests/test_perthreadlock.c +++ /dev/null @@ -1,410 +0,0 @@ -/* - * test_urcu.c - * - * Userspace RCU library - test program - * - * Copyright February 2009 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include "../config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include "cpuset.h" -#include "thread-id.h" - -/* hardcoded number of CPUs */ -#define NR_CPUS 16384 - -#ifndef DYNAMIC_LINK_TEST -#define _LGPL_SOURCE -#else -#define debug_yield_read() -#endif -#include - -struct test_array { - int a; -}; - -struct per_thread_lock { - pthread_mutex_t lock; -} __attribute__((aligned(CAA_CACHE_LINE_SIZE))); /* cache-line aligned */ - -static struct per_thread_lock *per_thread_lock; - -static volatile int test_go, test_stop; - -static unsigned long wdelay; - -static volatile struct test_array test_array = { 8 }; - -static unsigned long duration; - -/* read-side C.S. duration, in loops */ -static unsigned long rduration; - -/* write-side C.S. duration, in loops */ -static unsigned long wduration; - -static inline void loop_sleep(unsigned long loops) -{ - while (loops-- != 0) - caa_cpu_relax(); -} - -static int verbose_mode; - -#define printf_verbose(fmt, args...) \ - do { \ - if (verbose_mode) \ - printf(fmt, args); \ - } while (0) - -static unsigned int cpu_affinities[NR_CPUS]; -static unsigned int next_aff = 0; -static int use_affinity = 0; - -pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; - -static void set_affinity(void) -{ -#if HAVE_SCHED_SETAFFINITY - cpu_set_t mask; - int cpu, ret; -#endif /* HAVE_SCHED_SETAFFINITY */ - - if (!use_affinity) - return; - -#if HAVE_SCHED_SETAFFINITY - ret = pthread_mutex_lock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } - cpu = cpu_affinities[next_aff++]; - ret = pthread_mutex_unlock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } - CPU_ZERO(&mask); - CPU_SET(cpu, &mask); -#if SCHED_SETAFFINITY_ARGS == 2 - sched_setaffinity(0, &mask); -#else - sched_setaffinity(0, sizeof(mask), &mask); -#endif -#endif /* HAVE_SCHED_SETAFFINITY */ -} - -/* - * returns 0 if test should end. - */ -static int test_duration_write(void) -{ - return !test_stop; -} - -static int test_duration_read(void) -{ - return !test_stop; -} - -static DEFINE_URCU_TLS(unsigned long long, nr_writes); -static DEFINE_URCU_TLS(unsigned long long, nr_reads); - -static -unsigned long long __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *tot_nr_writes; -static -unsigned long long __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *tot_nr_reads; - -static unsigned int nr_readers; -static unsigned int nr_writers; - -pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; - -void rcu_copy_mutex_lock(void) -{ - int ret; - ret = pthread_mutex_lock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } -} - -void rcu_copy_mutex_unlock(void) -{ - int ret; - - ret = pthread_mutex_unlock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } -} - -void *thr_reader(void *data) -{ - unsigned long tidx = (unsigned long)data; - - printf_verbose("thread_begin %s, tid %lu\n", - "reader", urcu_get_thread_id()); - - set_affinity(); - - while (!test_go) - { - } - - for (;;) { - pthread_mutex_lock(&per_thread_lock[tidx].lock); - assert(test_array.a == 8); - if (caa_unlikely(rduration)) - loop_sleep(rduration); - pthread_mutex_unlock(&per_thread_lock[tidx].lock); - URCU_TLS(nr_reads)++; - if (caa_unlikely(!test_duration_read())) - break; - } - - tot_nr_reads[tidx] = URCU_TLS(nr_reads); - printf_verbose("thread_end %s, tid %lu\n", - "reader", urcu_get_thread_id()); - return ((void*)1); - -} - -void *thr_writer(void *data) -{ - unsigned long wtidx = (unsigned long)data; - long tidx; - - printf_verbose("thread_begin %s, tid %lu\n", - "writer", urcu_get_thread_id()); - - set_affinity(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - for (tidx = 0; tidx < nr_readers; tidx++) { - pthread_mutex_lock(&per_thread_lock[tidx].lock); - } - test_array.a = 0; - test_array.a = 8; - if (caa_unlikely(wduration)) - loop_sleep(wduration); - for (tidx = (long)nr_readers - 1; tidx >= 0; tidx--) { - pthread_mutex_unlock(&per_thread_lock[tidx].lock); - } - URCU_TLS(nr_writes)++; - if (caa_unlikely(!test_duration_write())) - break; - if (caa_unlikely(wdelay)) - loop_sleep(wdelay); - } - - printf_verbose("thread_end %s, tid %lu\n", - "writer", urcu_get_thread_id()); - tot_nr_writes[wtidx] = URCU_TLS(nr_writes); - return ((void*)2); -} - -void show_usage(int argc, char **argv) -{ - printf("Usage : %s nr_readers nr_writers duration (s) \n", - argv[0]); - printf("OPTIONS:\n"); -#ifdef DEBUG_YIELD - printf(" [-r] [-w] (yield reader and/or writer)\n"); -#endif - printf(" [-d delay] (writer period (us))\n"); - printf(" [-c duration] (reader C.S. duration (in loops))\n"); - printf(" [-e duration] (writer C.S. duration (in loops))\n"); - printf(" [-v] (verbose output)\n"); - printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); - printf("\n"); -} - -int main(int argc, char **argv) -{ - int err; - pthread_t *tid_reader, *tid_writer; - void *tret; - unsigned long long tot_reads = 0, tot_writes = 0; - int i, a; - - if (argc < 4) { - show_usage(argc, argv); - return -1; - } - cmm_smp_mb(); - - err = sscanf(argv[1], "%u", &nr_readers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[2], "%u", &nr_writers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[3], "%lu", &duration); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - for (i = 4; i < argc; i++) { - if (argv[i][0] != '-') - continue; - switch (argv[i][1]) { -#ifdef DEBUG_YIELD - case 'r': - yield_active |= YIELD_READ; - break; - case 'w': - yield_active |= YIELD_WRITE; - break; -#endif - case 'a': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - a = atoi(argv[++i]); - cpu_affinities[next_aff++] = a; - use_affinity = 1; - printf_verbose("Adding CPU %d affinity\n", a); - break; - case 'c': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - rduration = atol(argv[++i]); - break; - case 'd': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wdelay = atol(argv[++i]); - break; - case 'e': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wduration = atol(argv[++i]); - break; - case 'v': - verbose_mode = 1; - break; - } - } - - printf_verbose("running test for %lu seconds, %u readers, %u writers.\n", - duration, nr_readers, nr_writers); - printf_verbose("Writer delay : %lu loops.\n", wdelay); - printf_verbose("Reader duration : %lu loops.\n", rduration); - printf_verbose("thread %-6s, tid %lu\n", - "main", urcu_get_thread_id()); - - tid_reader = calloc(nr_readers, sizeof(*tid_reader)); - tid_writer = calloc(nr_writers, sizeof(*tid_writer)); - tot_nr_reads = calloc(nr_readers, sizeof(*tot_nr_reads)); - tot_nr_writes = calloc(nr_writers, sizeof(*tot_nr_writes)); - per_thread_lock = calloc(nr_readers, sizeof(*per_thread_lock)); - for (i = 0; i < nr_readers; i++) { - err = pthread_mutex_init(&per_thread_lock[i].lock, NULL); - if (err != 0) - exit(1); - } - - next_aff = 0; - - for (i = 0; i < nr_readers; i++) { - err = pthread_create(&tid_reader[i], NULL, thr_reader, - (void *)(long)i); - if (err != 0) - exit(1); - } - for (i = 0; i < nr_writers; i++) { - err = pthread_create(&tid_writer[i], NULL, thr_writer, - (void *)(long)i); - if (err != 0) - exit(1); - } - - cmm_smp_mb(); - - test_go = 1; - - sleep(duration); - - test_stop = 1; - - for (i = 0; i < nr_readers; i++) { - err = pthread_join(tid_reader[i], &tret); - if (err != 0) - exit(1); - tot_reads += tot_nr_reads[i]; - } - for (i = 0; i < nr_writers; i++) { - err = pthread_join(tid_writer[i], &tret); - if (err != 0) - exit(1); - tot_writes += tot_nr_writes[i]; - } - - printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads, - tot_writes); - printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu " - "nr_writers %3u " - "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n", - argv[0], duration, nr_readers, rduration, wduration, - nr_writers, wdelay, tot_reads, tot_writes, - tot_reads + tot_writes); - - free(tid_reader); - free(tid_writer); - free(tot_nr_reads); - free(tot_nr_writes); - free(per_thread_lock); - return 0; -} diff --git a/tests/test_perthreadlock_timing.c b/tests/test_perthreadlock_timing.c deleted file mode 100644 index 97bdd04..0000000 --- a/tests/test_perthreadlock_timing.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * test_perthreadloc_timing.c - * - * Per thread locks - test program - * - * Copyright February 2009 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "thread-id.h" - -#include - -struct test_array { - int a; -}; - -static struct test_array test_array = { 8 }; - -struct per_thread_lock { - pthread_mutex_t lock; -} __attribute__((aligned(CAA_CACHE_LINE_SIZE))); /* cache-line aligned */ - -static struct per_thread_lock *per_thread_lock; - -#define OUTER_READ_LOOP 200U -#define INNER_READ_LOOP 100000U -#define READ_LOOP ((unsigned long long)OUTER_READ_LOOP * INNER_READ_LOOP) - -#define OUTER_WRITE_LOOP 10U -#define INNER_WRITE_LOOP 200U -#define WRITE_LOOP ((unsigned long long)OUTER_WRITE_LOOP * INNER_WRITE_LOOP) - -static int num_read; -static int num_write; - -#define NR_READ num_read -#define NR_WRITE num_write - -static cycles_t __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *reader_time; -static cycles_t __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *writer_time; - -void *thr_reader(void *arg) -{ - int i, j; - cycles_t time1, time2; - long tidx = (long)arg; - - printf("thread_begin %s, tid %lu\n", - "reader", urcu_get_thread_id()); - sleep(2); - - time1 = caa_get_cycles(); - for (i = 0; i < OUTER_READ_LOOP; i++) { - for (j = 0; j < INNER_READ_LOOP; j++) { - pthread_mutex_lock(&per_thread_lock[tidx].lock); - assert(test_array.a == 8); - pthread_mutex_unlock(&per_thread_lock[tidx].lock); - } - } - time2 = caa_get_cycles(); - - reader_time[tidx] = time2 - time1; - - sleep(2); - printf("thread_end %s, tid %lu\n", - "reader", urcu_get_thread_id()); - return ((void*)1); - -} - -void *thr_writer(void *arg) -{ - int i, j; - long tidx; - cycles_t time1, time2; - - printf("thread_begin %s, tid %lu\n", - "writer", urcu_get_thread_id()); - sleep(2); - - for (i = 0; i < OUTER_WRITE_LOOP; i++) { - for (j = 0; j < INNER_WRITE_LOOP; j++) { - time1 = caa_get_cycles(); - for (tidx = 0; tidx < NR_READ; tidx++) { - pthread_mutex_lock(&per_thread_lock[tidx].lock); - } - test_array.a = 8; - for (tidx = NR_READ - 1; tidx >= 0; tidx--) { - pthread_mutex_unlock(&per_thread_lock[tidx].lock); - } - time2 = caa_get_cycles(); - writer_time[(unsigned long)arg] += time2 - time1; - usleep(1); - } - } - - printf("thread_end %s, tid %lu\n", - "writer", urcu_get_thread_id()); - return ((void*)2); -} - -int main(int argc, char **argv) -{ - int err; - pthread_t *tid_reader, *tid_writer; - void *tret; - int i; - cycles_t tot_rtime = 0; - cycles_t tot_wtime = 0; - - if (argc < 2) { - printf("Usage : %s nr_readers nr_writers\n", argv[0]); - exit(-1); - } - num_read = atoi(argv[1]); - num_write = atoi(argv[2]); - - reader_time = calloc(num_read, sizeof(*reader_time)); - writer_time = calloc(num_write, sizeof(*writer_time)); - tid_reader = calloc(num_read, sizeof(*tid_reader)); - tid_writer = calloc(num_write, sizeof(*tid_writer)); - - printf("thread %-6s, tid %lu\n", - "main", urcu_get_thread_id()); - - per_thread_lock = malloc(sizeof(struct per_thread_lock) * NR_READ); - - for (i = 0; i < NR_READ; i++) { - pthread_mutex_init(&per_thread_lock[i].lock, NULL); - } - for (i = 0; i < NR_READ; i++) { - err = pthread_create(&tid_reader[i], NULL, thr_reader, - (void *)(long)i); - if (err != 0) - exit(1); - } - for (i = 0; i < NR_WRITE; i++) { - err = pthread_create(&tid_writer[i], NULL, thr_writer, - (void *)(long)i); - if (err != 0) - exit(1); - } - - sleep(10); - - for (i = 0; i < NR_READ; i++) { - err = pthread_join(tid_reader[i], &tret); - if (err != 0) - exit(1); - tot_rtime += reader_time[i]; - } - for (i = 0; i < NR_WRITE; i++) { - err = pthread_join(tid_writer[i], &tret); - if (err != 0) - exit(1); - tot_wtime += writer_time[i]; - } - printf("Time per read : %g cycles\n", - (double)tot_rtime / ((double)NR_READ * (double)READ_LOOP)); - printf("Time per write : %g cycles\n", - (double)tot_wtime / ((double)NR_WRITE * (double)WRITE_LOOP)); - free(per_thread_lock); - - free(reader_time); - free(writer_time); - free(tid_reader); - free(tid_writer); - - return 0; -} diff --git a/tests/test_rwlock.c b/tests/test_rwlock.c deleted file mode 100644 index 5a0e8c1..0000000 --- a/tests/test_rwlock.c +++ /dev/null @@ -1,391 +0,0 @@ -/* - * test_urcu.c - * - * Userspace RCU library - test program - * - * Copyright February 2009 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include "../config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include "cpuset.h" -#include "thread-id.h" - -/* hardcoded number of CPUs */ -#define NR_CPUS 16384 - -#ifndef DYNAMIC_LINK_TEST -#define _LGPL_SOURCE -#else -#define debug_yield_read() -#endif -#include - -struct test_array { - int a; -}; - -pthread_rwlock_t lock = PTHREAD_RWLOCK_INITIALIZER; - -static volatile int test_go, test_stop; - -static unsigned long wdelay; - -static volatile struct test_array test_array = { 8 }; - -static unsigned long duration; - -/* read-side C.S. duration, in loops */ -static unsigned long rduration; - -/* write-side C.S. duration, in loops */ -static unsigned long wduration; - -static inline void loop_sleep(unsigned long loops) -{ - while (loops-- != 0) - caa_cpu_relax(); -} - -static int verbose_mode; - -#define printf_verbose(fmt, args...) \ - do { \ - if (verbose_mode) \ - printf(fmt, args); \ - } while (0) - -static unsigned int cpu_affinities[NR_CPUS]; -static unsigned int next_aff = 0; -static int use_affinity = 0; - -pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; - -static void set_affinity(void) -{ -#if HAVE_SCHED_SETAFFINITY - cpu_set_t mask; - int cpu, ret; -#endif /* HAVE_SCHED_SETAFFINITY */ - - if (!use_affinity) - return; - -#if HAVE_SCHED_SETAFFINITY - ret = pthread_mutex_lock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } - cpu = cpu_affinities[next_aff++]; - ret = pthread_mutex_unlock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } - - CPU_ZERO(&mask); - CPU_SET(cpu, &mask); -#if SCHED_SETAFFINITY_ARGS == 2 - sched_setaffinity(0, &mask); -#else - sched_setaffinity(0, sizeof(mask), &mask); -#endif -#endif /* HAVE_SCHED_SETAFFINITY */ -} - -/* - * returns 0 if test should end. - */ -static int test_duration_write(void) -{ - return !test_stop; -} - -static int test_duration_read(void) -{ - return !test_stop; -} - -static DEFINE_URCU_TLS(unsigned long long, nr_writes); -static DEFINE_URCU_TLS(unsigned long long, nr_reads); - -static unsigned int nr_readers; -static unsigned int nr_writers; - -pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; - -void rcu_copy_mutex_lock(void) -{ - int ret; - ret = pthread_mutex_lock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } -} - -void rcu_copy_mutex_unlock(void) -{ - int ret; - - ret = pthread_mutex_unlock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } -} - -void *thr_reader(void *_count) -{ - unsigned long long *count = _count; - - printf_verbose("thread_begin %s, tid %lu\n", - "reader", urcu_get_thread_id()); - - set_affinity(); - - while (!test_go) - { - } - - for (;;) { - pthread_rwlock_rdlock(&lock); - assert(test_array.a == 8); - if (caa_unlikely(rduration)) - loop_sleep(rduration); - pthread_rwlock_unlock(&lock); - URCU_TLS(nr_reads)++; - if (caa_unlikely(!test_duration_read())) - break; - } - - *count = URCU_TLS(nr_reads); - printf_verbose("thread_end %s, tid %lu\n", - "reader", urcu_get_thread_id()); - return ((void*)1); - -} - -void *thr_writer(void *_count) -{ - unsigned long long *count = _count; - - printf_verbose("thread_begin %s, tid %lu\n", - "writer", urcu_get_thread_id()); - - set_affinity(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - pthread_rwlock_wrlock(&lock); - test_array.a = 0; - test_array.a = 8; - if (caa_unlikely(wduration)) - loop_sleep(wduration); - pthread_rwlock_unlock(&lock); - URCU_TLS(nr_writes)++; - if (caa_unlikely(!test_duration_write())) - break; - if (caa_unlikely(wdelay)) - loop_sleep(wdelay); - } - - printf_verbose("thread_end %s, tid %lu\n", - "writer", urcu_get_thread_id()); - *count = URCU_TLS(nr_writes); - return ((void*)2); -} - -void show_usage(int argc, char **argv) -{ - printf("Usage : %s nr_readers nr_writers duration (s) \n", - argv[0]); - printf("OPTIONS:\n"); -#ifdef DEBUG_YIELD - printf(" [-r] [-w] (yield reader and/or writer)\n"); -#endif - printf(" [-d delay] (writer period (us))\n"); - printf(" [-c duration] (reader C.S. duration (in loops))\n"); - printf(" [-e duration] (writer C.S. duration (in loops))\n"); - printf(" [-v] (verbose output)\n"); - printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); - printf("\n"); -} - -int main(int argc, char **argv) -{ - int err; - pthread_t *tid_reader, *tid_writer; - void *tret; - unsigned long long *count_reader, *count_writer; - unsigned long long tot_reads = 0, tot_writes = 0; - int i, a; - - if (argc < 4) { - show_usage(argc, argv); - return -1; - } - cmm_smp_mb(); - - err = sscanf(argv[1], "%u", &nr_readers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[2], "%u", &nr_writers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[3], "%lu", &duration); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - for (i = 4; i < argc; i++) { - if (argv[i][0] != '-') - continue; - switch (argv[i][1]) { -#ifdef DEBUG_YIELD - case 'r': - yield_active |= YIELD_READ; - break; - case 'w': - yield_active |= YIELD_WRITE; - break; -#endif - case 'a': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - a = atoi(argv[++i]); - cpu_affinities[next_aff++] = a; - use_affinity = 1; - printf_verbose("Adding CPU %d affinity\n", a); - break; - case 'c': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - rduration = atol(argv[++i]); - break; - case 'd': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wdelay = atol(argv[++i]); - break; - case 'e': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wduration = atol(argv[++i]); - break; - case 'v': - verbose_mode = 1; - break; - } - } - - printf_verbose("running test for %lu seconds, %u readers, %u writers.\n", - duration, nr_readers, nr_writers); - printf_verbose("Writer delay : %lu loops.\n", wdelay); - printf_verbose("Reader duration : %lu loops.\n", rduration); - printf_verbose("thread %-6s, tid %lu\n", - "main", urcu_get_thread_id()); - - tid_reader = calloc(nr_readers, sizeof(*tid_reader)); - tid_writer = calloc(nr_writers, sizeof(*tid_writer)); - count_reader = calloc(nr_readers, sizeof(*count_reader)); - count_writer = calloc(nr_writers, sizeof(*count_writer)); - - next_aff = 0; - - for (i = 0; i < nr_readers; i++) { - err = pthread_create(&tid_reader[i], NULL, thr_reader, - &count_reader[i]); - if (err != 0) - exit(1); - } - for (i = 0; i < nr_writers; i++) { - err = pthread_create(&tid_writer[i], NULL, thr_writer, - &count_writer[i]); - if (err != 0) - exit(1); - } - - cmm_smp_mb(); - - test_go = 1; - - sleep(duration); - - test_stop = 1; - - for (i = 0; i < nr_readers; i++) { - err = pthread_join(tid_reader[i], &tret); - if (err != 0) - exit(1); - tot_reads += count_reader[i]; - } - for (i = 0; i < nr_writers; i++) { - err = pthread_join(tid_writer[i], &tret); - if (err != 0) - exit(1); - tot_writes += count_writer[i]; - } - - printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads, - tot_writes); - printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu " - "nr_writers %3u " - "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n", - argv[0], duration, nr_readers, rduration, wduration, - nr_writers, wdelay, tot_reads, tot_writes, - tot_reads + tot_writes); - - free(tid_reader); - free(tid_writer); - free(count_reader); - free(count_writer); - return 0; -} diff --git a/tests/test_rwlock_timing.c b/tests/test_rwlock_timing.c deleted file mode 100644 index d916071..0000000 --- a/tests/test_rwlock_timing.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * test_urcu.c - * - * Userspace RCU library - test program - * - * Copyright February 2009 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "thread-id.h" - -#include - -struct test_array { - int a; -}; - -pthread_rwlock_t lock = PTHREAD_RWLOCK_INITIALIZER; - -static struct test_array test_array = { 8 }; - -#define OUTER_READ_LOOP 200U -#define INNER_READ_LOOP 100000U -#define READ_LOOP ((unsigned long long)OUTER_READ_LOOP * INNER_READ_LOOP) - -#define OUTER_WRITE_LOOP 10U -#define INNER_WRITE_LOOP 200U -#define WRITE_LOOP ((unsigned long long)OUTER_WRITE_LOOP * INNER_WRITE_LOOP) - -static int num_read; -static int num_write; - -#define NR_READ num_read -#define NR_WRITE num_write - -static cycles_t __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *reader_time; -static cycles_t __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *writer_time; - -void *thr_reader(void *arg) -{ - int i, j; - cycles_t time1, time2; - - printf("thread_begin %s, tid %lu\n", - "reader", urcu_get_thread_id()); - sleep(2); - - time1 = caa_get_cycles(); - for (i = 0; i < OUTER_READ_LOOP; i++) { - for (j = 0; j < INNER_READ_LOOP; j++) { - pthread_rwlock_rdlock(&lock); - assert(test_array.a == 8); - pthread_rwlock_unlock(&lock); - } - } - time2 = caa_get_cycles(); - - reader_time[(unsigned long)arg] = time2 - time1; - - sleep(2); - printf("thread_end %s, tid %lu\n", - "reader", urcu_get_thread_id()); - return ((void*)1); - -} - -void *thr_writer(void *arg) -{ - int i, j; - cycles_t time1, time2; - - printf("thread_begin %s, tid %lu\n", - "writer", urcu_get_thread_id()); - sleep(2); - - for (i = 0; i < OUTER_WRITE_LOOP; i++) { - for (j = 0; j < INNER_WRITE_LOOP; j++) { - time1 = caa_get_cycles(); - pthread_rwlock_wrlock(&lock); - test_array.a = 8; - pthread_rwlock_unlock(&lock); - time2 = caa_get_cycles(); - writer_time[(unsigned long)arg] += time2 - time1; - usleep(1); - } - } - - printf("thread_end %s, tid %lu\n", - "writer", urcu_get_thread_id()); - return ((void*)2); -} - -int main(int argc, char **argv) -{ - int err; - pthread_t *tid_reader, *tid_writer; - void *tret; - int i; - cycles_t tot_rtime = 0; - cycles_t tot_wtime = 0; - - if (argc < 2) { - printf("Usage : %s nr_readers nr_writers\n", argv[0]); - exit(-1); - } - num_read = atoi(argv[1]); - num_write = atoi(argv[2]); - - reader_time = calloc(num_read, sizeof(*reader_time)); - writer_time = calloc(num_write, sizeof(*writer_time)); - tid_reader = calloc(num_read, sizeof(*tid_reader)); - tid_writer = calloc(num_write, sizeof(*tid_writer)); - - printf("thread %-6s, tid %lu\n", - "main", urcu_get_thread_id()); - - for (i = 0; i < NR_READ; i++) { - err = pthread_create(&tid_reader[i], NULL, thr_reader, - (void *)(long)i); - if (err != 0) - exit(1); - } - for (i = 0; i < NR_WRITE; i++) { - err = pthread_create(&tid_writer[i], NULL, thr_writer, - (void *)(long)i); - if (err != 0) - exit(1); - } - - sleep(10); - - for (i = 0; i < NR_READ; i++) { - err = pthread_join(tid_reader[i], &tret); - if (err != 0) - exit(1); - tot_rtime += reader_time[i]; - } - for (i = 0; i < NR_WRITE; i++) { - err = pthread_join(tid_writer[i], &tret); - if (err != 0) - exit(1); - tot_wtime += writer_time[i]; - } - printf("Time per read : %g cycles\n", - (double)tot_rtime / ((double)NR_READ * (double)READ_LOOP)); - printf("Time per write : %g cycles\n", - (double)tot_wtime / ((double)NR_WRITE * (double)WRITE_LOOP)); - - free(reader_time); - free(writer_time); - free(tid_reader); - free(tid_writer); - - return 0; -} diff --git a/tests/test_uatomic.c b/tests/test_uatomic.c deleted file mode 100644 index 804ce7b..0000000 --- a/tests/test_uatomic.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * test_uatomic.c - * - * Userspace RCU library - test atomic operations - * - * Copyright February 2009 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include - -struct testvals { -#ifdef UATOMIC_HAS_ATOMIC_BYTE - unsigned char c; -#endif -#ifdef UATOMIC_HAS_ATOMIC_SHORT - unsigned short s; -#endif - unsigned int i; - unsigned long l; -}; - -static struct testvals vals; - -#define do_test(ptr) \ -do { \ - __typeof__(*(ptr)) v; \ - \ - uatomic_add(ptr, 10); \ - assert(uatomic_read(ptr) == 10); \ - uatomic_add(ptr, -11UL); \ - assert(uatomic_read(ptr) == (__typeof__(*(ptr)))-1UL); \ - v = uatomic_cmpxchg(ptr, -1UL, 22); \ - assert(uatomic_read(ptr) == 22); \ - assert(v == (__typeof__(*(ptr)))-1UL); \ - v = uatomic_cmpxchg(ptr, 33, 44); \ - assert(uatomic_read(ptr) == 22); \ - assert(v == 22); \ - v = uatomic_xchg(ptr, 55); \ - assert(uatomic_read(ptr) == 55); \ - assert(v == 22); \ - uatomic_set(ptr, 22); \ - uatomic_inc(ptr); \ - assert(uatomic_read(ptr) == 23); \ - uatomic_dec(ptr); \ - assert(uatomic_read(ptr) == 22); \ - v = uatomic_add_return(ptr, 74); \ - assert(v == 96); \ - assert(uatomic_read(ptr) == 96); \ - uatomic_or(ptr, 58); \ - assert(uatomic_read(ptr) == 122); \ - v = uatomic_sub_return(ptr, 1); \ - assert(v == 121); \ - uatomic_sub(ptr, (unsigned int) 2); \ - assert(uatomic_read(ptr) == 119); \ - uatomic_inc(ptr); \ - uatomic_inc(ptr); \ - assert(uatomic_read(ptr) == 121); \ - uatomic_and(ptr, 129); \ - assert(uatomic_read(ptr) == 1); \ -} while (0) - -int main(int argc, char **argv) -{ -#ifdef UATOMIC_HAS_ATOMIC_BYTE - do_test(&vals.c); -#endif -#ifdef UATOMIC_HAS_ATOMIC_SHORT - do_test(&vals.s); -#endif - do_test(&vals.i); - do_test(&vals.l); - printf("Atomic ops test OK\n"); - - return 0; -} diff --git a/tests/test_urcu.c b/tests/test_urcu.c deleted file mode 100644 index 2764878..0000000 --- a/tests/test_urcu.c +++ /dev/null @@ -1,404 +0,0 @@ -/* - * test_urcu.c - * - * Userspace RCU library - test program - * - * Copyright February 2009 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include "../config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include "cpuset.h" -#include "thread-id.h" - -/* hardcoded number of CPUs */ -#define NR_CPUS 16384 - -#ifndef DYNAMIC_LINK_TEST -#define _LGPL_SOURCE -#else -#define rcu_debug_yield_read() -#endif -#include - -static volatile int test_go, test_stop; - -static unsigned long wdelay; - -static int *test_rcu_pointer; - -static unsigned long duration; - -/* read-side C.S. duration, in loops */ -static unsigned long rduration; - -/* write-side C.S. duration, in loops */ -static unsigned long wduration; - -static inline void loop_sleep(unsigned long loops) -{ - while (loops-- != 0) - caa_cpu_relax(); -} - -static int verbose_mode; - -#define printf_verbose(fmt, args...) \ - do { \ - if (verbose_mode) \ - printf(fmt, args); \ - } while (0) - -static unsigned int cpu_affinities[NR_CPUS]; -static unsigned int next_aff = 0; -static int use_affinity = 0; - -pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; - -static void set_affinity(void) -{ -#if HAVE_SCHED_SETAFFINITY - cpu_set_t mask; - int cpu, ret; -#endif /* HAVE_SCHED_SETAFFINITY */ - - if (!use_affinity) - return; - -#if HAVE_SCHED_SETAFFINITY - ret = pthread_mutex_lock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } - cpu = cpu_affinities[next_aff++]; - ret = pthread_mutex_unlock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } - - CPU_ZERO(&mask); - CPU_SET(cpu, &mask); -#if SCHED_SETAFFINITY_ARGS == 2 - sched_setaffinity(0, &mask); -#else - sched_setaffinity(0, sizeof(mask), &mask); -#endif -#endif /* HAVE_SCHED_SETAFFINITY */ -} - -/* - * returns 0 if test should end. - */ -static int test_duration_write(void) -{ - return !test_stop; -} - -static int test_duration_read(void) -{ - return !test_stop; -} - -static DEFINE_URCU_TLS(unsigned long long, nr_writes); -static DEFINE_URCU_TLS(unsigned long long, nr_reads); - -static unsigned int nr_readers; -static unsigned int nr_writers; - -pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; - -void rcu_copy_mutex_lock(void) -{ - int ret; - ret = pthread_mutex_lock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } -} - -void rcu_copy_mutex_unlock(void) -{ - int ret; - - ret = pthread_mutex_unlock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } -} - -void *thr_reader(void *_count) -{ - unsigned long long *count = _count; - int *local_ptr; - - printf_verbose("thread_begin %s, tid %lu\n", - "reader", urcu_get_thread_id()); - - set_affinity(); - - rcu_register_thread(); - assert(!rcu_read_ongoing()); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - rcu_read_lock(); - assert(rcu_read_ongoing()); - local_ptr = rcu_dereference(test_rcu_pointer); - rcu_debug_yield_read(); - if (local_ptr) - assert(*local_ptr == 8); - if (caa_unlikely(rduration)) - loop_sleep(rduration); - rcu_read_unlock(); - URCU_TLS(nr_reads)++; - if (caa_unlikely(!test_duration_read())) - break; - } - - rcu_unregister_thread(); - - /* test extra thread registration */ - rcu_register_thread(); - rcu_unregister_thread(); - - *count = URCU_TLS(nr_reads); - printf_verbose("thread_end %s, tid %lu\n", - "reader", urcu_get_thread_id()); - return ((void*)1); - -} - -void *thr_writer(void *_count) -{ - unsigned long long *count = _count; - int *new, *old; - - printf_verbose("thread_begin %s, tid %lu\n", - "writer", urcu_get_thread_id()); - - set_affinity(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - new = malloc(sizeof(int)); - assert(new); - *new = 8; - old = rcu_xchg_pointer(&test_rcu_pointer, new); - if (caa_unlikely(wduration)) - loop_sleep(wduration); - synchronize_rcu(); - if (old) - *old = 0; - free(old); - URCU_TLS(nr_writes)++; - if (caa_unlikely(!test_duration_write())) - break; - if (caa_unlikely(wdelay)) - loop_sleep(wdelay); - } - - printf_verbose("thread_end %s, tid %lu\n", - "writer", urcu_get_thread_id()); - *count = URCU_TLS(nr_writes); - return ((void*)2); -} - -void show_usage(int argc, char **argv) -{ - printf("Usage : %s nr_readers nr_writers duration (s) \n", - argv[0]); - printf("OPTIONS:\n"); -#ifdef DEBUG_YIELD - printf(" [-r] [-w] (yield reader and/or writer)\n"); -#endif - printf(" [-d delay] (writer period (us))\n"); - printf(" [-c duration] (reader C.S. duration (in loops))\n"); - printf(" [-e duration] (writer C.S. duration (in loops))\n"); - printf(" [-v] (verbose output)\n"); - printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); - printf("\n"); -} - -int main(int argc, char **argv) -{ - int err; - pthread_t *tid_reader, *tid_writer; - void *tret; - unsigned long long *count_reader, *count_writer; - unsigned long long tot_reads = 0, tot_writes = 0; - int i, a; - - if (argc < 4) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[1], "%u", &nr_readers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[2], "%u", &nr_writers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[3], "%lu", &duration); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - for (i = 4; i < argc; i++) { - if (argv[i][0] != '-') - continue; - switch (argv[i][1]) { -#ifdef DEBUG_YIELD - case 'r': - rcu_yield_active |= RCU_YIELD_READ; - break; - case 'w': - rcu_yield_active |= RCU_YIELD_WRITE; - break; -#endif - case 'a': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - a = atoi(argv[++i]); - cpu_affinities[next_aff++] = a; - use_affinity = 1; - printf_verbose("Adding CPU %d affinity\n", a); - break; - case 'c': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - rduration = atol(argv[++i]); - break; - case 'd': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wdelay = atol(argv[++i]); - break; - case 'e': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wduration = atol(argv[++i]); - break; - case 'v': - verbose_mode = 1; - break; - } - } - - printf_verbose("running test for %lu seconds, %u readers, %u writers.\n", - duration, nr_readers, nr_writers); - printf_verbose("Writer delay : %lu loops.\n", wdelay); - printf_verbose("Reader duration : %lu loops.\n", rduration); - printf_verbose("thread %-6s, tid %lu\n", - "main", urcu_get_thread_id()); - - tid_reader = calloc(nr_readers, sizeof(*tid_reader)); - tid_writer = calloc(nr_writers, sizeof(*tid_writer)); - count_reader = calloc(nr_readers, sizeof(*count_reader)); - count_writer = calloc(nr_writers, sizeof(*count_writer)); - - next_aff = 0; - - for (i = 0; i < nr_readers; i++) { - err = pthread_create(&tid_reader[i], NULL, thr_reader, - &count_reader[i]); - if (err != 0) - exit(1); - } - for (i = 0; i < nr_writers; i++) { - err = pthread_create(&tid_writer[i], NULL, thr_writer, - &count_writer[i]); - if (err != 0) - exit(1); - } - - cmm_smp_mb(); - - test_go = 1; - - sleep(duration); - - test_stop = 1; - - for (i = 0; i < nr_readers; i++) { - err = pthread_join(tid_reader[i], &tret); - if (err != 0) - exit(1); - tot_reads += count_reader[i]; - } - for (i = 0; i < nr_writers; i++) { - err = pthread_join(tid_writer[i], &tret); - if (err != 0) - exit(1); - tot_writes += count_writer[i]; - } - - printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads, - tot_writes); - printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu " - "nr_writers %3u " - "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n", - argv[0], duration, nr_readers, rduration, wduration, - nr_writers, wdelay, tot_reads, tot_writes, - tot_reads + tot_writes); - free(test_rcu_pointer); - free(tid_reader); - free(tid_writer); - free(count_reader); - free(count_writer); - return 0; -} diff --git a/tests/test_urcu_assign.c b/tests/test_urcu_assign.c deleted file mode 100644 index dc6bf2f..0000000 --- a/tests/test_urcu_assign.c +++ /dev/null @@ -1,439 +0,0 @@ -/* - * test_urcu_assign.c - * - * Userspace RCU library - test program - * - * Copyright February 2009 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include "../config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include "cpuset.h" -#include "thread-id.h" - -/* hardcoded number of CPUs */ -#define NR_CPUS 16384 - -#ifndef DYNAMIC_LINK_TEST -#define _LGPL_SOURCE -#else -#define rcu_debug_yield_read() -#endif -#include - -struct test_array { - int a; -}; - -static volatile int test_go, test_stop; - -static unsigned long wdelay; - -static struct test_array *test_rcu_pointer; - -static unsigned long duration; - -/* read-side C.S. duration, in loops */ -static unsigned long rduration; - -/* write-side C.S. duration, in loops */ -static unsigned long wduration; - -static inline void loop_sleep(unsigned long loops) -{ - while (loops-- != 0) - caa_cpu_relax(); -} - -static int verbose_mode; - -#define printf_verbose(fmt, args...) \ - do { \ - if (verbose_mode) \ - printf(fmt, args); \ - } while (0) - -static unsigned int cpu_affinities[NR_CPUS]; -static unsigned int next_aff = 0; -static int use_affinity = 0; - -pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; - -static void set_affinity(void) -{ -#if HAVE_SCHED_SETAFFINITY - cpu_set_t mask; - int cpu, ret; -#endif /* HAVE_SCHED_SETAFFINITY */ - - if (!use_affinity) - return; - -#if HAVE_SCHED_SETAFFINITY - ret = pthread_mutex_lock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } - cpu = cpu_affinities[next_aff++]; - ret = pthread_mutex_unlock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } - - CPU_ZERO(&mask); - CPU_SET(cpu, &mask); -#if SCHED_SETAFFINITY_ARGS == 2 - sched_setaffinity(0, &mask); -#else - sched_setaffinity(0, sizeof(mask), &mask); -#endif -#endif /* HAVE_SCHED_SETAFFINITY */ -} - -/* - * returns 0 if test should end. - */ -static int test_duration_write(void) -{ - return !test_stop; -} - -static int test_duration_read(void) -{ - return !test_stop; -} - -static DEFINE_URCU_TLS(unsigned long long, nr_writes); -static DEFINE_URCU_TLS(unsigned long long, nr_reads); - -static unsigned int nr_readers; -static unsigned int nr_writers; - -pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; - -void rcu_copy_mutex_lock(void) -{ - int ret; - ret = pthread_mutex_lock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } -} - -void rcu_copy_mutex_unlock(void) -{ - int ret; - - ret = pthread_mutex_unlock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } -} - -/* - * malloc/free are reusing memory areas too quickly, which does not let us - * test races appropriately. Use a large circular array for allocations. - * ARRAY_SIZE is larger than nr_writers, and we keep the mutex across - * both alloc and free, which insures we never run over our tail. - */ -#define ARRAY_SIZE (1048576 * nr_writers) -#define ARRAY_POISON 0xDEADBEEF -static int array_index; -static struct test_array *test_array; - -static struct test_array *test_array_alloc(void) -{ - struct test_array *ret; - int index; - - index = array_index % ARRAY_SIZE; - assert(test_array[index].a == ARRAY_POISON || - test_array[index].a == 0); - ret = &test_array[index]; - array_index++; - if (array_index == ARRAY_SIZE) - array_index = 0; - return ret; -} - -static void test_array_free(struct test_array *ptr) -{ - if (!ptr) - return; - ptr->a = ARRAY_POISON; -} - -void *thr_reader(void *_count) -{ - unsigned long long *count = _count; - struct test_array *local_ptr; - - printf_verbose("thread_begin %s, tid %lu\n", - "reader", urcu_get_thread_id()); - - set_affinity(); - - rcu_register_thread(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - rcu_read_lock(); - local_ptr = rcu_dereference(test_rcu_pointer); - rcu_debug_yield_read(); - if (local_ptr) - assert(local_ptr->a == 8); - if (caa_unlikely(rduration)) - loop_sleep(rduration); - rcu_read_unlock(); - URCU_TLS(nr_reads)++; - if (caa_unlikely(!test_duration_read())) - break; - } - - rcu_unregister_thread(); - - *count = URCU_TLS(nr_reads); - printf_verbose("thread_end %s, tid %lu\n", - "reader", urcu_get_thread_id()); - return ((void*)1); - -} - -void *thr_writer(void *_count) -{ - unsigned long long *count = _count; - struct test_array *new, *old; - - printf_verbose("thread_begin %s, tid %lu\n", - "writer", urcu_get_thread_id()); - - set_affinity(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - rcu_copy_mutex_lock(); - new = test_array_alloc(); - new->a = 8; - old = test_rcu_pointer; - rcu_assign_pointer(test_rcu_pointer, new); - if (caa_unlikely(wduration)) - loop_sleep(wduration); - synchronize_rcu(); - if (old) - old->a = 0; - test_array_free(old); - rcu_copy_mutex_unlock(); - URCU_TLS(nr_writes)++; - if (caa_unlikely(!test_duration_write())) - break; - if (caa_unlikely(wdelay)) - loop_sleep(wdelay); - } - - printf_verbose("thread_end %s, tid %lu\n", - "writer", urcu_get_thread_id()); - *count = URCU_TLS(nr_writes); - return ((void*)2); -} - -void show_usage(int argc, char **argv) -{ - printf("Usage : %s nr_readers nr_writers duration (s) \n", - argv[0]); - printf("OPTIONS:\n"); -#ifdef DEBUG_YIELD - printf(" [-r] [-w] (yield reader and/or writer)\n"); -#endif - printf(" [-d delay] (writer period (us))\n"); - printf(" [-c duration] (reader C.S. duration (in loops))\n"); - printf(" [-e duration] (writer C.S. duration (in loops))\n"); - printf(" [-v] (verbose output)\n"); - printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); - printf("\n"); -} - -int main(int argc, char **argv) -{ - int err; - pthread_t *tid_reader, *tid_writer; - void *tret; - unsigned long long *count_reader, *count_writer; - unsigned long long tot_reads = 0, tot_writes = 0; - int i, a; - - if (argc < 4) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[1], "%u", &nr_readers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[2], "%u", &nr_writers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[3], "%lu", &duration); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - for (i = 4; i < argc; i++) { - if (argv[i][0] != '-') - continue; - switch (argv[i][1]) { -#ifdef DEBUG_YIELD - case 'r': - rcu_yield_active |= RCU_YIELD_READ; - break; - case 'w': - rcu_yield_active |= RCU_YIELD_WRITE; - break; -#endif - case 'a': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - a = atoi(argv[++i]); - cpu_affinities[next_aff++] = a; - use_affinity = 1; - printf_verbose("Adding CPU %d affinity\n", a); - break; - case 'c': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - rduration = atol(argv[++i]); - break; - case 'd': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wdelay = atol(argv[++i]); - break; - case 'e': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wduration = atol(argv[++i]); - break; - case 'v': - verbose_mode = 1; - break; - } - } - - printf_verbose("running test for %lu seconds, %u readers, %u writers.\n", - duration, nr_readers, nr_writers); - printf_verbose("Writer delay : %lu loops.\n", wdelay); - printf_verbose("Reader duration : %lu loops.\n", rduration); - printf_verbose("thread %-6s, tid %lu\n", - "main", urcu_get_thread_id()); - - test_array = calloc(1, sizeof(*test_array) * ARRAY_SIZE); - tid_reader = calloc(nr_readers, sizeof(*tid_reader)); - tid_writer = calloc(nr_writers, sizeof(*tid_writer)); - count_reader = calloc(nr_readers, sizeof(*count_reader)); - count_writer = calloc(nr_writers, sizeof(*count_writer)); - - next_aff = 0; - - for (i = 0; i < nr_readers; i++) { - err = pthread_create(&tid_reader[i], NULL, thr_reader, - &count_reader[i]); - if (err != 0) - exit(1); - } - for (i = 0; i < nr_writers; i++) { - err = pthread_create(&tid_writer[i], NULL, thr_writer, - &count_writer[i]); - if (err != 0) - exit(1); - } - - cmm_smp_mb(); - - test_go = 1; - - sleep(duration); - - test_stop = 1; - - for (i = 0; i < nr_readers; i++) { - err = pthread_join(tid_reader[i], &tret); - if (err != 0) - exit(1); - tot_reads += count_reader[i]; - } - for (i = 0; i < nr_writers; i++) { - err = pthread_join(tid_writer[i], &tret); - if (err != 0) - exit(1); - tot_writes += count_writer[i]; - } - - printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads, - tot_writes); - printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu " - "nr_writers %3u " - "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n", - argv[0], duration, nr_readers, rduration, wduration, - nr_writers, wdelay, tot_reads, tot_writes, - tot_reads + tot_writes); - test_array_free(test_rcu_pointer); - free(test_array); - free(tid_reader); - free(tid_writer); - free(count_reader); - free(count_writer); - return 0; -} diff --git a/tests/test_urcu_bp.c b/tests/test_urcu_bp.c deleted file mode 100644 index 9ec5f3e..0000000 --- a/tests/test_urcu_bp.c +++ /dev/null @@ -1,399 +0,0 @@ -/* - * test_urcu.c - * - * Userspace RCU library - test program - * - * Copyright February 2009 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include "../config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include "cpuset.h" -#include "thread-id.h" - -/* hardcoded number of CPUs */ -#define NR_CPUS 16384 - -#ifndef DYNAMIC_LINK_TEST -#define _LGPL_SOURCE -#else -#define rcu_debug_yield_read() -#endif -#include - -static volatile int test_go, test_stop; - -static unsigned long wdelay; - -static int *test_rcu_pointer; - -static unsigned long duration; - -/* read-side C.S. duration, in loops */ -static unsigned long rduration; - -/* write-side C.S. duration, in loops */ -static unsigned long wduration; - -static inline void loop_sleep(unsigned long loops) -{ - while (loops-- != 0) - caa_cpu_relax(); -} - -static int verbose_mode; - -#define printf_verbose(fmt, args...) \ - do { \ - if (verbose_mode) \ - printf(fmt, args); \ - } while (0) - -static unsigned int cpu_affinities[NR_CPUS]; -static unsigned int next_aff = 0; -static int use_affinity = 0; - -pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; - -static void set_affinity(void) -{ -#if HAVE_SCHED_SETAFFINITY - cpu_set_t mask; - int cpu, ret; -#endif /* HAVE_SCHED_SETAFFINITY */ - - if (!use_affinity) - return; - -#if HAVE_SCHED_SETAFFINITY - ret = pthread_mutex_lock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } - cpu = cpu_affinities[next_aff++]; - ret = pthread_mutex_unlock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } - - CPU_ZERO(&mask); - CPU_SET(cpu, &mask); -#if SCHED_SETAFFINITY_ARGS == 2 - sched_setaffinity(0, &mask); -#else - sched_setaffinity(0, sizeof(mask), &mask); -#endif -#endif /* HAVE_SCHED_SETAFFINITY */ -} - -/* - * returns 0 if test should end. - */ -static int test_duration_write(void) -{ - return !test_stop; -} - -static int test_duration_read(void) -{ - return !test_stop; -} - -static DEFINE_URCU_TLS(unsigned long long, nr_writes); -static DEFINE_URCU_TLS(unsigned long long, nr_reads); - -static unsigned int nr_readers; -static unsigned int nr_writers; - -pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; - -void rcu_copy_mutex_lock(void) -{ - int ret; - ret = pthread_mutex_lock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } -} - -void rcu_copy_mutex_unlock(void) -{ - int ret; - - ret = pthread_mutex_unlock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } -} - -void *thr_reader(void *_count) -{ - unsigned long long *count = _count; - int *local_ptr; - - printf_verbose("thread_begin %s, tid %lu\n", - "reader", urcu_get_thread_id()); - - set_affinity(); - - rcu_register_thread(); - assert(!rcu_read_ongoing()); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - rcu_read_lock(); - assert(rcu_read_ongoing()); - local_ptr = rcu_dereference(test_rcu_pointer); - rcu_debug_yield_read(); - if (local_ptr) - assert(*local_ptr == 8); - if (caa_unlikely(rduration)) - loop_sleep(rduration); - rcu_read_unlock(); - URCU_TLS(nr_reads)++; - if (caa_unlikely(!test_duration_read())) - break; - } - - rcu_unregister_thread(); - - *count = URCU_TLS(nr_reads); - printf_verbose("thread_end %s, tid %lu\n", - "reader", urcu_get_thread_id()); - return ((void*)1); - -} - -void *thr_writer(void *_count) -{ - unsigned long long *count = _count; - int *new, *old; - - printf_verbose("thread_begin %s, tid %lu\n", - "writer", urcu_get_thread_id()); - - set_affinity(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - new = malloc(sizeof(int)); - *new = 8; - old = rcu_xchg_pointer(&test_rcu_pointer, new); - if (caa_unlikely(wduration)) - loop_sleep(wduration); - synchronize_rcu(); - if (old) - *old = 0; - free(old); - URCU_TLS(nr_writes)++; - if (caa_unlikely(!test_duration_write())) - break; - if (caa_unlikely(wdelay)) - loop_sleep(wdelay); - } - - printf_verbose("thread_end %s, tid %lu\n", - "writer", urcu_get_thread_id()); - *count = URCU_TLS(nr_writes); - return ((void*)2); -} - -void show_usage(int argc, char **argv) -{ - printf("Usage : %s nr_readers nr_writers duration (s) \n", - argv[0]); - printf("OPTIONS:\n"); -#ifdef DEBUG_YIELD - printf(" [-r] [-w] (yield reader and/or writer)\n"); -#endif - printf(" [-d delay] (writer period (us))\n"); - printf(" [-c duration] (reader C.S. duration (in loops))\n"); - printf(" [-e duration] (writer C.S. duration (in loops))\n"); - printf(" [-v] (verbose output)\n"); - printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); - printf("\n"); -} - -int main(int argc, char **argv) -{ - int err; - pthread_t *tid_reader, *tid_writer; - void *tret; - unsigned long long *count_reader, *count_writer; - unsigned long long tot_reads = 0, tot_writes = 0; - int i, a; - - if (argc < 4) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[1], "%u", &nr_readers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[2], "%u", &nr_writers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[3], "%lu", &duration); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - for (i = 4; i < argc; i++) { - if (argv[i][0] != '-') - continue; - switch (argv[i][1]) { -#ifdef DEBUG_YIELD - case 'r': - rcu_yield_active |= RCU_YIELD_READ; - break; - case 'w': - rcu_yield_active |= RCU_YIELD_WRITE; - break; -#endif - case 'a': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - a = atoi(argv[++i]); - cpu_affinities[next_aff++] = a; - use_affinity = 1; - printf_verbose("Adding CPU %d affinity\n", a); - break; - case 'c': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - rduration = atol(argv[++i]); - break; - case 'd': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wdelay = atol(argv[++i]); - break; - case 'e': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wduration = atol(argv[++i]); - break; - case 'v': - verbose_mode = 1; - break; - } - } - - printf_verbose("running test for %lu seconds, %u readers, %u writers.\n", - duration, nr_readers, nr_writers); - printf_verbose("Writer delay : %lu loops.\n", wdelay); - printf_verbose("Reader duration : %lu loops.\n", rduration); - printf_verbose("thread %-6s, tid %lu\n", - "main", urcu_get_thread_id()); - - tid_reader = calloc(nr_readers, sizeof(*tid_reader)); - tid_writer = calloc(nr_writers, sizeof(*tid_writer)); - count_reader = calloc(nr_readers, sizeof(*count_reader)); - count_writer = calloc(nr_writers, sizeof(*count_writer)); - - next_aff = 0; - - for (i = 0; i < nr_readers; i++) { - err = pthread_create(&tid_reader[i], NULL, thr_reader, - &count_reader[i]); - if (err != 0) - exit(1); - } - for (i = 0; i < nr_writers; i++) { - err = pthread_create(&tid_writer[i], NULL, thr_writer, - &count_writer[i]); - if (err != 0) - exit(1); - } - - cmm_smp_mb(); - - test_go = 1; - - sleep(duration); - - test_stop = 1; - - for (i = 0; i < nr_readers; i++) { - err = pthread_join(tid_reader[i], &tret); - if (err != 0) - exit(1); - tot_reads += count_reader[i]; - } - for (i = 0; i < nr_writers; i++) { - err = pthread_join(tid_writer[i], &tret); - if (err != 0) - exit(1); - tot_writes += count_writer[i]; - } - - printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads, - tot_writes); - printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu " - "nr_writers %3u " - "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n", - argv[0], duration, nr_readers, rduration, wduration, - nr_writers, wdelay, tot_reads, tot_writes, - tot_reads + tot_writes); - free(test_rcu_pointer); - free(tid_reader); - free(tid_writer); - free(count_reader); - free(count_writer); - return 0; -} diff --git a/tests/test_urcu_defer.c b/tests/test_urcu_defer.c deleted file mode 100644 index 4e785c4..0000000 --- a/tests/test_urcu_defer.c +++ /dev/null @@ -1,426 +0,0 @@ -/* - * test_urcu_defer.c - * - * Userspace RCU library - test program (with automatic reclamation) - * - * Copyright February 2009 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include "../config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include "cpuset.h" -#include "thread-id.h" - -/* hardcoded number of CPUs */ -#define NR_CPUS 16384 - -#ifndef DYNAMIC_LINK_TEST -#define _LGPL_SOURCE -#else -#define rcu_debug_yield_read() -#endif -#include -#include - -struct test_array { - int a; -}; - -static volatile int test_go, test_stop; - -static unsigned long wdelay; - -static struct test_array *test_rcu_pointer; - -static unsigned long duration; - -/* read-side C.S. duration, in loops */ -static unsigned long rduration; - -/* write-side C.S. duration, in loops */ -static unsigned long wduration; - -static inline void loop_sleep(unsigned long loops) -{ - while (loops-- != 0) - caa_cpu_relax(); -} - -static int verbose_mode; - -#define printf_verbose(fmt, args...) \ - do { \ - if (verbose_mode) \ - printf(fmt, args); \ - } while (0) - -static unsigned int cpu_affinities[NR_CPUS]; -static unsigned int next_aff = 0; -static int use_affinity = 0; - -pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; - -static void set_affinity(void) -{ -#if HAVE_SCHED_SETAFFINITY - cpu_set_t mask; - int cpu, ret; -#endif /* HAVE_SCHED_SETAFFINITY */ - - if (!use_affinity) - return; - -#if HAVE_SCHED_SETAFFINITY - ret = pthread_mutex_lock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } - cpu = cpu_affinities[next_aff++]; - ret = pthread_mutex_unlock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } - - CPU_ZERO(&mask); - CPU_SET(cpu, &mask); -#if SCHED_SETAFFINITY_ARGS == 2 - sched_setaffinity(0, &mask); -#else - sched_setaffinity(0, sizeof(mask), &mask); -#endif -#endif /* HAVE_SCHED_SETAFFINITY */ -} - -/* - * returns 0 if test should end. - */ -static int test_duration_write(void) -{ - return !test_stop; -} - -static int test_duration_read(void) -{ - return !test_stop; -} - -static DEFINE_URCU_TLS(unsigned long long, nr_writes); -static DEFINE_URCU_TLS(unsigned long long, nr_reads); - -static -unsigned long long __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *tot_nr_writes; - -static unsigned int nr_readers; -static unsigned int nr_writers; - -pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; - -void rcu_copy_mutex_lock(void) -{ - int ret; - ret = pthread_mutex_lock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } -} - -void rcu_copy_mutex_unlock(void) -{ - int ret; - - ret = pthread_mutex_unlock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } -} - -void *thr_reader(void *_count) -{ - unsigned long long *count = _count; - struct test_array *local_ptr; - - printf_verbose("thread_begin %s, tid %lu\n", - "reader", urcu_get_thread_id()); - - set_affinity(); - - rcu_register_thread(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - rcu_read_lock(); - local_ptr = rcu_dereference(test_rcu_pointer); - rcu_debug_yield_read(); - if (local_ptr) - assert(local_ptr->a == 8); - if (caa_unlikely(rduration)) - loop_sleep(rduration); - rcu_read_unlock(); - URCU_TLS(nr_reads)++; - if (caa_unlikely(!test_duration_read())) - break; - } - - rcu_unregister_thread(); - - *count = URCU_TLS(nr_reads); - printf_verbose("thread_end %s, tid %lu\n", - "reader", urcu_get_thread_id()); - return ((void*)1); - -} - -static void test_cb2(void *data) -{ -} - -static void test_cb1(void *data) -{ -} - -void *thr_writer(void *data) -{ - unsigned long wtidx = (unsigned long)data; - struct test_array *new, *old = NULL; - int ret; - - printf_verbose("thread_begin %s, tid %lu\n", - "writer", urcu_get_thread_id()); - - set_affinity(); - - ret = rcu_defer_register_thread(); - if (ret) { - printf("Error in rcu_defer_register_thread\n"); - exit(-1); - } - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - new = malloc(sizeof(*new)); - new->a = 8; - old = rcu_xchg_pointer(&test_rcu_pointer, new); - if (caa_unlikely(wduration)) - loop_sleep(wduration); - defer_rcu(free, old); - defer_rcu(test_cb1, old); - defer_rcu(test_cb1, (void *)-2L); - defer_rcu(test_cb1, (void *)-2L); - defer_rcu(test_cb1, old); - defer_rcu(test_cb2, (void *)-2L); - defer_rcu(test_cb2, (void *)-4L); - defer_rcu(test_cb2, (void *)-2L); - URCU_TLS(nr_writes)++; - if (caa_unlikely(!test_duration_write())) - break; - if (caa_unlikely(wdelay)) - loop_sleep(wdelay); - } - - rcu_defer_unregister_thread(); - - printf_verbose("thread_end %s, tid %lu\n", - "writer", urcu_get_thread_id()); - tot_nr_writes[wtidx] = URCU_TLS(nr_writes); - return ((void*)2); -} - -void show_usage(int argc, char **argv) -{ - printf("Usage : %s nr_readers nr_writers duration (s) \n", - argv[0]); - printf("OPTIONS:\n"); -#ifdef DEBUG_YIELD - printf(" [-r] [-w] (yield reader and/or writer)\n"); -#endif - printf(" [-d delay] (writer period (us))\n"); - printf(" [-c duration] (reader C.S. duration (in loops))\n"); - printf(" [-e duration] (writer C.S. duration (in loops))\n"); - printf(" [-v] (verbose output)\n"); - printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); - printf("\n"); -} - -int main(int argc, char **argv) -{ - int err; - pthread_t *tid_reader, *tid_writer; - void *tret; - unsigned long long *count_reader; - unsigned long long tot_reads = 0, tot_writes = 0; - int i, a; - - if (argc < 4) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[1], "%u", &nr_readers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[2], "%u", &nr_writers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[3], "%lu", &duration); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - for (i = 4; i < argc; i++) { - if (argv[i][0] != '-') - continue; - switch (argv[i][1]) { -#ifdef DEBUG_YIELD - case 'r': - rcu_yield_active |= RCU_YIELD_READ; - break; - case 'w': - rcu_yield_active |= RCU_YIELD_WRITE; - break; -#endif - case 'a': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - a = atoi(argv[++i]); - cpu_affinities[next_aff++] = a; - use_affinity = 1; - printf_verbose("Adding CPU %d affinity\n", a); - break; - case 'c': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - rduration = atol(argv[++i]); - break; - case 'd': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wdelay = atol(argv[++i]); - break; - case 'e': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wduration = atol(argv[++i]); - break; - case 'v': - verbose_mode = 1; - break; - } - } - - printf_verbose("running test for %lu seconds, %u readers, %u writers.\n", - duration, nr_readers, nr_writers); - printf_verbose("Writer delay : %lu loops.\n", wdelay); - printf_verbose("Reader duration : %lu loops.\n", rduration); - printf_verbose("thread %-6s, tid %lu\n", - "main", urcu_get_thread_id()); - - tid_reader = calloc(nr_readers, sizeof(*tid_reader)); - tid_writer = calloc(nr_writers, sizeof(*tid_writer)); - count_reader = calloc(nr_readers, sizeof(*count_reader)); - tot_nr_writes = calloc(nr_writers, sizeof(*tot_nr_writes)); - - next_aff = 0; - - for (i = 0; i < nr_readers; i++) { - err = pthread_create(&tid_reader[i], NULL, thr_reader, - &count_reader[i]); - if (err != 0) - exit(1); - } - for (i = 0; i < nr_writers; i++) { - err = pthread_create(&tid_writer[i], NULL, thr_writer, - (void *)(long)i); - if (err != 0) - exit(1); - } - - cmm_smp_mb(); - - test_go = 1; - - sleep(duration); - - test_stop = 1; - - for (i = 0; i < nr_readers; i++) { - err = pthread_join(tid_reader[i], &tret); - if (err != 0) - exit(1); - tot_reads += count_reader[i]; - } - for (i = 0; i < nr_writers; i++) { - err = pthread_join(tid_writer[i], &tret); - if (err != 0) - exit(1); - tot_writes += tot_nr_writes[i]; - } - - printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads, - tot_writes); - printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu " - "nr_writers %3u " - "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n", - argv[0], duration, nr_readers, rduration, wduration, - nr_writers, wdelay, tot_reads, tot_writes, - tot_reads + tot_writes); - free(tid_reader); - free(tid_writer); - free(count_reader); - free(tot_nr_writes); - - return 0; -} diff --git a/tests/test_urcu_fork.c b/tests/test_urcu_fork.c deleted file mode 100644 index 6e454b5..0000000 --- a/tests/test_urcu_fork.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * test_urcu_fork.c - * - * Userspace RCU library - test program (fork) - * - * Copyright February 2012 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include "../config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#ifndef DYNAMIC_LINK_TEST -#define _LGPL_SOURCE -#else -#define rcu_debug_yield_read() -#endif -#include - -struct test_node { - int somedata; - struct rcu_head head; -}; - -static void cb(struct rcu_head *head) -{ - struct test_node *node; - - fprintf(stderr, "rcu callback invoked in pid: %d\n", - (int) getpid()); - node = caa_container_of(head, struct test_node, head); - free(node); -} - -static void test_rcu(void) -{ - struct test_node *node; - - rcu_register_thread(); - - synchronize_rcu(); - - rcu_read_lock(); - rcu_read_unlock(); - - node = malloc(sizeof(*node)); - assert(node); - - call_rcu(&node->head, cb); - - synchronize_rcu(); - - rcu_unregister_thread(); -} - -int main(int argc, char **argv) -{ - pid_t pid; - int ret; - -#if 0 - /* pthread_atfork does not work with malloc/free in callbacks */ - ret = pthread_atfork(call_rcu_before_fork, - call_rcu_after_fork_parent, - call_rcu_after_fork_child); - if (ret) { - errno = ret; - perror("pthread_atfork"); - exit(EXIT_FAILURE); - } -#endif - - test_rcu(); - - synchronize_rcu(); - - fprintf(stderr, "%s parent pid: %d, before fork\n", - argv[0], (int) getpid()); - - call_rcu_before_fork(); - pid = fork(); - - if (pid == 0) { - /* child */ - call_rcu_after_fork_child(); - fprintf(stderr, "%s child pid: %d, after fork\n", - argv[0], (int) getpid()); - test_rcu(); - fprintf(stderr, "%s child pid: %d, after rcu test\n", - argv[0], (int) getpid()); - } else if (pid > 0) { - int status; - - /* parent */ - call_rcu_after_fork_parent(); - fprintf(stderr, "%s parent pid: %d, after fork\n", - argv[0], (int) getpid()); - test_rcu(); - fprintf(stderr, "%s parent pid: %d, after rcu test\n", - argv[0], (int) getpid()); - for (;;) { - pid = wait(&status); - if (WIFEXITED(status)) { - fprintf(stderr, "child %u exited normally with status %u\n", - pid, WEXITSTATUS(status)); - break; - } else if (WIFSIGNALED(status)) { - fprintf(stderr, "child %u was terminated by signal %u\n", - pid, WTERMSIG(status)); - break; - } else { - continue; - } - } - } else { - perror("fork"); - exit(EXIT_FAILURE); - } - exit(EXIT_SUCCESS); -} diff --git a/tests/test_urcu_gc.c b/tests/test_urcu_gc.c deleted file mode 100644 index 5c57f3f..0000000 --- a/tests/test_urcu_gc.c +++ /dev/null @@ -1,470 +0,0 @@ -/* - * test_urcu_gc.c - * - * Userspace RCU library - test program (with batch reclamation) - * - * Copyright February 2009 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include "../config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include "cpuset.h" -#include "thread-id.h" - -/* hardcoded number of CPUs */ -#define NR_CPUS 16384 - -#ifndef DYNAMIC_LINK_TEST -#define _LGPL_SOURCE -#else -#define rcu_debug_yield_read() -#endif -#include - -struct test_array { - int a; -}; - -static volatile int test_go, test_stop; - -static unsigned long wdelay; - -static struct test_array *test_rcu_pointer; - -static unsigned int reclaim_batch = 1; - -struct reclaim_queue { - void **queue; /* Beginning of queue */ - void **head; /* Insert position */ -}; - -static struct reclaim_queue *pending_reclaims; - -static unsigned long duration; - -/* read-side C.S. duration, in loops */ -static unsigned long rduration; - -/* write-side C.S. duration, in loops */ -static unsigned long wduration; - -static inline void loop_sleep(unsigned long loops) -{ - while (loops-- != 0) - caa_cpu_relax(); -} - -static int verbose_mode; - -#define printf_verbose(fmt, args...) \ - do { \ - if (verbose_mode) \ - printf(fmt, args); \ - } while (0) - -static unsigned int cpu_affinities[NR_CPUS]; -static unsigned int next_aff = 0; -static int use_affinity = 0; - -pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; - -static void set_affinity(void) -{ -#if HAVE_SCHED_SETAFFINITY - cpu_set_t mask; - int cpu, ret; -#endif /* HAVE_SCHED_SETAFFINITY */ - - if (!use_affinity) - return; - -#if HAVE_SCHED_SETAFFINITY - ret = pthread_mutex_lock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } - cpu = cpu_affinities[next_aff++]; - ret = pthread_mutex_unlock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } - - CPU_ZERO(&mask); - CPU_SET(cpu, &mask); -#if SCHED_SETAFFINITY_ARGS == 2 - sched_setaffinity(0, &mask); -#else - sched_setaffinity(0, sizeof(mask), &mask); -#endif -#endif /* HAVE_SCHED_SETAFFINITY */ -} - -/* - * returns 0 if test should end. - */ -static int test_duration_write(void) -{ - return !test_stop; -} - -static int test_duration_read(void) -{ - return !test_stop; -} - -static DEFINE_URCU_TLS(unsigned long long, nr_writes); -static DEFINE_URCU_TLS(unsigned long long, nr_reads); - -static -unsigned long long __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *tot_nr_writes; - -static unsigned int nr_readers; -static unsigned int nr_writers; - -pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; - -void rcu_copy_mutex_lock(void) -{ - int ret; - ret = pthread_mutex_lock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } -} - -void rcu_copy_mutex_unlock(void) -{ - int ret; - - ret = pthread_mutex_unlock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } -} - -void *thr_reader(void *_count) -{ - unsigned long long *count = _count; - struct test_array *local_ptr; - - printf_verbose("thread_begin %s, tid %lu\n", - "reader", urcu_get_thread_id()); - - set_affinity(); - - rcu_register_thread(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - rcu_read_lock(); - local_ptr = rcu_dereference(test_rcu_pointer); - rcu_debug_yield_read(); - if (local_ptr) - assert(local_ptr->a == 8); - if (caa_unlikely(rduration)) - loop_sleep(rduration); - rcu_read_unlock(); - URCU_TLS(nr_reads)++; - if (caa_unlikely(!test_duration_read())) - break; - } - - rcu_unregister_thread(); - - *count = URCU_TLS(nr_reads); - printf_verbose("thread_end %s, tid %lu\n", - "reader", urcu_get_thread_id()); - return ((void*)1); - -} - -static void rcu_gc_clear_queue(unsigned long wtidx) -{ - void **p; - - /* Wait for Q.S and empty queue */ - synchronize_rcu(); - - for (p = pending_reclaims[wtidx].queue; - p < pending_reclaims[wtidx].head; p++) { - /* poison */ - if (*p) - ((struct test_array *)*p)->a = 0; - free(*p); - } - pending_reclaims[wtidx].head = pending_reclaims[wtidx].queue; -} - -/* Using per-thread queue */ -static void rcu_gc_reclaim(unsigned long wtidx, void *old) -{ - /* Queue pointer */ - *pending_reclaims[wtidx].head = old; - pending_reclaims[wtidx].head++; - - if (caa_likely(pending_reclaims[wtidx].head - pending_reclaims[wtidx].queue - < reclaim_batch)) - return; - - rcu_gc_clear_queue(wtidx); -} - -void *thr_writer(void *data) -{ - unsigned long wtidx = (unsigned long)data; -#ifdef TEST_LOCAL_GC - struct test_array *old = NULL; -#else - struct test_array *new, *old; -#endif - - printf_verbose("thread_begin %s, tid %lu\n", - "writer", urcu_get_thread_id()); - - set_affinity(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { -#ifndef TEST_LOCAL_GC - new = malloc(sizeof(*new)); - new->a = 8; - old = rcu_xchg_pointer(&test_rcu_pointer, new); -#endif - if (caa_unlikely(wduration)) - loop_sleep(wduration); - rcu_gc_reclaim(wtidx, old); - URCU_TLS(nr_writes)++; - if (caa_unlikely(!test_duration_write())) - break; - if (caa_unlikely(wdelay)) - loop_sleep(wdelay); - } - - printf_verbose("thread_end %s, tid %lu\n", - "writer", urcu_get_thread_id()); - tot_nr_writes[wtidx] = URCU_TLS(nr_writes); - return ((void*)2); -} - -void show_usage(int argc, char **argv) -{ - printf("Usage : %s nr_readers nr_writers duration (s) \n", - argv[0]); - printf("OPTIONS:\n"); -#ifdef DEBUG_YIELD - printf(" [-r] [-w] (yield reader and/or writer)\n"); -#endif - printf(" [-d delay] (writer period (us))\n"); - printf(" [-c duration] (reader C.S. duration (in loops))\n"); - printf(" [-e duration] (writer C.S. duration (in loops))\n"); - printf(" [-v] (verbose output)\n"); - printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); - printf("\n"); -} - -int main(int argc, char **argv) -{ - int err; - pthread_t *tid_reader, *tid_writer; - void *tret; - unsigned long long *count_reader; - unsigned long long tot_reads = 0, tot_writes = 0; - int i, a; - - if (argc < 4) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[1], "%u", &nr_readers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[2], "%u", &nr_writers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[3], "%lu", &duration); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - for (i = 4; i < argc; i++) { - if (argv[i][0] != '-') - continue; - switch (argv[i][1]) { -#ifdef DEBUG_YIELD - case 'r': - rcu_yield_active |= RCU_YIELD_READ; - break; - case 'w': - rcu_yield_active |= RCU_YIELD_WRITE; - break; -#endif - case 'a': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - a = atoi(argv[++i]); - cpu_affinities[next_aff++] = a; - use_affinity = 1; - printf_verbose("Adding CPU %d affinity\n", a); - break; - case 'b': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - reclaim_batch = atol(argv[++i]); - break; - case 'c': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - rduration = atol(argv[++i]); - break; - case 'd': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wdelay = atol(argv[++i]); - break; - case 'e': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wduration = atol(argv[++i]); - break; - case 'v': - verbose_mode = 1; - break; - } - } - - printf_verbose("running test for %lu seconds, %u readers, %u writers.\n", - duration, nr_readers, nr_writers); - printf_verbose("Writer delay : %lu loops.\n", wdelay); - printf_verbose("Reader duration : %lu loops.\n", rduration); - printf_verbose("thread %-6s, tid %lu\n", - "main", urcu_get_thread_id()); - - tid_reader = calloc(nr_readers, sizeof(*tid_reader)); - tid_writer = calloc(nr_writers, sizeof(*tid_writer)); - count_reader = calloc(nr_readers, sizeof(*count_reader)); - tot_nr_writes = calloc(nr_writers, sizeof(*tot_nr_writes)); - pending_reclaims = calloc(nr_writers, sizeof(*pending_reclaims)); - if (reclaim_batch * sizeof(*pending_reclaims[i].queue) - < CAA_CACHE_LINE_SIZE) - for (i = 0; i < nr_writers; i++) - pending_reclaims[i].queue = calloc(1, CAA_CACHE_LINE_SIZE); - else - for (i = 0; i < nr_writers; i++) - pending_reclaims[i].queue = calloc(reclaim_batch, - sizeof(*pending_reclaims[i].queue)); - for (i = 0; i < nr_writers; i++) - pending_reclaims[i].head = pending_reclaims[i].queue; - - next_aff = 0; - - for (i = 0; i < nr_readers; i++) { - err = pthread_create(&tid_reader[i], NULL, thr_reader, - &count_reader[i]); - if (err != 0) - exit(1); - } - for (i = 0; i < nr_writers; i++) { - err = pthread_create(&tid_writer[i], NULL, thr_writer, - (void *)(long)i); - if (err != 0) - exit(1); - } - - cmm_smp_mb(); - - test_go = 1; - - sleep(duration); - - test_stop = 1; - - for (i = 0; i < nr_readers; i++) { - err = pthread_join(tid_reader[i], &tret); - if (err != 0) - exit(1); - tot_reads += count_reader[i]; - } - for (i = 0; i < nr_writers; i++) { - err = pthread_join(tid_writer[i], &tret); - if (err != 0) - exit(1); - tot_writes += tot_nr_writes[i]; - rcu_gc_clear_queue(i); - } - - printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads, - tot_writes); - printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu " - "nr_writers %3u " - "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu " - "batch %u\n", - argv[0], duration, nr_readers, rduration, wduration, - nr_writers, wdelay, tot_reads, tot_writes, - tot_reads + tot_writes, reclaim_batch); - free(tid_reader); - free(tid_writer); - free(count_reader); - free(tot_nr_writes); - for (i = 0; i < nr_writers; i++) - free(pending_reclaims[i].queue); - free(pending_reclaims); - - return 0; -} diff --git a/tests/test_urcu_hash.c b/tests/test_urcu_hash.c deleted file mode 100644 index f862adb..0000000 --- a/tests/test_urcu_hash.c +++ /dev/null @@ -1,774 +0,0 @@ -/* - * test_urcu_hash.c - * - * Userspace RCU library - test program - * - * Copyright 2009-2012 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include "test_urcu_hash.h" - -enum test_hash { - TEST_HASH_RW, - TEST_HASH_UNIQUE, -}; - -struct test_hash_cb { - void (*sigusr1)(int signo); - void (*sigusr2)(int signo); - void *(*thr_reader)(void *_count); - void *(*thr_writer)(void *_count); - int (*populate_hash)(void); -}; - -static -struct test_hash_cb test_hash_cb[] = { - [TEST_HASH_RW] = { - test_hash_rw_sigusr1_handler, - test_hash_rw_sigusr2_handler, - test_hash_rw_thr_reader, - test_hash_rw_thr_writer, - test_hash_rw_populate_hash, - }, - [TEST_HASH_UNIQUE] = { - test_hash_unique_sigusr1_handler, - test_hash_unique_sigusr2_handler, - test_hash_unique_thr_reader, - test_hash_unique_thr_writer, - test_hash_unique_populate_hash, - }, - -}; - -static enum test_hash test_choice = TEST_HASH_RW; - -void (*get_sigusr1_cb(void))(int) -{ - return test_hash_cb[test_choice].sigusr1; -} - -void (*get_sigusr2_cb(void))(int) -{ - return test_hash_cb[test_choice].sigusr2; -} - -void *(*get_thr_reader_cb(void))(void *) -{ - return test_hash_cb[test_choice].thr_reader; -} - -void *(*get_thr_writer_cb(void))(void *) -{ - return test_hash_cb[test_choice].thr_writer; -} - -int (*get_populate_hash_cb(void))(void) -{ - return test_hash_cb[test_choice].populate_hash; -} - -DEFINE_URCU_TLS(unsigned int, rand_lookup); -DEFINE_URCU_TLS(unsigned long, nr_add); -DEFINE_URCU_TLS(unsigned long, nr_addexist); -DEFINE_URCU_TLS(unsigned long, nr_del); -DEFINE_URCU_TLS(unsigned long, nr_delnoent); -DEFINE_URCU_TLS(unsigned long, lookup_fail); -DEFINE_URCU_TLS(unsigned long, lookup_ok); - -struct cds_lfht *test_ht; - -volatile int test_go, test_stop; - -unsigned long wdelay; - -unsigned long duration; - -/* read-side C.S. duration, in loops */ -unsigned long rduration; - -unsigned long init_hash_size = DEFAULT_HASH_SIZE; -unsigned long min_hash_alloc_size = DEFAULT_MIN_ALLOC_SIZE; -unsigned long max_hash_buckets_size = (1UL << 20); -unsigned long init_populate; -int opt_auto_resize; -int add_only, add_unique, add_replace; -const struct cds_lfht_mm_type *memory_backend; - -unsigned long init_pool_offset, lookup_pool_offset, write_pool_offset; -unsigned long init_pool_size = DEFAULT_RAND_POOL, - lookup_pool_size = DEFAULT_RAND_POOL, - write_pool_size = DEFAULT_RAND_POOL; -int validate_lookup; -unsigned long nr_hash_chains; /* 0: normal table, other: number of hash chains */ - -int count_pipe[2]; - -int verbose_mode; - -unsigned int cpu_affinities[NR_CPUS]; -unsigned int next_aff = 0; -int use_affinity = 0; - -pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; - -DEFINE_URCU_TLS(unsigned long long, nr_writes); -DEFINE_URCU_TLS(unsigned long long, nr_reads); - -unsigned int nr_readers; -unsigned int nr_writers; - -static pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; - -void set_affinity(void) -{ -#if HAVE_SCHED_SETAFFINITY - cpu_set_t mask; - int cpu, ret; -#endif /* HAVE_SCHED_SETAFFINITY */ - - if (!use_affinity) - return; - -#if HAVE_SCHED_SETAFFINITY - ret = pthread_mutex_lock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } - cpu = cpu_affinities[next_aff++]; - ret = pthread_mutex_unlock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } - CPU_ZERO(&mask); - CPU_SET(cpu, &mask); -#if SCHED_SETAFFINITY_ARGS == 2 - sched_setaffinity(0, &mask); -#else - sched_setaffinity(0, sizeof(mask), &mask); -#endif -#endif /* HAVE_SCHED_SETAFFINITY */ -} - -void rcu_copy_mutex_lock(void) -{ - int ret; - ret = pthread_mutex_lock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } -} - -void rcu_copy_mutex_unlock(void) -{ - int ret; - - ret = pthread_mutex_unlock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } -} - -unsigned long test_compare(const void *key1, size_t key1_len, - const void *key2, size_t key2_len) -{ - if (caa_unlikely(key1_len != key2_len)) - return -1; - assert(key1_len == sizeof(unsigned long)); - if (key1 == key2) - return 0; - else - return 1; -} - -void *thr_count(void *arg) -{ - printf_verbose("thread_begin %s, tid %lu\n", - "counter", urcu_get_thread_id()); - - rcu_register_thread(); - - for (;;) { - unsigned long count; - long approx_before, approx_after; - ssize_t len; - char buf[1]; - - rcu_thread_offline(); - len = read(count_pipe[0], buf, 1); - rcu_thread_online(); - if (caa_unlikely(!test_duration_read())) - break; - if (len != 1) - continue; - /* Accounting */ - printf("Counting nodes... "); - fflush(stdout); - rcu_read_lock(); - cds_lfht_count_nodes(test_ht, &approx_before, &count, - &approx_after); - rcu_read_unlock(); - printf("done.\n"); - printf("Approximation before node accounting: %ld nodes.\n", - approx_before); - printf("Accounting of nodes in the hash table: " - "%lu nodes.\n", - count); - printf("Approximation after node accounting: %ld nodes.\n", - approx_after); - } - rcu_unregister_thread(); - return NULL; -} - -void free_node_cb(struct rcu_head *head) -{ - struct lfht_test_node *node = - caa_container_of(head, struct lfht_test_node, head); - free(node); -} - -static -void test_delete_all_nodes(struct cds_lfht *ht) -{ - struct cds_lfht_iter iter; - struct lfht_test_node *node; - unsigned long count = 0; - - cds_lfht_for_each_entry(ht, &iter, node, node) { - int ret; - - ret = cds_lfht_del(test_ht, cds_lfht_iter_get_node(&iter)); - assert(!ret); - call_rcu(&node->head, free_node_cb); - count++; - } - printf("deleted %lu nodes.\n", count); -} - -void show_usage(int argc, char **argv) -{ - printf("Usage : %s nr_readers nr_writers duration (s) \n", - argv[0]); - printf("OPTIONS:\n"); -#ifdef DEBUG_YIELD - printf(" [-r] [-w] (yield reader and/or writer)\n"); -#endif - printf(" [-d delay] (writer period (us))\n"); - printf(" [-c duration] (reader C.S. duration (in loops))\n"); - printf(" [-v] (verbose output)\n"); - printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); - printf(" [-h size] (initial number of buckets)\n"); - printf(" [-m size] (minimum number of allocated buckets)\n"); - printf(" [-n size] (maximum number of buckets)\n"); -printf(" [not -u nor -s] Add entries (supports redundant keys).\n"); - printf(" [-u] Uniquify add (no redundant keys).\n"); - printf(" [-s] Replace (swap) entries.\n"); - printf(" [-i] Add only (no removal).\n"); - printf(" [-k nr_nodes] Number of nodes to insert initially.\n"); - printf(" [-A] Automatically resize hash table.\n"); - printf(" [-B order|chunk|mmap] Specify the memory backend.\n"); - printf(" [-R offset] Lookup pool offset.\n"); - printf(" [-S offset] Write pool offset.\n"); - printf(" [-T offset] Init pool offset.\n"); - printf(" [-M size] Lookup pool size.\n"); - printf(" [-N size] Write pool size.\n"); - printf(" [-O size] Init pool size.\n"); - printf(" [-V] Validate lookups of init values.\n"); - printf(" (use with filled init pool, same lookup range,\n"); - printf(" with different write range)\n"); - printf(" [-U] Uniqueness test.\n"); - printf(" [-C] Number of hash chains.\n"); - printf("\n"); -} - -int main(int argc, char **argv) -{ - pthread_t *tid_reader, *tid_writer; - pthread_t tid_count; - void *tret; - unsigned long long *count_reader; - struct wr_count *count_writer; - unsigned long long tot_reads = 0, tot_writes = 0, - tot_add = 0, tot_add_exist = 0, tot_remove = 0; - unsigned long count; - long approx_before, approx_after; - int i, a, ret, err, mainret = 0; - struct sigaction act; - unsigned int remain; - unsigned int nr_readers_created = 0, nr_writers_created = 0; - long long nr_leaked; - - if (argc < 4) { - show_usage(argc, argv); - mainret = 1; - goto end; - } - - err = sscanf(argv[1], "%u", &nr_readers); - if (err != 1) { - show_usage(argc, argv); - mainret = 1; - goto end; - } - - err = sscanf(argv[2], "%u", &nr_writers); - if (err != 1) { - show_usage(argc, argv); - mainret = 1; - goto end; - } - - err = sscanf(argv[3], "%lu", &duration); - if (err != 1) { - show_usage(argc, argv); - mainret = 1; - goto end; - } - - for (i = 4; i < argc; i++) { - if (argv[i][0] != '-') - continue; - switch (argv[i][1]) { -#ifdef DEBUG_YIELD - case 'r': - yield_active |= YIELD_READ; - break; - case 'w': - yield_active |= YIELD_WRITE; - break; -#endif - case 'a': - if (argc < i + 2) { - show_usage(argc, argv); - mainret = 1; - goto end; - } - a = atoi(argv[++i]); - cpu_affinities[next_aff++] = a; - use_affinity = 1; - printf_verbose("Adding CPU %d affinity\n", a); - break; - case 'c': - if (argc < i + 2) { - show_usage(argc, argv); - mainret = 1; - goto end; - } - rduration = atol(argv[++i]); - break; - case 'd': - if (argc < i + 2) { - show_usage(argc, argv); - mainret = 1; - goto end; - } - wdelay = atol(argv[++i]); - break; - case 'v': - verbose_mode = 1; - break; - case 'h': - if (argc < i + 2) { - show_usage(argc, argv); - mainret = 1; - goto end; - } - init_hash_size = atol(argv[++i]); - break; - case 'm': - if (argc < i + 2) { - show_usage(argc, argv); - mainret = 1; - goto end; - } - min_hash_alloc_size = atol(argv[++i]); - break; - case 'n': - if (argc < i + 2) { - show_usage(argc, argv); - mainret = 1; - goto end; - } - max_hash_buckets_size = atol(argv[++i]); - break; - case 'u': - if (add_replace) { - printf("Please specify at most one of -s or -u.\n"); - exit(-1); - } - add_unique = 1; - break; - case 's': - if (add_unique) { - printf("Please specify at most one of -s or -u.\n"); - exit(-1); - } - add_replace = 1; - break; - case 'i': - add_only = 1; - break; - case 'k': - init_populate = atol(argv[++i]); - break; - case 'A': - opt_auto_resize = 1; - break; - case 'B': - if (argc < i + 2) { - show_usage(argc, argv); - mainret = 1; - goto end; - } - i++; - if (!strcmp("order", argv[i])) - memory_backend = &cds_lfht_mm_order; - else if (!strcmp("chunk", argv[i])) - memory_backend = &cds_lfht_mm_chunk; - else if (!strcmp("mmap", argv[i])) - memory_backend = &cds_lfht_mm_mmap; - else { - printf("Please specify memory backend with order|chunk|mmap.\n"); - mainret = 1; - goto end; - } - break; - case 'R': - lookup_pool_offset = atol(argv[++i]); - break; - case 'S': - write_pool_offset = atol(argv[++i]); - break; - case 'T': - init_pool_offset = atol(argv[++i]); - break; - case 'M': - lookup_pool_size = atol(argv[++i]); - break; - case 'N': - write_pool_size = atol(argv[++i]); - break; - case 'O': - init_pool_size = atol(argv[++i]); - break; - case 'V': - validate_lookup = 1; - break; - case 'U': - test_choice = TEST_HASH_UNIQUE; - break; - case 'C': - nr_hash_chains = atol(argv[++i]); - break; - } - } - - /* Check if hash size is power of 2 */ - if (init_hash_size && init_hash_size & (init_hash_size - 1)) { - printf("Error: Initial number of buckets (%lu) is not a power of 2.\n", - init_hash_size); - mainret = 1; - goto end; - } - - if (min_hash_alloc_size && min_hash_alloc_size & (min_hash_alloc_size - 1)) { - printf("Error: Minimum number of allocated buckets (%lu) is not a power of 2.\n", - min_hash_alloc_size); - mainret = 1; - goto end; - } - - if (max_hash_buckets_size && max_hash_buckets_size & (max_hash_buckets_size - 1)) { - printf("Error: Maximum number of buckets (%lu) is not a power of 2.\n", - max_hash_buckets_size); - mainret = 1; - goto end; - } - - memset(&act, 0, sizeof(act)); - ret = sigemptyset(&act.sa_mask); - if (ret == -1) { - perror("sigemptyset"); - mainret = 1; - goto end; - } - act.sa_handler = get_sigusr1_cb(); - act.sa_flags = SA_RESTART; - ret = sigaction(SIGUSR1, &act, NULL); - if (ret == -1) { - perror("sigaction"); - mainret = 1; - goto end; - } - - act.sa_handler = get_sigusr2_cb(); - act.sa_flags = SA_RESTART; - ret = sigaction(SIGUSR2, &act, NULL); - if (ret == -1) { - perror("sigaction"); - mainret = 1; - goto end; - } - - printf_verbose("running test for %lu seconds, %u readers, %u writers.\n", - duration, nr_readers, nr_writers); - printf_verbose("Writer delay : %lu loops.\n", wdelay); - printf_verbose("Reader duration : %lu loops.\n", rduration); - printf_verbose("Mode:%s%s.\n", - add_only ? " add only" : " add/remove", - add_unique ? " uniquify" : ( add_replace ? " replace" : " insert")); - printf_verbose("Initial number of buckets: %lu buckets.\n", init_hash_size); - printf_verbose("Minimum number of allocated buckets: %lu buckets.\n", min_hash_alloc_size); - printf_verbose("Maximum number of buckets: %lu buckets.\n", max_hash_buckets_size); - printf_verbose("Init pool size offset %lu size %lu.\n", - init_pool_offset, init_pool_size); - printf_verbose("Lookup pool size offset %lu size %lu.\n", - lookup_pool_offset, lookup_pool_size); - printf_verbose("Update pool size offset %lu size %lu.\n", - write_pool_offset, write_pool_size); - printf_verbose("Number of hash chains: %lu.\n", - nr_hash_chains); - printf_verbose("thread %-6s, tid %lu\n", - "main", urcu_get_thread_id()); - - tid_reader = calloc(nr_readers, sizeof(*tid_reader)); - if (!tid_reader) { - mainret = 1; - goto end; - } - tid_writer = calloc(nr_writers, sizeof(*tid_writer)); - if (!tid_writer) { - mainret = 1; - goto end_free_tid_reader; - } - count_reader = calloc(nr_readers, sizeof(*count_reader)); - if (!count_reader) { - mainret = 1; - goto end_free_tid_writer; - } - count_writer = calloc(nr_writers, sizeof(*count_writer)); - if (!count_writer) { - mainret = 1; - goto end_free_count_reader; - } - - err = create_all_cpu_call_rcu_data(0); - if (err) { - printf("Per-CPU call_rcu() worker threads unavailable. Using default global worker thread.\n"); - } - - if (memory_backend) { - test_ht = _cds_lfht_new(init_hash_size, min_hash_alloc_size, - max_hash_buckets_size, - (opt_auto_resize ? CDS_LFHT_AUTO_RESIZE : 0) | - CDS_LFHT_ACCOUNTING, memory_backend, - &rcu_flavor, NULL); - } else { - test_ht = cds_lfht_new(init_hash_size, min_hash_alloc_size, - max_hash_buckets_size, - (opt_auto_resize ? CDS_LFHT_AUTO_RESIZE : 0) | - CDS_LFHT_ACCOUNTING, NULL); - } - if (!test_ht) { - printf("Error allocating hash table.\n"); - mainret = 1; - goto end_free_call_rcu_data; - } - - /* - * Hash Population needs to be seen as a RCU reader - * thread from the point of view of resize. - */ - rcu_register_thread(); - ret = (get_populate_hash_cb())(); - assert(!ret); - - rcu_thread_offline(); - - next_aff = 0; - - ret = pipe(count_pipe); - if (ret == -1) { - perror("pipe"); - mainret = 1; - goto end_online; - } - - /* spawn counter thread */ - err = pthread_create(&tid_count, NULL, thr_count, - NULL); - if (err != 0) { - errno = err; - mainret = 1; - perror("pthread_create"); - goto end_close_pipe; - } - - for (i = 0; i < nr_readers; i++) { - err = pthread_create(&tid_reader[i], - NULL, get_thr_reader_cb(), - &count_reader[i]); - if (err != 0) { - errno = err; - mainret = 1; - perror("pthread_create"); - goto end_pthread_join; - } - nr_readers_created++; - } - for (i = 0; i < nr_writers; i++) { - err = pthread_create(&tid_writer[i], - NULL, get_thr_writer_cb(), - &count_writer[i]); - if (err != 0) { - errno = err; - mainret = 1; - perror("pthread_create"); - goto end_pthread_join; - } - nr_writers_created++; - } - - cmm_smp_mb(); - - test_go = 1; - - remain = duration; - do { - remain = sleep(remain); - } while (remain > 0); - - test_stop = 1; - -end_pthread_join: - for (i = 0; i < nr_readers_created; i++) { - err = pthread_join(tid_reader[i], &tret); - if (err != 0) { - errno = err; - mainret = 1; - perror("pthread_join"); - } - tot_reads += count_reader[i]; - } - for (i = 0; i < nr_writers_created; i++) { - err = pthread_join(tid_writer[i], &tret); - if (err != 0) { - errno = err; - mainret = 1; - perror("pthread_join"); - } - tot_writes += count_writer[i].update_ops; - tot_add += count_writer[i].add; - tot_add_exist += count_writer[i].add_exist; - tot_remove += count_writer[i].remove; - } - - /* teardown counter thread */ - act.sa_handler = SIG_IGN; - act.sa_flags = SA_RESTART; - ret = sigaction(SIGUSR2, &act, NULL); - if (ret == -1) { - mainret = 1; - perror("sigaction"); - } - { - char msg[1] = { 0x42 }; - ssize_t ret; - - do { - ret = write(count_pipe[1], msg, 1); /* wakeup thread */ - } while (ret == -1L && errno == EINTR); - } - err = pthread_join(tid_count, &tret); - if (err != 0) { - errno = err; - mainret = 1; - perror("pthread_join"); - } - -end_close_pipe: - for (i = 0; i < 2; i++) { - err = close(count_pipe[i]); - if (err) { - mainret = 1; - perror("close pipe"); - } - } - fflush(stdout); -end_online: - rcu_thread_online(); - rcu_read_lock(); - printf("Counting nodes... "); - cds_lfht_count_nodes(test_ht, &approx_before, &count, &approx_after); - printf("done.\n"); - test_delete_all_nodes(test_ht); - rcu_read_unlock(); - rcu_thread_offline(); - if (count) { - printf("Approximation before node accounting: %ld nodes.\n", - approx_before); - printf("Nodes deleted from hash table before destroy: " - "%lu nodes.\n", - count); - printf("Approximation after node accounting: %ld nodes.\n", - approx_after); - } - - ret = cds_lfht_destroy(test_ht, NULL); - if (ret) { - printf_verbose("final delete aborted\n"); - mainret = 1; - } else { - printf_verbose("final delete success\n"); - } - printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads, - tot_writes); - nr_leaked = (long long) tot_add + init_populate - tot_remove - count; - printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu " - "nr_writers %3u " - "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu " - "nr_add %12llu nr_add_fail %12llu nr_remove %12llu nr_leaked %12lld\n", - argv[0], duration, nr_readers, rduration, - nr_writers, wdelay, tot_reads, tot_writes, - tot_reads + tot_writes, tot_add, tot_add_exist, tot_remove, - nr_leaked); - if (nr_leaked != 0) { - mainret = 1; - printf("WARNING: %lld nodes were leaked!\n", nr_leaked); - } - - rcu_unregister_thread(); -end_free_call_rcu_data: - free_all_cpu_call_rcu_data(); - free(count_writer); -end_free_count_reader: - free(count_reader); -end_free_tid_writer: - free(tid_writer); -end_free_tid_reader: - free(tid_reader); -end: - if (!mainret) - exit(EXIT_SUCCESS); - else - exit(EXIT_FAILURE); -} diff --git a/tests/test_urcu_hash.h b/tests/test_urcu_hash.h deleted file mode 100644 index cd064e6..0000000 --- a/tests/test_urcu_hash.h +++ /dev/null @@ -1,390 +0,0 @@ -#ifndef _TEST_URCU_HASH_H -#define _TEST_URCU_HASH_H - -/* - * test_urcu_hash.h - * - * Userspace RCU library - test program - * - * Copyright 2009-2012 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "../config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "cpuset.h" -#include "thread-id.h" - -#define DEFAULT_HASH_SIZE 32 -#define DEFAULT_MIN_ALLOC_SIZE 1 -#define DEFAULT_RAND_POOL 1000000 - -/* - * Note: the hash seed should be a random value for hash tables - * targeting production environments to provide protection against - * denial of service attacks. We keep it a static value within this test - * program to compare identical benchmark runs. - */ -#define TEST_HASH_SEED 0x42UL - -/* hardcoded number of CPUs */ -#define NR_CPUS 16384 - -#ifdef POISON_FREE -#define poison_free(ptr) \ - do { \ - memset(ptr, 0x42, sizeof(*(ptr))); \ - free(ptr); \ - } while (0) -#else -#define poison_free(ptr) free(ptr) -#endif - -#ifndef DYNAMIC_LINK_TEST -#define _LGPL_SOURCE -#else -#define debug_yield_read() -#endif -#include -#include -#include - -struct wr_count { - unsigned long update_ops; - unsigned long add; - unsigned long add_exist; - unsigned long remove; -}; - -extern DECLARE_URCU_TLS(unsigned int, rand_lookup); -extern DECLARE_URCU_TLS(unsigned long, nr_add); -extern DECLARE_URCU_TLS(unsigned long, nr_addexist); -extern DECLARE_URCU_TLS(unsigned long, nr_del); -extern DECLARE_URCU_TLS(unsigned long, nr_delnoent); -extern DECLARE_URCU_TLS(unsigned long, lookup_fail); -extern DECLARE_URCU_TLS(unsigned long, lookup_ok); - -extern struct cds_lfht *test_ht; - -struct test_data { - int a; - int b; -}; - -struct lfht_test_node { - struct cds_lfht_node node; - void *key; - unsigned int key_len; - /* cache-cold for iteration */ - struct rcu_head head; -}; - -static inline struct lfht_test_node * -to_test_node(struct cds_lfht_node *node) -{ - return caa_container_of(node, struct lfht_test_node, node); -} - -static inline -void lfht_test_node_init(struct lfht_test_node *node, void *key, - size_t key_len) -{ - cds_lfht_node_init(&node->node); - node->key = key; - node->key_len = key_len; -} - -static inline struct lfht_test_node * -cds_lfht_iter_get_test_node(struct cds_lfht_iter *iter) -{ - return to_test_node(cds_lfht_iter_get_node(iter)); -} - -extern volatile int test_go, test_stop; - -extern unsigned long wdelay; - -extern unsigned long duration; - -/* read-side C.S. duration, in loops */ -extern unsigned long rduration; - -extern unsigned long init_hash_size; -extern unsigned long min_hash_alloc_size; -extern unsigned long max_hash_buckets_size; -extern unsigned long init_populate; -extern int opt_auto_resize; -extern int add_only, add_unique, add_replace; -extern const struct cds_lfht_mm_type *memory_backend; - -extern unsigned long init_pool_offset, lookup_pool_offset, write_pool_offset; -extern unsigned long init_pool_size, - lookup_pool_size, - write_pool_size; -extern int validate_lookup; - -extern unsigned long nr_hash_chains; - -extern int count_pipe[2]; - -static inline void loop_sleep(unsigned long loops) -{ - while (loops-- != 0) - caa_cpu_relax(); -} - -extern int verbose_mode; - -#define printf_verbose(fmt, args...) \ - do { \ - if (verbose_mode) \ - printf(fmt, ## args); \ - } while (0) - -extern unsigned int cpu_affinities[NR_CPUS]; -extern unsigned int next_aff; -extern int use_affinity; - -extern pthread_mutex_t affinity_mutex; - -void set_affinity(void); - -/* - * returns 0 if test should end. - */ -static inline int test_duration_write(void) -{ - return !test_stop; -} - -static inline int test_duration_read(void) -{ - return !test_stop; -} - -extern DECLARE_URCU_TLS(unsigned long long, nr_writes); -extern DECLARE_URCU_TLS(unsigned long long, nr_reads); - -extern unsigned int nr_readers; -extern unsigned int nr_writers; - -void rcu_copy_mutex_lock(void); -void rcu_copy_mutex_unlock(void); - -/* - * Hash function - * Source: http://burtleburtle.net/bob/c/lookup3.c - * Originally Public Domain - */ - -#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k)))) - -#define mix(a, b, c) \ -do { \ - a -= c; a ^= rot(c, 4); c += b; \ - b -= a; b ^= rot(a, 6); a += c; \ - c -= b; c ^= rot(b, 8); b += a; \ - a -= c; a ^= rot(c, 16); c += b; \ - b -= a; b ^= rot(a, 19); a += c; \ - c -= b; c ^= rot(b, 4); b += a; \ -} while (0) - -#define final(a, b, c) \ -{ \ - c ^= b; c -= rot(b, 14); \ - a ^= c; a -= rot(c, 11); \ - b ^= a; b -= rot(a, 25); \ - c ^= b; c -= rot(b, 16); \ - a ^= c; a -= rot(c, 4);\ - b ^= a; b -= rot(a, 14); \ - c ^= b; c -= rot(b, 24); \ -} - -static inline __attribute__((unused)) -uint32_t hash_u32( - const uint32_t *k, /* the key, an array of uint32_t values */ - size_t length, /* the length of the key, in uint32_ts */ - uint32_t initval) /* the previous hash, or an arbitrary value */ -{ - uint32_t a, b, c; - - /* Set up the internal state */ - a = b = c = 0xdeadbeef + (((uint32_t) length) << 2) + initval; - - /*----------------------------------------- handle most of the key */ - while (length > 3) { - a += k[0]; - b += k[1]; - c += k[2]; - mix(a, b, c); - length -= 3; - k += 3; - } - - /*----------------------------------- handle the last 3 uint32_t's */ - switch (length) { /* all the case statements fall through */ - case 3: c += k[2]; - case 2: b += k[1]; - case 1: a += k[0]; - final(a, b, c); - case 0: /* case 0: nothing left to add */ - break; - } - /*---------------------------------------------- report the result */ - return c; -} - -static inline -void hashword2( - const uint32_t *k, /* the key, an array of uint32_t values */ - size_t length, /* the length of the key, in uint32_ts */ - uint32_t *pc, /* IN: seed OUT: primary hash value */ - uint32_t *pb) /* IN: more seed OUT: secondary hash value */ -{ - uint32_t a, b, c; - - /* Set up the internal state */ - a = b = c = 0xdeadbeef + ((uint32_t) (length << 2)) + *pc; - c += *pb; - - /*----------------------------------------- handle most of the key */ - while (length > 3) { - a += k[0]; - b += k[1]; - c += k[2]; - mix(a, b, c); - length -= 3; - k += 3; - } - - /*----------------------------------- handle the last 3 uint32_t's */ - switch (length) { /* all the case statements fall through */ - case 3: c += k[2]; - case 2: b += k[1]; - case 1: a += k[0]; - final(a, b, c); - case 0: /* case 0: nothing left to add */ - break; - } - /*---------------------------------------------- report the result */ - *pc = c; - *pb = b; -} - -#if (CAA_BITS_PER_LONG == 32) -static inline -unsigned long test_hash_mix(const void *_key, size_t length, unsigned long seed) -{ - unsigned int key = (unsigned int) _key; - - assert(length == sizeof(unsigned int)); - return hash_u32(&key, 1, seed); -} -#else -static inline -unsigned long test_hash_mix(const void *_key, size_t length, unsigned long seed) -{ - union { - uint64_t v64; - uint32_t v32[2]; - } v; - union { - uint64_t v64; - uint32_t v32[2]; - } key; - - assert(length == sizeof(unsigned long)); - v.v64 = (uint64_t) seed; - key.v64 = (uint64_t) _key; - hashword2(key.v32, 2, &v.v32[0], &v.v32[1]); - return v.v64; -} -#endif - -/* - * Hash function with nr_hash_chains != 0 for testing purpose only! - * Creates very long hash chains, deteriorating the hash table into a - * few linked lists, depending on the nr_hash_chains value. The purpose - * of this test is to check how the hash table behaves with hash chains - * containing different values, which is a rare case in a normal hash - * table. - */ -static inline -unsigned long test_hash(const void *_key, size_t length, - unsigned long seed) -{ - if (nr_hash_chains == 0) { - return test_hash_mix(_key, length, seed); - } else { - unsigned long v; - - assert(length == sizeof(unsigned long)); - v = (unsigned long) _key; - return v % nr_hash_chains; - } -} - -unsigned long test_compare(const void *key1, size_t key1_len, - const void *key2, size_t key2_len); - -static inline -int test_match(struct cds_lfht_node *node, const void *key) -{ - struct lfht_test_node *test_node = to_test_node(node); - - return !test_compare(test_node->key, test_node->key_len, - key, sizeof(unsigned long)); -} - -static inline -void cds_lfht_test_lookup(struct cds_lfht *ht, void *key, size_t key_len, - struct cds_lfht_iter *iter) -{ - assert(key_len == sizeof(unsigned long)); - - cds_lfht_lookup(ht, test_hash(key, key_len, TEST_HASH_SEED), - test_match, key, iter); -} - -void free_node_cb(struct rcu_head *head); - -/* rw test */ -void test_hash_rw_sigusr1_handler(int signo); -void test_hash_rw_sigusr2_handler(int signo); -void *test_hash_rw_thr_reader(void *_count); -void *test_hash_rw_thr_writer(void *_count); -int test_hash_rw_populate_hash(void); - -/* unique test */ -void test_hash_unique_sigusr1_handler(int signo); -void test_hash_unique_sigusr2_handler(int signo); -void *test_hash_unique_thr_reader(void *_count); -void *test_hash_unique_thr_writer(void *_count); -int test_hash_unique_populate_hash(void); - -#endif /* _TEST_URCU_HASH_H */ diff --git a/tests/test_urcu_hash_rw.c b/tests/test_urcu_hash_rw.c deleted file mode 100644 index 8802b9c..0000000 --- a/tests/test_urcu_hash_rw.c +++ /dev/null @@ -1,284 +0,0 @@ -/* - * test_urcu_hash_rw.c - * - * Userspace RCU library - test program - * - * Copyright 2009-2012 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include "test_urcu_hash.h" - -enum urcu_hash_addremove { - AR_RANDOM = 0, - AR_ADD = 1, - AR_REMOVE = -1, -}; /* 1: add, -1 remove, 0: random */ - -static enum urcu_hash_addremove addremove; /* 1: add, -1 remove, 0: random */ - -void test_hash_rw_sigusr1_handler(int signo) -{ - switch (addremove) { - case AR_ADD: - printf("Add/Remove: random.\n"); - addremove = AR_RANDOM; - break; - case AR_RANDOM: - printf("Add/Remove: remove only.\n"); - addremove = AR_REMOVE; - break; - case AR_REMOVE: - printf("Add/Remove: add only.\n"); - addremove = AR_ADD; - break; - } -} - -void test_hash_rw_sigusr2_handler(int signo) -{ - char msg[1] = { 0x42 }; - ssize_t ret; - - do { - ret = write(count_pipe[1], msg, 1); /* wakeup thread */ - } while (ret == -1L && errno == EINTR); -} - -void *test_hash_rw_thr_reader(void *_count) -{ - unsigned long long *count = _count; - struct lfht_test_node *node; - struct cds_lfht_iter iter; - - printf_verbose("thread_begin %s, tid %lu\n", - "reader", urcu_get_thread_id()); - - URCU_TLS(rand_lookup) = urcu_get_thread_id() ^ time(NULL); - - set_affinity(); - - rcu_register_thread(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - rcu_read_lock(); - cds_lfht_test_lookup(test_ht, - (void *)(((unsigned long) rand_r(&URCU_TLS(rand_lookup)) % lookup_pool_size) + lookup_pool_offset), - sizeof(void *), &iter); - node = cds_lfht_iter_get_test_node(&iter); - if (node == NULL) { - if (validate_lookup) { - printf("[ERROR] Lookup cannot find initial node.\n"); - exit(-1); - } - URCU_TLS(lookup_fail)++; - } else { - URCU_TLS(lookup_ok)++; - } - rcu_debug_yield_read(); - if (caa_unlikely(rduration)) - loop_sleep(rduration); - rcu_read_unlock(); - URCU_TLS(nr_reads)++; - if (caa_unlikely(!test_duration_read())) - break; - if (caa_unlikely((URCU_TLS(nr_reads) & ((1 << 10) - 1)) == 0)) - rcu_quiescent_state(); - } - - rcu_unregister_thread(); - - *count = URCU_TLS(nr_reads); - printf_verbose("thread_end %s, tid %lu\n", - "reader", urcu_get_thread_id()); - printf_verbose("read tid : %lx, lookupfail %lu, lookupok %lu\n", - urcu_get_thread_id(), - URCU_TLS(lookup_fail), - URCU_TLS(lookup_ok)); - return ((void*)1); - -} - -void *test_hash_rw_thr_writer(void *_count) -{ - struct lfht_test_node *node; - struct cds_lfht_node *ret_node; - struct cds_lfht_iter iter; - struct wr_count *count = _count; - int ret; - - printf_verbose("thread_begin %s, tid %lu\n", - "writer", urcu_get_thread_id()); - - URCU_TLS(rand_lookup) = urcu_get_thread_id() ^ time(NULL); - - set_affinity(); - - rcu_register_thread(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - if ((addremove == AR_ADD || add_only) - || (addremove == AR_RANDOM && rand_r(&URCU_TLS(rand_lookup)) & 1)) { - node = malloc(sizeof(struct lfht_test_node)); - lfht_test_node_init(node, - (void *)(((unsigned long) rand_r(&URCU_TLS(rand_lookup)) % write_pool_size) + write_pool_offset), - sizeof(void *)); - rcu_read_lock(); - if (add_unique) { - ret_node = cds_lfht_add_unique(test_ht, - test_hash(node->key, node->key_len, TEST_HASH_SEED), - test_match, node->key, &node->node); - } else { - if (add_replace) - ret_node = cds_lfht_add_replace(test_ht, - test_hash(node->key, node->key_len, TEST_HASH_SEED), - test_match, node->key, &node->node); - else - cds_lfht_add(test_ht, - test_hash(node->key, node->key_len, TEST_HASH_SEED), - &node->node); - } - rcu_read_unlock(); - if (add_unique && ret_node != &node->node) { - free(node); - URCU_TLS(nr_addexist)++; - } else { - if (add_replace && ret_node) { - call_rcu(&to_test_node(ret_node)->head, - free_node_cb); - URCU_TLS(nr_addexist)++; - } else { - URCU_TLS(nr_add)++; - } - } - } else { - /* May delete */ - rcu_read_lock(); - cds_lfht_test_lookup(test_ht, - (void *)(((unsigned long) rand_r(&URCU_TLS(rand_lookup)) % write_pool_size) + write_pool_offset), - sizeof(void *), &iter); - ret = cds_lfht_del(test_ht, cds_lfht_iter_get_node(&iter)); - rcu_read_unlock(); - if (ret == 0) { - node = cds_lfht_iter_get_test_node(&iter); - call_rcu(&node->head, free_node_cb); - URCU_TLS(nr_del)++; - } else - URCU_TLS(nr_delnoent)++; - } -#if 0 - //if (URCU_TLS(nr_writes) % 100000 == 0) { - if (URCU_TLS(nr_writes) % 1000 == 0) { - rcu_read_lock(); - if (rand_r(&URCU_TLS(rand_lookup)) & 1) { - ht_resize(test_ht, 1); - } else { - ht_resize(test_ht, -1); - } - rcu_read_unlock(); - } -#endif //0 - URCU_TLS(nr_writes)++; - if (caa_unlikely(!test_duration_write())) - break; - if (caa_unlikely(wdelay)) - loop_sleep(wdelay); - if (caa_unlikely((URCU_TLS(nr_writes) & ((1 << 10) - 1)) == 0)) - rcu_quiescent_state(); - } - - rcu_unregister_thread(); - - printf_verbose("thread_end %s, tid %lu\n", - "writer", urcu_get_thread_id()); - printf_verbose("info tid %lu: nr_add %lu, nr_addexist %lu, nr_del %lu, " - "nr_delnoent %lu\n", urcu_get_thread_id(), - URCU_TLS(nr_add), - URCU_TLS(nr_addexist), - URCU_TLS(nr_del), - URCU_TLS(nr_delnoent)); - count->update_ops = URCU_TLS(nr_writes); - count->add = URCU_TLS(nr_add); - count->add_exist = URCU_TLS(nr_addexist); - count->remove = URCU_TLS(nr_del); - return ((void*)2); -} - -int test_hash_rw_populate_hash(void) -{ - struct lfht_test_node *node; - struct cds_lfht_node *ret_node; - - if (!init_populate) - return 0; - - printf("Starting rw test\n"); - - URCU_TLS(rand_lookup) = urcu_get_thread_id() ^ time(NULL); - - if ((add_unique || add_replace) && init_populate * 10 > init_pool_size) { - printf("WARNING: required to populate %lu nodes (-k), but random " -"pool is quite small (%lu values) and we are in add_unique (-u) or add_replace (-s) mode. Try with a " -"larger random pool (-p option). This may take a while...\n", init_populate, init_pool_size); - } - - while (URCU_TLS(nr_add) < init_populate) { - node = malloc(sizeof(struct lfht_test_node)); - lfht_test_node_init(node, - (void *)(((unsigned long) rand_r(&URCU_TLS(rand_lookup)) % init_pool_size) + init_pool_offset), - sizeof(void *)); - rcu_read_lock(); - if (add_unique) { - ret_node = cds_lfht_add_unique(test_ht, - test_hash(node->key, node->key_len, TEST_HASH_SEED), - test_match, node->key, &node->node); - } else { - if (add_replace) - ret_node = cds_lfht_add_replace(test_ht, - test_hash(node->key, node->key_len, TEST_HASH_SEED), - test_match, node->key, &node->node); - else - cds_lfht_add(test_ht, - test_hash(node->key, node->key_len, TEST_HASH_SEED), - &node->node); - } - rcu_read_unlock(); - if (add_unique && ret_node != &node->node) { - free(node); - URCU_TLS(nr_addexist)++; - } else { - if (add_replace && ret_node) { - call_rcu(&to_test_node(ret_node)->head, free_node_cb); - URCU_TLS(nr_addexist)++; - } else { - URCU_TLS(nr_add)++; - } - } - URCU_TLS(nr_writes)++; - } - return 0; -} diff --git a/tests/test_urcu_hash_unique.c b/tests/test_urcu_hash_unique.c deleted file mode 100644 index adbbde0..0000000 --- a/tests/test_urcu_hash_unique.c +++ /dev/null @@ -1,279 +0,0 @@ -/* - * test_urcu_hash_unique.c - * - * Userspace RCU library - test program - * - * Copyright 2009-2012 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include "test_urcu_hash.h" - -enum urcu_hash_addremove { - AR_RANDOM = 0, - AR_ADD = 1, - AR_REMOVE = -1, -}; /* 1: add, -1 remove, 0: random */ - -static enum urcu_hash_addremove addremove; /* 1: add, -1 remove, 0: random */ - -void test_hash_unique_sigusr1_handler(int signo) -{ - switch (addremove) { - case AR_ADD: - printf("Add/Remove: random.\n"); - addremove = AR_RANDOM; - break; - case AR_RANDOM: - printf("Add/Remove: remove only.\n"); - addremove = AR_REMOVE; - break; - case AR_REMOVE: - printf("Add/Remove: add only.\n"); - addremove = AR_ADD; - break; - } -} - -void test_hash_unique_sigusr2_handler(int signo) -{ - char msg[1] = { 0x42 }; - ssize_t ret; - - do { - ret = write(count_pipe[1], msg, 1); /* wakeup thread */ - } while (ret == -1L && errno == EINTR); -} - -void *test_hash_unique_thr_reader(void *_count) -{ - unsigned long long *count = _count; - - printf_verbose("thread_begin %s, tid %lu\n", - "reader", urcu_get_thread_id()); - - URCU_TLS(rand_lookup) = urcu_get_thread_id() ^ time(NULL); - - set_affinity(); - - rcu_register_thread(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - struct lfht_test_node *node; - struct cds_lfht_iter iter; - /* - * iterate on whole table, ensuring that no duplicate is - * found. - */ - rcu_read_lock(); - cds_lfht_for_each_entry(test_ht, &iter, node, node) { - struct cds_lfht_iter dup_iter; - - dup_iter = iter; - cds_lfht_next_duplicate(test_ht, test_match, - node->key, &dup_iter); - if (dup_iter.node != NULL) { - printf("[ERROR] Duplicate key %p found\n", node->key); - } - } - rcu_read_unlock(); - - rcu_debug_yield_read(); - if (caa_unlikely(rduration)) - loop_sleep(rduration); - URCU_TLS(nr_reads)++; - if (caa_unlikely(!test_duration_read())) - break; - if (caa_unlikely((URCU_TLS(nr_reads) & ((1 << 10) - 1)) == 0)) - rcu_quiescent_state(); - } - - rcu_unregister_thread(); - - *count = URCU_TLS(nr_reads); - printf_verbose("thread_end %s, tid %lu\n", - "reader", urcu_get_thread_id()); - printf_verbose("read tid : %lu, lookupfail %lu, lookupok %lu\n", - urcu_get_thread_id(), URCU_TLS(lookup_fail), - URCU_TLS(lookup_ok)); - return ((void*)1); - -} - -void *test_hash_unique_thr_writer(void *_count) -{ - struct lfht_test_node *node; - struct cds_lfht_node *ret_node; - struct cds_lfht_iter iter; - struct wr_count *count = _count; - int ret; - int loc_add_unique; - - printf_verbose("thread_begin %s, tid %lu\n", - "writer", urcu_get_thread_id()); - - URCU_TLS(rand_lookup) = urcu_get_thread_id() ^ time(NULL); - - set_affinity(); - - rcu_register_thread(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - /* - * add unique/add replace with new node key from range. - */ - if (1 || (addremove == AR_ADD || add_only) - || (addremove == AR_RANDOM && rand_r(&URCU_TLS(rand_lookup)) & 1)) { - node = malloc(sizeof(struct lfht_test_node)); - lfht_test_node_init(node, - (void *)(((unsigned long) rand_r(&URCU_TLS(rand_lookup)) % write_pool_size) + write_pool_offset), - sizeof(void *)); - rcu_read_lock(); - loc_add_unique = rand_r(&URCU_TLS(rand_lookup)) & 1; - if (loc_add_unique) { - ret_node = cds_lfht_add_unique(test_ht, - test_hash(node->key, node->key_len, TEST_HASH_SEED), - test_match, node->key, &node->node); - } else { - ret_node = cds_lfht_add_replace(test_ht, - test_hash(node->key, node->key_len, TEST_HASH_SEED), - test_match, node->key, &node->node); -#if 0 //generate an error on purpose - cds_lfht_add(test_ht, - test_hash(node->key, node->key_len, TEST_HASH_SEED), - &node->node); - ret_node = NULL; -#endif //0 - } - rcu_read_unlock(); - if (loc_add_unique) { - if (ret_node != &node->node) { - free(node); - URCU_TLS(nr_addexist)++; - } else { - URCU_TLS(nr_add)++; - } - } else { - if (ret_node) { - call_rcu(&to_test_node(ret_node)->head, - free_node_cb); - URCU_TLS(nr_addexist)++; - } else { - URCU_TLS(nr_add)++; - } - } - } else { - /* May delete */ - rcu_read_lock(); - cds_lfht_test_lookup(test_ht, - (void *)(((unsigned long) rand_r(&URCU_TLS(rand_lookup)) % write_pool_size) + write_pool_offset), - sizeof(void *), &iter); - ret = cds_lfht_del(test_ht, cds_lfht_iter_get_node(&iter)); - rcu_read_unlock(); - if (ret == 0) { - node = cds_lfht_iter_get_test_node(&iter); - call_rcu(&node->head, free_node_cb); - URCU_TLS(nr_del)++; - } else - URCU_TLS(nr_delnoent)++; - } -#if 0 - //if (URCU_TLS(nr_writes) % 100000 == 0) { - if (URCU_TLS(nr_writes) % 1000 == 0) { - rcu_read_lock(); - if (rand_r(&URCU_TLS(rand_lookup)) & 1) { - ht_resize(test_ht, 1); - } else { - ht_resize(test_ht, -1); - } - rcu_read_unlock(); - } -#endif //0 - URCU_TLS(nr_writes)++; - if (caa_unlikely(!test_duration_write())) - break; - if (caa_unlikely(wdelay)) - loop_sleep(wdelay); - if (caa_unlikely((URCU_TLS(nr_writes) & ((1 << 10) - 1)) == 0)) - rcu_quiescent_state(); - } - - rcu_unregister_thread(); - - printf_verbose("thread_end %s, tid %lu\n", - "writer", urcu_get_thread_id()); - printf_verbose("info tid %lu: nr_add %lu, nr_addexist %lu, nr_del %lu, " - "nr_delnoent %lu\n", urcu_get_thread_id(), - URCU_TLS(nr_add), - URCU_TLS(nr_addexist), - URCU_TLS(nr_del), - URCU_TLS(nr_delnoent)); - count->update_ops = URCU_TLS(nr_writes); - count->add = URCU_TLS(nr_add); - count->add_exist = URCU_TLS(nr_addexist); - count->remove = URCU_TLS(nr_del); - return ((void*)2); -} - -int test_hash_unique_populate_hash(void) -{ - struct lfht_test_node *node; - struct cds_lfht_node *ret_node; - - printf("Starting uniqueness test.\n"); - - URCU_TLS(rand_lookup) = urcu_get_thread_id() ^ time(NULL); - - if (!init_populate) - return 0; - - if (init_populate * 10 > init_pool_size) { - printf("WARNING: required to populate %lu nodes (-k), but random " -"pool is quite small (%lu values) and we are in add_unique (-u) or add_replace (-s) mode. Try with a " -"larger random pool (-p option). This may take a while...\n", init_populate, init_pool_size); - } - - while (URCU_TLS(nr_add) < init_populate) { - node = malloc(sizeof(struct lfht_test_node)); - lfht_test_node_init(node, - (void *)(((unsigned long) rand_r(&URCU_TLS(rand_lookup)) % init_pool_size) + init_pool_offset), - sizeof(void *)); - rcu_read_lock(); - ret_node = cds_lfht_add_replace(test_ht, - test_hash(node->key, node->key_len, TEST_HASH_SEED), - test_match, node->key, &node->node); - rcu_read_unlock(); - if (ret_node) { - call_rcu(&to_test_node(ret_node)->head, free_node_cb); - URCU_TLS(nr_addexist)++; - } else { - URCU_TLS(nr_add)++; - } - URCU_TLS(nr_writes)++; - } - return 0; -} diff --git a/tests/test_urcu_lfq.c b/tests/test_urcu_lfq.c deleted file mode 100644 index 5717f23..0000000 --- a/tests/test_urcu_lfq.c +++ /dev/null @@ -1,436 +0,0 @@ -/* - * test_urcu_lfq.c - * - * Userspace RCU library - example RCU-based lock-free queue - * - * Copyright February 2010 - Mathieu Desnoyers - * Copyright February 2010 - Paolo Bonzini - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include "../config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include "cpuset.h" -#include "thread-id.h" - -/* hardcoded number of CPUs */ -#define NR_CPUS 16384 - -#ifndef DYNAMIC_LINK_TEST -#define _LGPL_SOURCE -#endif -#include -#include - -static volatile int test_go, test_stop; - -static unsigned long rduration; - -static unsigned long duration; - -/* read-side C.S. duration, in loops */ -static unsigned long wdelay; - -static inline void loop_sleep(unsigned long loops) -{ - while (loops-- != 0) - caa_cpu_relax(); -} - -static int verbose_mode; - -#define printf_verbose(fmt, args...) \ - do { \ - if (verbose_mode) \ - printf(fmt, args); \ - } while (0) - -static unsigned int cpu_affinities[NR_CPUS]; -static unsigned int next_aff = 0; -static int use_affinity = 0; - -pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; - -static void set_affinity(void) -{ -#if HAVE_SCHED_SETAFFINITY - cpu_set_t mask; - int cpu, ret; -#endif /* HAVE_SCHED_SETAFFINITY */ - - if (!use_affinity) - return; - -#if HAVE_SCHED_SETAFFINITY - ret = pthread_mutex_lock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } - cpu = cpu_affinities[next_aff++]; - ret = pthread_mutex_unlock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } - - CPU_ZERO(&mask); - CPU_SET(cpu, &mask); -#if SCHED_SETAFFINITY_ARGS == 2 - sched_setaffinity(0, &mask); -#else - sched_setaffinity(0, sizeof(mask), &mask); -#endif -#endif /* HAVE_SCHED_SETAFFINITY */ -} - -/* - * returns 0 if test should end. - */ -static int test_duration_dequeue(void) -{ - return !test_stop; -} - -static int test_duration_enqueue(void) -{ - return !test_stop; -} - -static DEFINE_URCU_TLS(unsigned long long, nr_dequeues); -static DEFINE_URCU_TLS(unsigned long long, nr_enqueues); - -static DEFINE_URCU_TLS(unsigned long long, nr_successful_dequeues); -static DEFINE_URCU_TLS(unsigned long long, nr_successful_enqueues); - -static unsigned int nr_enqueuers; -static unsigned int nr_dequeuers; - -struct test { - struct cds_lfq_node_rcu list; - struct rcu_head rcu; -}; - -static struct cds_lfq_queue_rcu q; - -void *thr_enqueuer(void *_count) -{ - unsigned long long *count = _count; - - printf_verbose("thread_begin %s, tid %lu\n", - "enqueuer", urcu_get_thread_id()); - - set_affinity(); - - rcu_register_thread(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - struct test *node = malloc(sizeof(*node)); - if (!node) - goto fail; - cds_lfq_node_init_rcu(&node->list); - rcu_read_lock(); - cds_lfq_enqueue_rcu(&q, &node->list); - rcu_read_unlock(); - URCU_TLS(nr_successful_enqueues)++; - - if (caa_unlikely(wdelay)) - loop_sleep(wdelay); -fail: - URCU_TLS(nr_enqueues)++; - if (caa_unlikely(!test_duration_enqueue())) - break; - } - - rcu_unregister_thread(); - - count[0] = URCU_TLS(nr_enqueues); - count[1] = URCU_TLS(nr_successful_enqueues); - printf_verbose("enqueuer thread_end, tid %lu, " - "enqueues %llu successful_enqueues %llu\n", - urcu_get_thread_id(), - URCU_TLS(nr_enqueues), - URCU_TLS(nr_successful_enqueues)); - return ((void*)1); - -} - -static -void free_node_cb(struct rcu_head *head) -{ - struct test *node = - caa_container_of(head, struct test, rcu); - free(node); -} - -void *thr_dequeuer(void *_count) -{ - unsigned long long *count = _count; - - printf_verbose("thread_begin %s, tid %lu\n", - "dequeuer", urcu_get_thread_id()); - - set_affinity(); - - rcu_register_thread(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - struct cds_lfq_node_rcu *qnode; - - rcu_read_lock(); - qnode = cds_lfq_dequeue_rcu(&q); - rcu_read_unlock(); - - if (qnode) { - struct test *node; - - node = caa_container_of(qnode, struct test, list); - call_rcu(&node->rcu, free_node_cb); - URCU_TLS(nr_successful_dequeues)++; - } - - URCU_TLS(nr_dequeues)++; - if (caa_unlikely(!test_duration_dequeue())) - break; - if (caa_unlikely(rduration)) - loop_sleep(rduration); - } - - rcu_unregister_thread(); - printf_verbose("dequeuer thread_end, tid %lu, " - "dequeues %llu, successful_dequeues %llu\n", - urcu_get_thread_id(), - URCU_TLS(nr_dequeues), - URCU_TLS(nr_successful_dequeues)); - count[0] = URCU_TLS(nr_dequeues); - count[1] = URCU_TLS(nr_successful_dequeues); - return ((void*)2); -} - -void test_end(struct cds_lfq_queue_rcu *q, unsigned long long *nr_dequeues) -{ - struct cds_lfq_node_rcu *snode; - - do { - snode = cds_lfq_dequeue_rcu(q); - if (snode) { - struct test *node; - - node = caa_container_of(snode, struct test, list); - free(node); /* no more concurrent access */ - (*nr_dequeues)++; - } - } while (snode); -} - -void show_usage(int argc, char **argv) -{ - printf("Usage : %s nr_dequeuers nr_enqueuers duration (s) \n", - argv[0]); - printf("OPTIONS:\n"); - printf(" [-d delay] (enqueuer period (in loops))\n"); - printf(" [-c duration] (dequeuer period (in loops))\n"); - printf(" [-v] (verbose output)\n"); - printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); - printf("\n"); -} - -int main(int argc, char **argv) -{ - int err; - pthread_t *tid_enqueuer, *tid_dequeuer; - void *tret; - unsigned long long *count_enqueuer, *count_dequeuer; - unsigned long long tot_enqueues = 0, tot_dequeues = 0; - unsigned long long tot_successful_enqueues = 0, - tot_successful_dequeues = 0; - unsigned long long end_dequeues = 0; - int i, a; - - if (argc < 4) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[1], "%u", &nr_dequeuers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[2], "%u", &nr_enqueuers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[3], "%lu", &duration); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - for (i = 4; i < argc; i++) { - if (argv[i][0] != '-') - continue; - switch (argv[i][1]) { - case 'a': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - a = atoi(argv[++i]); - cpu_affinities[next_aff++] = a; - use_affinity = 1; - printf_verbose("Adding CPU %d affinity\n", a); - break; - case 'c': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - rduration = atol(argv[++i]); - break; - case 'd': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wdelay = atol(argv[++i]); - break; - case 'v': - verbose_mode = 1; - break; - } - } - - printf_verbose("running test for %lu seconds, %u enqueuers, " - "%u dequeuers.\n", - duration, nr_enqueuers, nr_dequeuers); - printf_verbose("Writer delay : %lu loops.\n", rduration); - printf_verbose("Reader duration : %lu loops.\n", wdelay); - printf_verbose("thread %-6s, tid %lu\n", - "main", urcu_get_thread_id()); - - tid_enqueuer = calloc(nr_enqueuers, sizeof(*tid_enqueuer)); - tid_dequeuer = calloc(nr_dequeuers, sizeof(*tid_dequeuer)); - count_enqueuer = calloc(nr_enqueuers, 2 * sizeof(*count_enqueuer)); - count_dequeuer = calloc(nr_dequeuers, 2 * sizeof(*count_dequeuer)); - cds_lfq_init_rcu(&q, call_rcu); - err = create_all_cpu_call_rcu_data(0); - if (err) { - printf("Per-CPU call_rcu() worker threads unavailable. Using default global worker thread.\n"); - } - - next_aff = 0; - - for (i = 0; i < nr_enqueuers; i++) { - err = pthread_create(&tid_enqueuer[i], NULL, thr_enqueuer, - &count_enqueuer[2 * i]); - if (err != 0) - exit(1); - } - for (i = 0; i < nr_dequeuers; i++) { - err = pthread_create(&tid_dequeuer[i], NULL, thr_dequeuer, - &count_dequeuer[2 * i]); - if (err != 0) - exit(1); - } - - cmm_smp_mb(); - - test_go = 1; - - for (i = 0; i < duration; i++) { - sleep(1); - if (verbose_mode) - write (1, ".", 1); - } - - test_stop = 1; - - for (i = 0; i < nr_enqueuers; i++) { - err = pthread_join(tid_enqueuer[i], &tret); - if (err != 0) - exit(1); - tot_enqueues += count_enqueuer[2 * i]; - tot_successful_enqueues += count_enqueuer[2 * i + 1]; - } - for (i = 0; i < nr_dequeuers; i++) { - err = pthread_join(tid_dequeuer[i], &tret); - if (err != 0) - exit(1); - tot_dequeues += count_dequeuer[2 * i]; - tot_successful_dequeues += count_dequeuer[2 * i + 1]; - } - - test_end(&q, &end_dequeues); - err = cds_lfq_destroy_rcu(&q); - assert(!err); - - printf_verbose("total number of enqueues : %llu, dequeues %llu\n", - tot_enqueues, tot_dequeues); - printf_verbose("total number of successful enqueues : %llu, " - "successful dequeues %llu\n", - tot_successful_enqueues, tot_successful_dequeues); - printf("SUMMARY %-25s testdur %4lu nr_enqueuers %3u wdelay %6lu " - "nr_dequeuers %3u " - "rdur %6lu nr_enqueues %12llu nr_dequeues %12llu " - "successful enqueues %12llu successful dequeues %12llu " - "end_dequeues %llu nr_ops %12llu\n", - argv[0], duration, nr_enqueuers, wdelay, - nr_dequeuers, rduration, tot_enqueues, tot_dequeues, - tot_successful_enqueues, - tot_successful_dequeues, end_dequeues, - tot_enqueues + tot_dequeues); - if (tot_successful_enqueues != tot_successful_dequeues + end_dequeues) - printf("WARNING! Discrepancy between nr succ. enqueues %llu vs " - "succ. dequeues + end dequeues %llu.\n", - tot_successful_enqueues, - tot_successful_dequeues + end_dequeues); - - free_all_cpu_call_rcu_data(); - free(count_enqueuer); - free(count_dequeuer); - free(tid_enqueuer); - free(tid_dequeuer); - return 0; -} diff --git a/tests/test_urcu_lfs.c b/tests/test_urcu_lfs.c deleted file mode 100644 index 5659d9a..0000000 --- a/tests/test_urcu_lfs.c +++ /dev/null @@ -1,518 +0,0 @@ -/* - * test_urcu_lfs.c - * - * Userspace RCU library - example lock-free stack - * - * Copyright 2010-2012 - Mathieu Desnoyers - * Copyright February 2010 - Paolo Bonzini - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include "../config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include "cpuset.h" -#include "thread-id.h" - -/* hardcoded number of CPUs */ -#define NR_CPUS 16384 - -#ifndef DYNAMIC_LINK_TEST -#define _LGPL_SOURCE -#endif -#include -#include - -/* - * External synchronization used. - */ -enum test_sync { - TEST_SYNC_NONE = 0, - TEST_SYNC_RCU, -}; - -static enum test_sync test_sync; - -static volatile int test_go, test_stop; - -static unsigned long rduration; - -static unsigned long duration; - -/* read-side C.S. duration, in loops */ -static unsigned long wdelay; - -static inline void loop_sleep(unsigned long loops) -{ - while (loops-- != 0) - caa_cpu_relax(); -} - -static int verbose_mode; - -static int test_pop, test_pop_all; - -#define printf_verbose(fmt, args...) \ - do { \ - if (verbose_mode) \ - printf(fmt, ## args); \ - } while (0) - -static unsigned int cpu_affinities[NR_CPUS]; -static unsigned int next_aff = 0; -static int use_affinity = 0; - -pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; - -static void set_affinity(void) -{ -#if HAVE_SCHED_SETAFFINITY - cpu_set_t mask; - int cpu, ret; -#endif /* HAVE_SCHED_SETAFFINITY */ - - if (!use_affinity) - return; - -#if HAVE_SCHED_SETAFFINITY - ret = pthread_mutex_lock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } - cpu = cpu_affinities[next_aff++]; - ret = pthread_mutex_unlock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } - - CPU_ZERO(&mask); - CPU_SET(cpu, &mask); -#if SCHED_SETAFFINITY_ARGS == 2 - sched_setaffinity(0, &mask); -#else - sched_setaffinity(0, sizeof(mask), &mask); -#endif -#endif /* HAVE_SCHED_SETAFFINITY */ -} - -/* - * returns 0 if test should end. - */ -static int test_duration_dequeue(void) -{ - return !test_stop; -} - -static int test_duration_enqueue(void) -{ - return !test_stop; -} - -static DEFINE_URCU_TLS(unsigned long long, nr_dequeues); -static DEFINE_URCU_TLS(unsigned long long, nr_enqueues); - -static DEFINE_URCU_TLS(unsigned long long, nr_successful_dequeues); -static DEFINE_URCU_TLS(unsigned long long, nr_successful_enqueues); - -static unsigned int nr_enqueuers; -static unsigned int nr_dequeuers; - -struct test { - struct cds_lfs_node list; - struct rcu_head rcu; -}; - -static struct cds_lfs_stack s; - -static void *thr_enqueuer(void *_count) -{ - unsigned long long *count = _count; - - printf_verbose("thread_begin %s, tid %lu\n", - "enqueuer", urcu_get_thread_id()); - - set_affinity(); - - rcu_register_thread(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - struct test *node = malloc(sizeof(*node)); - if (!node) - goto fail; - cds_lfs_node_init(&node->list); - cds_lfs_push(&s, &node->list); - URCU_TLS(nr_successful_enqueues)++; - - if (caa_unlikely(wdelay)) - loop_sleep(wdelay); -fail: - URCU_TLS(nr_enqueues)++; - if (caa_unlikely(!test_duration_enqueue())) - break; - } - - rcu_unregister_thread(); - - count[0] = URCU_TLS(nr_enqueues); - count[1] = URCU_TLS(nr_successful_enqueues); - printf_verbose("enqueuer thread_end, tid %lu, " - "enqueues %llu successful_enqueues %llu\n", - urcu_get_thread_id(), - URCU_TLS(nr_enqueues), - URCU_TLS(nr_successful_enqueues)); - return ((void*)1); - -} - -static -void free_node_cb(struct rcu_head *head) -{ - struct test *node = - caa_container_of(head, struct test, rcu); - free(node); -} - -static -void do_test_pop(enum test_sync sync) -{ - struct cds_lfs_node *snode; - - if (sync == TEST_SYNC_RCU) - rcu_read_lock(); - snode = __cds_lfs_pop(&s); - if (sync == TEST_SYNC_RCU) - rcu_read_unlock(); - if (snode) { - struct test *node; - - node = caa_container_of(snode, - struct test, list); - if (sync == TEST_SYNC_RCU) - call_rcu(&node->rcu, free_node_cb); - else - free(node); - URCU_TLS(nr_successful_dequeues)++; - } - URCU_TLS(nr_dequeues)++; -} - -static -void do_test_pop_all(enum test_sync sync) -{ - struct cds_lfs_node *snode; - struct cds_lfs_head *head; - struct cds_lfs_node *n; - - head = __cds_lfs_pop_all(&s); - cds_lfs_for_each_safe(head, snode, n) { - struct test *node; - - node = caa_container_of(snode, struct test, list); - if (sync == TEST_SYNC_RCU) - call_rcu(&node->rcu, free_node_cb); - else - free(node); - URCU_TLS(nr_successful_dequeues)++; - URCU_TLS(nr_dequeues)++; - } - -} - -static void *thr_dequeuer(void *_count) -{ - unsigned long long *count = _count; - unsigned int counter = 0; - - printf_verbose("thread_begin %s, tid %lu\n", - "dequeuer", urcu_get_thread_id()); - - set_affinity(); - - rcu_register_thread(); - - while (!test_go) - { - } - cmm_smp_mb(); - - assert(test_pop || test_pop_all); - - for (;;) { - if (test_pop && test_pop_all) { - /* both pop and pop all */ - if (counter & 1) - do_test_pop(test_sync); - else - do_test_pop_all(test_sync); - counter++; - } else { - if (test_pop) - do_test_pop(test_sync); - else - do_test_pop_all(test_sync); - } - - if (caa_unlikely(!test_duration_dequeue())) - break; - if (caa_unlikely(rduration)) - loop_sleep(rduration); - } - - rcu_unregister_thread(); - - printf_verbose("dequeuer thread_end, tid %lu, " - "dequeues %llu, successful_dequeues %llu\n", - urcu_get_thread_id(), - URCU_TLS(nr_dequeues), - URCU_TLS(nr_successful_dequeues)); - count[0] = URCU_TLS(nr_dequeues); - count[1] = URCU_TLS(nr_successful_dequeues); - return ((void*)2); -} - -static void test_end(struct cds_lfs_stack *s, unsigned long long *nr_dequeues) -{ - struct cds_lfs_node *snode; - - do { - snode = __cds_lfs_pop(s); - if (snode) { - struct test *node; - - node = caa_container_of(snode, struct test, list); - free(node); - (*nr_dequeues)++; - } - } while (snode); -} - -static void show_usage(int argc, char **argv) -{ - printf("Usage : %s nr_dequeuers nr_enqueuers duration (s) \n", - argv[0]); - printf("OPTIONS:\n"); - printf(" [-d delay] (enqueuer period (in loops))\n"); - printf(" [-c duration] (dequeuer period (in loops))\n"); - printf(" [-v] (verbose output)\n"); - printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); - printf(" [-p] (test pop)\n"); - printf(" [-P] (test pop_all, enabled by default)\n"); - printf(" [-R] (use RCU external synchronization)\n"); - printf(" Note: default: no external synchronization used.\n"); - printf("\n"); -} - -int main(int argc, char **argv) -{ - int err; - pthread_t *tid_enqueuer, *tid_dequeuer; - void *tret; - unsigned long long *count_enqueuer, *count_dequeuer; - unsigned long long tot_enqueues = 0, tot_dequeues = 0; - unsigned long long tot_successful_enqueues = 0, - tot_successful_dequeues = 0; - unsigned long long end_dequeues = 0; - int i, a; - - if (argc < 4) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[1], "%u", &nr_dequeuers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[2], "%u", &nr_enqueuers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[3], "%lu", &duration); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - for (i = 4; i < argc; i++) { - if (argv[i][0] != '-') - continue; - switch (argv[i][1]) { - case 'a': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - a = atoi(argv[++i]); - cpu_affinities[next_aff++] = a; - use_affinity = 1; - printf_verbose("Adding CPU %d affinity\n", a); - break; - case 'c': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - rduration = atol(argv[++i]); - break; - case 'd': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wdelay = atol(argv[++i]); - break; - case 'v': - verbose_mode = 1; - break; - case 'p': - test_pop = 1; - break; - case 'P': - test_pop_all = 1; - break; - case 'R': - test_sync = TEST_SYNC_RCU; - break; - } - } - - /* activate pop_all test by default */ - if (!test_pop && !test_pop_all) - test_pop_all = 1; - - printf_verbose("running test for %lu seconds, %u enqueuers, " - "%u dequeuers.\n", - duration, nr_enqueuers, nr_dequeuers); - if (test_pop) - printf_verbose("pop test activated.\n"); - if (test_pop_all) - printf_verbose("pop_all test activated.\n"); - if (test_sync == TEST_SYNC_RCU) - printf_verbose("External sync: RCU.\n"); - else - printf_verbose("External sync: none.\n"); - printf_verbose("Writer delay : %lu loops.\n", rduration); - printf_verbose("Reader duration : %lu loops.\n", wdelay); - printf_verbose("thread %-6s, tid %lu\n", - "main", urcu_get_thread_id()); - - tid_enqueuer = calloc(nr_enqueuers, sizeof(*tid_enqueuer)); - tid_dequeuer = calloc(nr_dequeuers, sizeof(*tid_dequeuer)); - count_enqueuer = calloc(nr_enqueuers, 2 * sizeof(*count_enqueuer)); - count_dequeuer = calloc(nr_dequeuers, 2 * sizeof(*count_dequeuer)); - cds_lfs_init(&s); - err = create_all_cpu_call_rcu_data(0); - if (err) { - printf("Per-CPU call_rcu() worker threads unavailable. Using default global worker thread.\n"); - } - - next_aff = 0; - - for (i = 0; i < nr_enqueuers; i++) { - err = pthread_create(&tid_enqueuer[i], NULL, thr_enqueuer, - &count_enqueuer[2 * i]); - if (err != 0) - exit(1); - } - for (i = 0; i < nr_dequeuers; i++) { - err = pthread_create(&tid_dequeuer[i], NULL, thr_dequeuer, - &count_dequeuer[2 * i]); - if (err != 0) - exit(1); - } - - cmm_smp_mb(); - - test_go = 1; - - for (i = 0; i < duration; i++) { - sleep(1); - if (verbose_mode) - write (1, ".", 1); - } - - test_stop = 1; - - for (i = 0; i < nr_enqueuers; i++) { - err = pthread_join(tid_enqueuer[i], &tret); - if (err != 0) - exit(1); - tot_enqueues += count_enqueuer[2 * i]; - tot_successful_enqueues += count_enqueuer[2 * i + 1]; - } - for (i = 0; i < nr_dequeuers; i++) { - err = pthread_join(tid_dequeuer[i], &tret); - if (err != 0) - exit(1); - tot_dequeues += count_dequeuer[2 * i]; - tot_successful_dequeues += count_dequeuer[2 * i + 1]; - } - - test_end(&s, &end_dequeues); - - printf_verbose("total number of enqueues : %llu, dequeues %llu\n", - tot_enqueues, tot_dequeues); - printf_verbose("total number of successful enqueues : %llu, " - "successful dequeues %llu\n", - tot_successful_enqueues, tot_successful_dequeues); - printf("SUMMARY %-25s testdur %4lu nr_enqueuers %3u wdelay %6lu " - "nr_dequeuers %3u " - "rdur %6lu nr_enqueues %12llu nr_dequeues %12llu " - "successful enqueues %12llu successful dequeues %12llu " - "end_dequeues %llu nr_ops %12llu\n", - argv[0], duration, nr_enqueuers, wdelay, - nr_dequeuers, rduration, tot_enqueues, tot_dequeues, - tot_successful_enqueues, - tot_successful_dequeues, end_dequeues, - tot_enqueues + tot_dequeues); - if (tot_successful_enqueues != tot_successful_dequeues + end_dequeues) - printf("WARNING! Discrepancy between nr succ. enqueues %llu vs " - "succ. dequeues + end dequeues %llu.\n", - tot_successful_enqueues, - tot_successful_dequeues + end_dequeues); - - free_all_cpu_call_rcu_data(); - free(count_enqueuer); - free(count_dequeuer); - free(tid_enqueuer); - free(tid_dequeuer); - return 0; -} diff --git a/tests/test_urcu_lfs_rcu.c b/tests/test_urcu_lfs_rcu.c deleted file mode 100644 index b76bde1..0000000 --- a/tests/test_urcu_lfs_rcu.c +++ /dev/null @@ -1,435 +0,0 @@ -/* - * test_urcu_lfs_rcu.c - * - * Userspace RCU library - example RCU-based lock-free stack - * - * Copyright February 2010 - Mathieu Desnoyers - * Copyright February 2010 - Paolo Bonzini - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include "../config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include "cpuset.h" -#include "thread-id.h" - -/* hardcoded number of CPUs */ -#define NR_CPUS 16384 - -#ifndef DYNAMIC_LINK_TEST -#define _LGPL_SOURCE -#endif -#include - -/* Remove deprecation warnings from test build. */ -#define CDS_LFS_RCU_DEPRECATED - -#include - -static volatile int test_go, test_stop; - -static unsigned long rduration; - -static unsigned long duration; - -/* read-side C.S. duration, in loops */ -static unsigned long wdelay; - -static inline void loop_sleep(unsigned long loops) -{ - while (loops-- != 0) - caa_cpu_relax(); -} - -static int verbose_mode; - -#define printf_verbose(fmt, args...) \ - do { \ - if (verbose_mode) \ - printf(fmt, args); \ - } while (0) - -static unsigned int cpu_affinities[NR_CPUS]; -static unsigned int next_aff = 0; -static int use_affinity = 0; - -pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; - -static void set_affinity(void) -{ - cpu_set_t mask; - int cpu; - int ret; - - if (!use_affinity) - return; - -#if HAVE_SCHED_SETAFFINITY - ret = pthread_mutex_lock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } - cpu = cpu_affinities[next_aff++]; - ret = pthread_mutex_unlock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } - - CPU_ZERO(&mask); - CPU_SET(cpu, &mask); -#if SCHED_SETAFFINITY_ARGS == 2 - sched_setaffinity(0, &mask); -#else - sched_setaffinity(0, sizeof(mask), &mask); -#endif -#endif /* HAVE_SCHED_SETAFFINITY */ -} - -/* - * returns 0 if test should end. - */ -static int test_duration_dequeue(void) -{ - return !test_stop; -} - -static int test_duration_enqueue(void) -{ - return !test_stop; -} - -static DEFINE_URCU_TLS(unsigned long long, nr_dequeues); -static DEFINE_URCU_TLS(unsigned long long, nr_enqueues); - -static DEFINE_URCU_TLS(unsigned long long, nr_successful_dequeues); -static DEFINE_URCU_TLS(unsigned long long, nr_successful_enqueues); - -static unsigned int nr_enqueuers; -static unsigned int nr_dequeuers; - -struct test { - struct cds_lfs_node_rcu list; - struct rcu_head rcu; -}; - -static struct cds_lfs_stack_rcu s; - -void *thr_enqueuer(void *_count) -{ - unsigned long long *count = _count; - - printf_verbose("thread_begin %s, tid %lu\n", - "enqueuer", urcu_get_thread_id()); - - set_affinity(); - - rcu_register_thread(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - struct test *node = malloc(sizeof(*node)); - if (!node) - goto fail; - cds_lfs_node_init_rcu(&node->list); - /* No rcu read-side is needed for push */ - cds_lfs_push_rcu(&s, &node->list); - URCU_TLS(nr_successful_enqueues)++; - - if (caa_unlikely(wdelay)) - loop_sleep(wdelay); -fail: - URCU_TLS(nr_enqueues)++; - if (caa_unlikely(!test_duration_enqueue())) - break; - } - - rcu_unregister_thread(); - - count[0] = URCU_TLS(nr_enqueues); - count[1] = URCU_TLS(nr_successful_enqueues); - printf_verbose("enqueuer thread_end, tid %lu, " - "enqueues %llu successful_enqueues %llu\n", - urcu_get_thread_id(), - URCU_TLS(nr_enqueues), - URCU_TLS(nr_successful_enqueues)); - return ((void*)1); - -} - -static -void free_node_cb(struct rcu_head *head) -{ - struct test *node = - caa_container_of(head, struct test, rcu); - free(node); -} - -void *thr_dequeuer(void *_count) -{ - unsigned long long *count = _count; - - printf_verbose("thread_begin %s, tid %lu\n", - "dequeuer", urcu_get_thread_id()); - - set_affinity(); - - rcu_register_thread(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - struct cds_lfs_node_rcu *snode; - - rcu_read_lock(); - snode = cds_lfs_pop_rcu(&s); - rcu_read_unlock(); - if (snode) { - struct test *node; - - node = caa_container_of(snode, struct test, list); - call_rcu(&node->rcu, free_node_cb); - URCU_TLS(nr_successful_dequeues)++; - } - URCU_TLS(nr_dequeues)++; - if (caa_unlikely(!test_duration_dequeue())) - break; - if (caa_unlikely(rduration)) - loop_sleep(rduration); - } - - rcu_unregister_thread(); - - printf_verbose("dequeuer thread_end, tid %lu, " - "dequeues %llu, successful_dequeues %llu\n", - urcu_get_thread_id(), - URCU_TLS(nr_dequeues), - URCU_TLS(nr_successful_dequeues)); - count[0] = URCU_TLS(nr_dequeues); - count[1] = URCU_TLS(nr_successful_dequeues); - return ((void*)2); -} - -void test_end(struct cds_lfs_stack_rcu *s, unsigned long long *nr_dequeues) -{ - struct cds_lfs_node_rcu *snode; - - do { - snode = cds_lfs_pop_rcu(s); - if (snode) { - struct test *node; - - node = caa_container_of(snode, struct test, list); - free(node); - (*nr_dequeues)++; - } - } while (snode); -} - -void show_usage(int argc, char **argv) -{ - printf("Usage : %s nr_dequeuers nr_enqueuers duration (s) \n", - argv[0]); - printf("OPTIONS:\n"); - printf(" [-d delay] (enqueuer period (in loops))\n"); - printf(" [-c duration] (dequeuer period (in loops))\n"); - printf(" [-v] (verbose output)\n"); - printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); - printf("\n"); -} - -int main(int argc, char **argv) -{ - int err; - pthread_t *tid_enqueuer, *tid_dequeuer; - void *tret; - unsigned long long *count_enqueuer, *count_dequeuer; - unsigned long long tot_enqueues = 0, tot_dequeues = 0; - unsigned long long tot_successful_enqueues = 0, - tot_successful_dequeues = 0; - unsigned long long end_dequeues = 0; - int i, a; - - if (argc < 4) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[1], "%u", &nr_dequeuers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[2], "%u", &nr_enqueuers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[3], "%lu", &duration); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - for (i = 4; i < argc; i++) { - if (argv[i][0] != '-') - continue; - switch (argv[i][1]) { - case 'a': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - a = atoi(argv[++i]); - cpu_affinities[next_aff++] = a; - use_affinity = 1; - printf_verbose("Adding CPU %d affinity\n", a); - break; - case 'c': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - rduration = atol(argv[++i]); - break; - case 'd': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wdelay = atol(argv[++i]); - break; - case 'v': - verbose_mode = 1; - break; - } - } - - printf_verbose("running test for %lu seconds, %u enqueuers, " - "%u dequeuers.\n", - duration, nr_enqueuers, nr_dequeuers); - printf_verbose("Writer delay : %lu loops.\n", rduration); - printf_verbose("Reader duration : %lu loops.\n", wdelay); - printf_verbose("thread %-6s, tid %lu\n", - "main", urcu_get_thread_id()); - - tid_enqueuer = calloc(nr_enqueuers, sizeof(*tid_enqueuer)); - tid_dequeuer = calloc(nr_dequeuers, sizeof(*tid_dequeuer)); - count_enqueuer = calloc(nr_enqueuers, 2 * sizeof(*count_enqueuer)); - count_dequeuer = calloc(nr_dequeuers, 2 * sizeof(*count_dequeuer)); - cds_lfs_init_rcu(&s); - err = create_all_cpu_call_rcu_data(0); - if (err) { - printf("Per-CPU call_rcu() worker threads unavailable. Using default global worker thread.\n"); - } - - next_aff = 0; - - for (i = 0; i < nr_enqueuers; i++) { - err = pthread_create(&tid_enqueuer[i], NULL, thr_enqueuer, - &count_enqueuer[2 * i]); - if (err != 0) - exit(1); - } - for (i = 0; i < nr_dequeuers; i++) { - err = pthread_create(&tid_dequeuer[i], NULL, thr_dequeuer, - &count_dequeuer[2 * i]); - if (err != 0) - exit(1); - } - - cmm_smp_mb(); - - test_go = 1; - - for (i = 0; i < duration; i++) { - sleep(1); - if (verbose_mode) - write (1, ".", 1); - } - - test_stop = 1; - - for (i = 0; i < nr_enqueuers; i++) { - err = pthread_join(tid_enqueuer[i], &tret); - if (err != 0) - exit(1); - tot_enqueues += count_enqueuer[2 * i]; - tot_successful_enqueues += count_enqueuer[2 * i + 1]; - } - for (i = 0; i < nr_dequeuers; i++) { - err = pthread_join(tid_dequeuer[i], &tret); - if (err != 0) - exit(1); - tot_dequeues += count_dequeuer[2 * i]; - tot_successful_dequeues += count_dequeuer[2 * i + 1]; - } - - test_end(&s, &end_dequeues); - - printf_verbose("total number of enqueues : %llu, dequeues %llu\n", - tot_enqueues, tot_dequeues); - printf_verbose("total number of successful enqueues : %llu, " - "successful dequeues %llu\n", - tot_successful_enqueues, tot_successful_dequeues); - printf("SUMMARY %-25s testdur %4lu nr_enqueuers %3u wdelay %6lu " - "nr_dequeuers %3u " - "rdur %6lu nr_enqueues %12llu nr_dequeues %12llu " - "successful enqueues %12llu successful dequeues %12llu " - "end_dequeues %llu nr_ops %12llu\n", - argv[0], duration, nr_enqueuers, wdelay, - nr_dequeuers, rduration, tot_enqueues, tot_dequeues, - tot_successful_enqueues, - tot_successful_dequeues, end_dequeues, - tot_enqueues + tot_dequeues); - if (tot_successful_enqueues != tot_successful_dequeues + end_dequeues) - printf("WARNING! Discrepancy between nr succ. enqueues %llu vs " - "succ. dequeues + end dequeues %llu.\n", - tot_successful_enqueues, - tot_successful_dequeues + end_dequeues); - - free_all_cpu_call_rcu_data(); - free(count_enqueuer); - free(count_dequeuer); - free(tid_enqueuer); - free(tid_dequeuer); - return 0; -} diff --git a/tests/test_urcu_multiflavor-bp.c b/tests/test_urcu_multiflavor-bp.c deleted file mode 100644 index 199818b..0000000 --- a/tests/test_urcu_multiflavor-bp.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * test_urcu_multiflavor-bp.c - * - * Userspace RCU library - test multiple RCU flavors into one program - * - * Copyright February 2012 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef DYNAMIC_LINK_TEST -#define _LGPL_SOURCE -#endif - -#define RCU_SIGNAL -#include -#include "test_urcu_multiflavor.h" - -int test_mf_bp(void) -{ - rcu_register_thread(); - rcu_read_lock(); - rcu_read_unlock(); - synchronize_rcu(); - rcu_unregister_thread(); - return 0; -} diff --git a/tests/test_urcu_multiflavor-mb.c b/tests/test_urcu_multiflavor-mb.c deleted file mode 100644 index e4ba6a4..0000000 --- a/tests/test_urcu_multiflavor-mb.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * test_urcu_multiflavor-mb.c - * - * Userspace RCU library - test multiple RCU flavors into one program - * - * Copyright February 2012 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef DYNAMIC_LINK_TEST -#define _LGPL_SOURCE -#endif - -#define RCU_MB -#include -#include "test_urcu_multiflavor.h" - -int test_mf_mb(void) -{ - rcu_register_thread(); - rcu_read_lock(); - rcu_read_unlock(); - synchronize_rcu(); - rcu_unregister_thread(); - return 0; -} diff --git a/tests/test_urcu_multiflavor-memb.c b/tests/test_urcu_multiflavor-memb.c deleted file mode 100644 index 583f2e1..0000000 --- a/tests/test_urcu_multiflavor-memb.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * test_urcu_multiflavor-memb.c - * - * Userspace RCU library - test multiple RCU flavors into one program - * - * Copyright February 2012 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef DYNAMIC_LINK_TEST -#define _LGPL_SOURCE -#endif - -#include -#include "test_urcu_multiflavor.h" - -int test_mf_memb(void) -{ - rcu_register_thread(); - rcu_read_lock(); - rcu_read_unlock(); - synchronize_rcu(); - rcu_unregister_thread(); - return 0; -} diff --git a/tests/test_urcu_multiflavor-qsbr.c b/tests/test_urcu_multiflavor-qsbr.c deleted file mode 100644 index 64f32f4..0000000 --- a/tests/test_urcu_multiflavor-qsbr.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * test_urcu_multiflavor-qsbr.c - * - * Userspace RCU library - test multiple RCU flavors into one program - * - * Copyright February 2012 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef DYNAMIC_LINK_TEST -#define _LGPL_SOURCE -#endif - -#include -#include "test_urcu_multiflavor.h" - -int test_mf_qsbr(void) -{ - rcu_register_thread(); - rcu_read_lock(); - rcu_read_unlock(); - synchronize_rcu(); - rcu_unregister_thread(); - return 0; -} diff --git a/tests/test_urcu_multiflavor-signal.c b/tests/test_urcu_multiflavor-signal.c deleted file mode 100644 index 816c615..0000000 --- a/tests/test_urcu_multiflavor-signal.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * test_urcu_multiflavor-signal.c - * - * Userspace RCU library - test multiple RCU flavors into one program - * - * Copyright February 2012 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef DYNAMIC_LINK_TEST -#define _LGPL_SOURCE -#endif - -#define RCU_SIGNAL -#include -#include "test_urcu_multiflavor.h" - -int test_mf_signal(void) -{ - rcu_register_thread(); - rcu_read_lock(); - rcu_read_unlock(); - synchronize_rcu(); - rcu_unregister_thread(); - return 0; -} diff --git a/tests/test_urcu_multiflavor.c b/tests/test_urcu_multiflavor.c deleted file mode 100644 index cd573ca..0000000 --- a/tests/test_urcu_multiflavor.c +++ /dev/null @@ -1,50 +0,0 @@ -/* - * test_urcu_multiflavor.c - * - * Userspace RCU library - test multiple RCU flavors into one program - * - * Copyright February 2012 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include "test_urcu_multiflavor.h" - -int main(int argc, char **argv) -{ - int ret; - - ret = test_mf_memb(); - if (ret) - goto failure; - ret = test_mf_mb(); - if (ret) - goto failure; - ret = test_mf_signal(); - if (ret) - goto failure; - ret = test_mf_qsbr(); - if (ret) - goto failure; - ret = test_mf_bp(); - if (ret) - goto failure; - - exit(EXIT_SUCCESS); - -failure: - exit(EXIT_FAILURE); -} diff --git a/tests/test_urcu_multiflavor.h b/tests/test_urcu_multiflavor.h deleted file mode 100644 index 419ad5d..0000000 --- a/tests/test_urcu_multiflavor.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * test_urcu_multiflavor.h - * - * Userspace RCU library - test multiple RCU flavors into one program - * - * Copyright February 2012 - Mathieu Desnoyers - * Copyright February 2012 - Lai Jiangshan - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -extern int test_mf_memb(void); -extern int test_mf_mb(void); -extern int test_mf_signal(void); -extern int test_mf_qsbr(void); -extern int test_mf_bp(void); - diff --git a/tests/test_urcu_qsbr.c b/tests/test_urcu_qsbr.c deleted file mode 100644 index 85d8430..0000000 --- a/tests/test_urcu_qsbr.c +++ /dev/null @@ -1,410 +0,0 @@ -/* - * test_urcu.c - * - * Userspace RCU library - test program - * - * Copyright February 2009 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include "../config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include "cpuset.h" -#include "thread-id.h" - -/* hardcoded number of CPUs */ -#define NR_CPUS 16384 - -#ifndef DYNAMIC_LINK_TEST -#define _LGPL_SOURCE -#else -#define rcu_debug_yield_read() -#endif -#include "urcu-qsbr.h" - -static volatile int test_go, test_stop; - -static unsigned long wdelay; - -static int *test_rcu_pointer; - -static unsigned long duration; - -/* read-side C.S. duration, in loops */ -static unsigned long rduration; - -/* write-side C.S. duration, in loops */ -static unsigned long wduration; - -static inline void loop_sleep(unsigned long loops) -{ - while (loops-- != 0) - caa_cpu_relax(); -} - -static int verbose_mode; - -#define printf_verbose(fmt, args...) \ - do { \ - if (verbose_mode) \ - printf(fmt, args); \ - } while (0) - -static unsigned int cpu_affinities[NR_CPUS]; -static unsigned int next_aff = 0; -static int use_affinity = 0; - -pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; - -static void set_affinity(void) -{ -#if HAVE_SCHED_SETAFFINITY - cpu_set_t mask; - int cpu, ret; -#endif /* HAVE_SCHED_SETAFFINITY */ - - if (!use_affinity) - return; - -#if HAVE_SCHED_SETAFFINITY - ret = pthread_mutex_lock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } - cpu = cpu_affinities[next_aff++]; - ret = pthread_mutex_unlock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } - CPU_ZERO(&mask); - CPU_SET(cpu, &mask); -#if SCHED_SETAFFINITY_ARGS == 2 - sched_setaffinity(0, &mask); -#else - sched_setaffinity(0, sizeof(mask), &mask); -#endif -#endif /* HAVE_SCHED_SETAFFINITY */ -} - -/* - * returns 0 if test should end. - */ -static int test_duration_write(void) -{ - return !test_stop; -} - -static int test_duration_read(void) -{ - return !test_stop; -} - -static DEFINE_URCU_TLS(unsigned long long, nr_writes); -static DEFINE_URCU_TLS(unsigned long long, nr_reads); - -static unsigned int nr_readers; -static unsigned int nr_writers; - -pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; - -void rcu_copy_mutex_lock(void) -{ - int ret; - ret = pthread_mutex_lock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } -} - -void rcu_copy_mutex_unlock(void) -{ - int ret; - - ret = pthread_mutex_unlock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } -} - -void *thr_reader(void *_count) -{ - unsigned long long *count = _count; - int *local_ptr; - - printf_verbose("thread_begin %s, tid %lu\n", - "reader", urcu_get_thread_id()); - - set_affinity(); - - rcu_register_thread(); - - assert(rcu_read_ongoing()); - rcu_thread_offline(); - assert(!rcu_read_ongoing()); - rcu_thread_online(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - rcu_read_lock(); - assert(rcu_read_ongoing()); - local_ptr = rcu_dereference(test_rcu_pointer); - rcu_debug_yield_read(); - if (local_ptr) - assert(*local_ptr == 8); - if (caa_unlikely(rduration)) - loop_sleep(rduration); - rcu_read_unlock(); - URCU_TLS(nr_reads)++; - /* QS each 1024 reads */ - if (caa_unlikely((URCU_TLS(nr_reads) & ((1 << 10) - 1)) == 0)) - rcu_quiescent_state(); - if (caa_unlikely(!test_duration_read())) - break; - } - - rcu_unregister_thread(); - - /* test extra thread registration */ - rcu_register_thread(); - rcu_unregister_thread(); - - *count = URCU_TLS(nr_reads); - printf_verbose("thread_end %s, tid %lu\n", - "reader", urcu_get_thread_id()); - return ((void*)1); - -} - -void *thr_writer(void *_count) -{ - unsigned long long *count = _count; - int *new, *old; - - printf_verbose("thread_begin %s, tid %lu\n", - "writer", urcu_get_thread_id()); - - set_affinity(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - new = malloc(sizeof(int)); - assert(new); - *new = 8; - old = rcu_xchg_pointer(&test_rcu_pointer, new); - if (caa_unlikely(wduration)) - loop_sleep(wduration); - synchronize_rcu(); - if (old) - *old = 0; - free(old); - URCU_TLS(nr_writes)++; - if (caa_unlikely(!test_duration_write())) - break; - if (caa_unlikely(wdelay)) - loop_sleep(wdelay); - } - - printf_verbose("thread_end %s, tid %lu\n", - "writer", urcu_get_thread_id()); - *count = URCU_TLS(nr_writes); - return ((void*)2); -} - -void show_usage(int argc, char **argv) -{ - printf("Usage : %s nr_readers nr_writers duration (s) \n", - argv[0]); - printf("OPTIONS:\n"); -#ifdef DEBUG_YIELD - printf(" [-r] [-w] (yield reader and/or writer)\n"); -#endif - printf(" [-d delay] (writer period (us))\n"); - printf(" [-c duration] (reader C.S. duration (in loops))\n"); - printf(" [-e duration] (writer C.S. duration (in loops))\n"); - printf(" [-v] (verbose output)\n"); - printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); - printf("\n"); -} - -int main(int argc, char **argv) -{ - int err; - pthread_t *tid_reader, *tid_writer; - void *tret; - unsigned long long *count_reader, *count_writer; - unsigned long long tot_reads = 0, tot_writes = 0; - int i, a; - - if (argc < 4) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[1], "%u", &nr_readers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[2], "%u", &nr_writers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[3], "%lu", &duration); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - for (i = 4; i < argc; i++) { - if (argv[i][0] != '-') - continue; - switch (argv[i][1]) { -#ifdef DEBUG_YIELD - case 'r': - rcu_yield_active |= RCU_RCU_YIELD_READ; - break; - case 'w': - rcu_yield_active |= RCU_RCU_YIELD_WRITE; - break; -#endif - case 'a': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - a = atoi(argv[++i]); - cpu_affinities[next_aff++] = a; - use_affinity = 1; - printf_verbose("Adding CPU %d affinity\n", a); - break; - case 'c': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - rduration = atol(argv[++i]); - break; - case 'd': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wdelay = atol(argv[++i]); - break; - case 'e': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wduration = atol(argv[++i]); - break; - case 'v': - verbose_mode = 1; - break; - } - } - - printf_verbose("running test for %lu seconds, %u readers, %u writers.\n", - duration, nr_readers, nr_writers); - printf_verbose("Writer delay : %lu loops.\n", wdelay); - printf_verbose("Reader duration : %lu loops.\n", rduration); - printf_verbose("thread %-6s, tid %lu\n", - "main", urcu_get_thread_id()); - - tid_reader = calloc(nr_readers, sizeof(*tid_reader)); - tid_writer = calloc(nr_writers, sizeof(*tid_writer)); - count_reader = calloc(nr_readers, sizeof(*count_reader)); - count_writer = calloc(nr_writers, sizeof(*count_writer)); - - next_aff = 0; - - for (i = 0; i < nr_readers; i++) { - err = pthread_create(&tid_reader[i], NULL, thr_reader, - &count_reader[i]); - if (err != 0) - exit(1); - } - for (i = 0; i < nr_writers; i++) { - err = pthread_create(&tid_writer[i], NULL, thr_writer, - &count_writer[i]); - if (err != 0) - exit(1); - } - - cmm_smp_mb(); - - test_go = 1; - - sleep(duration); - - test_stop = 1; - - for (i = 0; i < nr_readers; i++) { - err = pthread_join(tid_reader[i], &tret); - if (err != 0) - exit(1); - tot_reads += count_reader[i]; - } - for (i = 0; i < nr_writers; i++) { - err = pthread_join(tid_writer[i], &tret); - if (err != 0) - exit(1); - tot_writes += count_writer[i]; - } - - printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads, - tot_writes); - printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu " - "nr_writers %3u " - "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu\n", - argv[0], duration, nr_readers, rduration, wduration, - nr_writers, wdelay, tot_reads, tot_writes, - tot_reads + tot_writes); - free(test_rcu_pointer); - free(tid_reader); - free(tid_writer); - free(count_reader); - free(count_writer); - return 0; -} diff --git a/tests/test_urcu_qsbr_gc.c b/tests/test_urcu_qsbr_gc.c deleted file mode 100644 index e0a2004..0000000 --- a/tests/test_urcu_qsbr_gc.c +++ /dev/null @@ -1,470 +0,0 @@ -/* - * test_urcu_gc.c - * - * Userspace RCU library - test program (with baatch reclamation) - * - * Copyright February 2009 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include "../config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include "cpuset.h" -#include "thread-id.h" - -/* hardcoded number of CPUs */ -#define NR_CPUS 16384 - -#define _LGPL_SOURCE -#include - -struct test_array { - int a; -}; - -static volatile int test_go, test_stop; - -static unsigned long wdelay; - -static struct test_array *test_rcu_pointer; - -static unsigned long duration; - -/* read-side C.S. duration, in loops */ -static unsigned long rduration; -static unsigned int reclaim_batch = 1; - -struct reclaim_queue { - void **queue; /* Beginning of queue */ - void **head; /* Insert position */ -}; - -static struct reclaim_queue *pending_reclaims; - - -/* write-side C.S. duration, in loops */ -static unsigned long wduration; - -static inline void loop_sleep(unsigned long loops) -{ - while (loops-- != 0) - caa_cpu_relax(); -} - -static int verbose_mode; - -#define printf_verbose(fmt, args...) \ - do { \ - if (verbose_mode) \ - printf(fmt, args); \ - } while (0) - -static unsigned int cpu_affinities[NR_CPUS]; -static unsigned int next_aff = 0; -static int use_affinity = 0; - -pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; - -static void set_affinity(void) -{ -#if HAVE_SCHED_SETAFFINITY - cpu_set_t mask; - int cpu, ret; -#endif /* HAVE_SCHED_SETAFFINITY */ - - if (!use_affinity) - return; - -#if HAVE_SCHED_SETAFFINITY - ret = pthread_mutex_lock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } - cpu = cpu_affinities[next_aff++]; - ret = pthread_mutex_unlock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } - - CPU_ZERO(&mask); - CPU_SET(cpu, &mask); -#if SCHED_SETAFFINITY_ARGS == 2 - sched_setaffinity(0, &mask); -#else - sched_setaffinity(0, sizeof(mask), &mask); -#endif -#endif /* HAVE_SCHED_SETAFFINITY */ -} - -/* - * returns 0 if test should end. - */ -static int test_duration_write(void) -{ - return !test_stop; -} - -static int test_duration_read(void) -{ - return !test_stop; -} - -static DEFINE_URCU_TLS(unsigned long long, nr_writes); -static DEFINE_URCU_TLS(unsigned long long, nr_reads); - -static unsigned int nr_readers; -static unsigned int nr_writers; - -pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; -static -unsigned long long __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *tot_nr_writes; - - -void rcu_copy_mutex_lock(void) -{ - int ret; - ret = pthread_mutex_lock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } -} - -void rcu_copy_mutex_unlock(void) -{ - int ret; - - ret = pthread_mutex_unlock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } -} - -void *thr_reader(void *_count) -{ - unsigned long long *count = _count; - struct test_array *local_ptr; - - printf_verbose("thread_begin %s, tid %lu\n", - "reader", urcu_get_thread_id()); - - set_affinity(); - - rcu_register_thread(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - _rcu_read_lock(); - local_ptr = _rcu_dereference(test_rcu_pointer); - rcu_debug_yield_read(); - if (local_ptr) - assert(local_ptr->a == 8); - if (caa_unlikely(rduration)) - loop_sleep(rduration); - _rcu_read_unlock(); - URCU_TLS(nr_reads)++; - /* QS each 1024 reads */ - if (caa_unlikely((URCU_TLS(nr_reads) & ((1 << 10) - 1)) == 0)) - _rcu_quiescent_state(); - if (caa_unlikely(!test_duration_read())) - break; - } - - rcu_unregister_thread(); - - *count = URCU_TLS(nr_reads); - printf_verbose("thread_end %s, tid %lu\n", - "reader", urcu_get_thread_id()); - return ((void*)1); - -} - -static void rcu_gc_clear_queue(unsigned long wtidx) -{ - void **p; - - /* Wait for Q.S and empty queue */ - synchronize_rcu(); - - for (p = pending_reclaims[wtidx].queue; - p < pending_reclaims[wtidx].head; p++) { - /* poison */ - if (*p) - ((struct test_array *)*p)->a = 0; - free(*p); - } - pending_reclaims[wtidx].head = pending_reclaims[wtidx].queue; -} - -/* Using per-thread queue */ -static void rcu_gc_reclaim(unsigned long wtidx, void *old) -{ - /* Queue pointer */ - *pending_reclaims[wtidx].head = old; - pending_reclaims[wtidx].head++; - - if (caa_likely(pending_reclaims[wtidx].head - pending_reclaims[wtidx].queue - < reclaim_batch)) - return; - - rcu_gc_clear_queue(wtidx); -} - -void *thr_writer(void *data) -{ - unsigned long wtidx = (unsigned long)data; -#ifdef TEST_LOCAL_GC - struct test_array *old = NULL; -#else - struct test_array *new, *old; -#endif - - printf_verbose("thread_begin %s, tid %lu\n", - "writer", urcu_get_thread_id()); - - set_affinity(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { -#ifndef TEST_LOCAL_GC - new = malloc(sizeof(*new)); - new->a = 8; - old = _rcu_xchg_pointer(&test_rcu_pointer, new); -#endif - if (caa_unlikely(wduration)) - loop_sleep(wduration); - rcu_gc_reclaim(wtidx, old); - URCU_TLS(nr_writes)++; - if (caa_unlikely(!test_duration_write())) - break; - if (caa_unlikely(wdelay)) - loop_sleep(wdelay); - } - - printf_verbose("thread_end %s, tid %lu\n", - "writer", urcu_get_thread_id()); - tot_nr_writes[wtidx] = URCU_TLS(nr_writes); - return ((void*)2); -} - -void show_usage(int argc, char **argv) -{ - printf("Usage : %s nr_readers nr_writers duration (s) \n", - argv[0]); - printf("OPTIONS:\n"); -#ifdef DEBUG_YIELD - printf(" [-r] [-w] (yield reader and/or writer)\n"); -#endif - printf(" [-b batch] (batch reclaim)\n"); - printf(" [-d delay] (writer period (us))\n"); - printf(" [-c duration] (reader C.S. duration (in loops))\n"); - printf(" [-e duration] (writer C.S. duration (in loops))\n"); - printf(" [-v] (verbose output)\n"); - printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); - printf("\n"); -} - -int main(int argc, char **argv) -{ - int err; - pthread_t *tid_reader, *tid_writer; - void *tret; - unsigned long long *count_reader; - unsigned long long tot_reads = 0, tot_writes = 0; - int i, a; - - if (argc < 4) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[1], "%u", &nr_readers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[2], "%u", &nr_writers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[3], "%lu", &duration); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - for (i = 4; i < argc; i++) { - if (argv[i][0] != '-') - continue; - switch (argv[i][1]) { -#ifdef DEBUG_YIELD - case 'r': - rcu_yield_active |= RCU_YIELD_READ; - break; - case 'w': - rcu_yield_active |= RCU_YIELD_WRITE; - break; -#endif - case 'a': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - a = atoi(argv[++i]); - cpu_affinities[next_aff++] = a; - use_affinity = 1; - printf_verbose("Adding CPU %d affinity\n", a); - break; - case 'b': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - reclaim_batch = atol(argv[++i]); - break; - case 'c': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - rduration = atol(argv[++i]); - break; - case 'd': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wdelay = atol(argv[++i]); - break; - case 'e': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wduration = atol(argv[++i]); - break; - case 'v': - verbose_mode = 1; - break; - } - } - - printf_verbose("running test for %lu seconds, %u readers, %u writers.\n", - duration, nr_readers, nr_writers); - printf_verbose("Writer delay : %lu loops.\n", wdelay); - printf_verbose("Reader duration : %lu loops.\n", rduration); - printf_verbose("thread %-6s, tid %lu\n", - "main", urcu_get_thread_id()); - - tid_reader = calloc(nr_readers, sizeof(*tid_reader)); - tid_writer = calloc(nr_writers, sizeof(*tid_writer)); - count_reader = calloc(nr_readers, sizeof(*count_reader)); - tot_nr_writes = calloc(nr_writers, sizeof(*tot_nr_writes)); - pending_reclaims = calloc(nr_writers, sizeof(*pending_reclaims)); - if (reclaim_batch * sizeof(*pending_reclaims[i].queue) - < CAA_CACHE_LINE_SIZE) - for (i = 0; i < nr_writers; i++) - pending_reclaims[i].queue = calloc(1, CAA_CACHE_LINE_SIZE); - else - for (i = 0; i < nr_writers; i++) - pending_reclaims[i].queue = calloc(reclaim_batch, - sizeof(*pending_reclaims[i].queue)); - for (i = 0; i < nr_writers; i++) - pending_reclaims[i].head = pending_reclaims[i].queue; - - next_aff = 0; - - for (i = 0; i < nr_readers; i++) { - err = pthread_create(&tid_reader[i], NULL, thr_reader, - &count_reader[i]); - if (err != 0) - exit(1); - } - for (i = 0; i < nr_writers; i++) { - err = pthread_create(&tid_writer[i], NULL, thr_writer, - (void *)(long)i); - if (err != 0) - exit(1); - } - - cmm_smp_mb(); - - test_go = 1; - - sleep(duration); - - test_stop = 1; - - for (i = 0; i < nr_readers; i++) { - err = pthread_join(tid_reader[i], &tret); - if (err != 0) - exit(1); - tot_reads += count_reader[i]; - } - for (i = 0; i < nr_writers; i++) { - err = pthread_join(tid_writer[i], &tret); - if (err != 0) - exit(1); - tot_writes += tot_nr_writes[i]; - rcu_gc_clear_queue(i); - } - - printf_verbose("total number of reads : %llu, writes %llu\n", tot_reads, - tot_writes); - printf("SUMMARY %-25s testdur %4lu nr_readers %3u rdur %6lu wdur %6lu " - "nr_writers %3u " - "wdelay %6lu nr_reads %12llu nr_writes %12llu nr_ops %12llu " - "batch %u\n", - argv[0], duration, nr_readers, rduration, wduration, - nr_writers, wdelay, tot_reads, tot_writes, - tot_reads + tot_writes, reclaim_batch); - free(tid_reader); - free(tid_writer); - free(count_reader); - free(tot_nr_writes); - for (i = 0; i < nr_writers; i++) - free(pending_reclaims[i].queue); - free(pending_reclaims); - - return 0; -} diff --git a/tests/test_urcu_qsbr_timing.c b/tests/test_urcu_qsbr_timing.c deleted file mode 100644 index 9b5195d..0000000 --- a/tests/test_urcu_qsbr_timing.c +++ /dev/null @@ -1,225 +0,0 @@ -/* - * test_qsbr_timing.c - * - * Userspace QSBR - test program - * - * Copyright February 2009 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "thread-id.h" - -#define _LGPL_SOURCE -#include - -pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; - -void rcu_copy_mutex_lock(void) -{ - int ret; - ret = pthread_mutex_lock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } -} - -void rcu_copy_mutex_unlock(void) -{ - int ret; - - ret = pthread_mutex_unlock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } -} - -struct test_array { - int a; -}; - -static struct test_array *test_rcu_pointer; - -#define OUTER_READ_LOOP 2000U -#define INNER_READ_LOOP 100000U -#define READ_LOOP ((unsigned long long)OUTER_READ_LOOP * INNER_READ_LOOP) - -#define OUTER_WRITE_LOOP 10U -#define INNER_WRITE_LOOP 200U -#define WRITE_LOOP ((unsigned long long)OUTER_WRITE_LOOP * INNER_WRITE_LOOP) - -static int num_read; -static int num_write; - -#define NR_READ num_read -#define NR_WRITE num_write - -static cycles_t __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *reader_time; -static cycles_t __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *writer_time; - -void *thr_reader(void *arg) -{ - int i, j; - struct test_array *local_ptr; - cycles_t time1, time2; - - printf("thread_begin %s, tid %lu\n", - "reader", urcu_get_thread_id()); - sleep(2); - - rcu_register_thread(); - - time1 = caa_get_cycles(); - for (i = 0; i < OUTER_READ_LOOP; i++) { - for (j = 0; j < INNER_READ_LOOP; j++) { - _rcu_read_lock(); - local_ptr = _rcu_dereference(test_rcu_pointer); - if (local_ptr) { - assert(local_ptr->a == 8); - } - _rcu_read_unlock(); - } - _rcu_quiescent_state(); - } - time2 = caa_get_cycles(); - - rcu_unregister_thread(); - - reader_time[(unsigned long)arg] = time2 - time1; - - sleep(2); - printf("thread_end %s, tid %lu\n", - "reader", urcu_get_thread_id()); - return ((void*)1); - -} - -void *thr_writer(void *arg) -{ - int i, j; - struct test_array *new, *old; - cycles_t time1, time2; - - printf("thread_begin %s, tid %lu\n", - "writer", urcu_get_thread_id()); - sleep(2); - - for (i = 0; i < OUTER_WRITE_LOOP; i++) { - for (j = 0; j < INNER_WRITE_LOOP; j++) { - time1 = caa_get_cycles(); - new = malloc(sizeof(struct test_array)); - rcu_copy_mutex_lock(); - old = test_rcu_pointer; - if (old) { - assert(old->a == 8); - } - new->a = 8; - old = rcu_xchg_pointer(&test_rcu_pointer, new); - rcu_copy_mutex_unlock(); - synchronize_rcu(); - /* can be done after unlock */ - if (old) { - old->a = 0; - } - free(old); - time2 = caa_get_cycles(); - writer_time[(unsigned long)arg] += time2 - time1; - usleep(1); - } - } - - printf("thread_end %s, tid %lu\n", - "writer", urcu_get_thread_id()); - return ((void*)2); -} - -int main(int argc, char **argv) -{ - int err; - pthread_t *tid_reader, *tid_writer; - void *tret; - int i; - cycles_t tot_rtime = 0; - cycles_t tot_wtime = 0; - - if (argc < 2) { - printf("Usage : %s nr_readers nr_writers\n", argv[0]); - exit(-1); - } - num_read = atoi(argv[1]); - num_write = atoi(argv[2]); - - reader_time = calloc(num_read, sizeof(*reader_time)); - writer_time = calloc(num_write, sizeof(*writer_time)); - tid_reader = calloc(num_read, sizeof(*tid_reader)); - tid_writer = calloc(num_write, sizeof(*tid_writer)); - - printf("thread %-6s, tid %lu\n", - "main", urcu_get_thread_id()); - - for (i = 0; i < NR_READ; i++) { - err = pthread_create(&tid_reader[i], NULL, thr_reader, - (void *)(long)i); - if (err != 0) - exit(1); - } - for (i = 0; i < NR_WRITE; i++) { - err = pthread_create(&tid_writer[i], NULL, thr_writer, - (void *)(long)i); - if (err != 0) - exit(1); - } - - sleep(10); - - for (i = 0; i < NR_READ; i++) { - err = pthread_join(tid_reader[i], &tret); - if (err != 0) - exit(1); - tot_rtime += reader_time[i]; - } - for (i = 0; i < NR_WRITE; i++) { - err = pthread_join(tid_writer[i], &tret); - if (err != 0) - exit(1); - tot_wtime += writer_time[i]; - } - free(test_rcu_pointer); - printf("Time per read : %g cycles\n", - (double)tot_rtime / ((double)NR_READ * (double)READ_LOOP)); - printf("Time per write : %g cycles\n", - (double)tot_wtime / ((double)NR_WRITE * (double)WRITE_LOOP)); - - free(reader_time); - free(writer_time); - free(tid_reader); - free(tid_writer); - - return 0; -} diff --git a/tests/test_urcu_timing.c b/tests/test_urcu_timing.c deleted file mode 100644 index e31b676..0000000 --- a/tests/test_urcu_timing.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - * test_urcu.c - * - * Userspace RCU library - test program - * - * Copyright February 2009 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "thread-id.h" - -#define _LGPL_SOURCE -#include - -pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER; - -void rcu_copy_mutex_lock(void) -{ - int ret; - ret = pthread_mutex_lock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } -} - -void rcu_copy_mutex_unlock(void) -{ - int ret; - - ret = pthread_mutex_unlock(&rcu_copy_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } -} - -struct test_array { - int a; -}; - -static struct test_array *test_rcu_pointer; - -#define OUTER_READ_LOOP 2000U -#define INNER_READ_LOOP 100000U -#define READ_LOOP ((unsigned long long)OUTER_READ_LOOP * INNER_READ_LOOP) - -#define OUTER_WRITE_LOOP 10U -#define INNER_WRITE_LOOP 200U -#define WRITE_LOOP ((unsigned long long)OUTER_WRITE_LOOP * INNER_WRITE_LOOP) - -static int num_read; -static int num_write; - -#define NR_READ num_read -#define NR_WRITE num_write - -static cycles_t __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *reader_time; -static cycles_t __attribute__((aligned(CAA_CACHE_LINE_SIZE))) *writer_time; - -void *thr_reader(void *arg) -{ - int i, j; - struct test_array *local_ptr; - cycles_t time1, time2; - - printf("thread_begin %s, tid %lu\n", - "reader", urcu_get_thread_id()); - sleep(2); - - rcu_register_thread(); - - time1 = caa_get_cycles(); - for (i = 0; i < OUTER_READ_LOOP; i++) { - for (j = 0; j < INNER_READ_LOOP; j++) { - rcu_read_lock(); - local_ptr = rcu_dereference(test_rcu_pointer); - if (local_ptr) { - assert(local_ptr->a == 8); - } - rcu_read_unlock(); - } - } - time2 = caa_get_cycles(); - - rcu_unregister_thread(); - - reader_time[(unsigned long)arg] = time2 - time1; - - sleep(2); - printf("thread_end %s, tid %lu\n", - "reader", urcu_get_thread_id()); - return ((void*)1); - -} - -void *thr_writer(void *arg) -{ - int i, j; - struct test_array *new, *old; - cycles_t time1, time2; - - printf("thread_begin %s, tid %lu\n", - "writer", urcu_get_thread_id()); - sleep(2); - - for (i = 0; i < OUTER_WRITE_LOOP; i++) { - for (j = 0; j < INNER_WRITE_LOOP; j++) { - time1 = caa_get_cycles(); - new = malloc(sizeof(struct test_array)); - rcu_copy_mutex_lock(); - old = test_rcu_pointer; - if (old) { - assert(old->a == 8); - } - new->a = 8; - old = rcu_xchg_pointer(&test_rcu_pointer, new); - rcu_copy_mutex_unlock(); - synchronize_rcu(); - /* can be done after unlock */ - if (old) { - old->a = 0; - } - free(old); - time2 = caa_get_cycles(); - writer_time[(unsigned long)arg] += time2 - time1; - usleep(1); - } - } - - printf("thread_end %s, tid %lu\n", - "writer", urcu_get_thread_id()); - return ((void*)2); -} - -int main(int argc, char **argv) -{ - int err; - pthread_t *tid_reader, *tid_writer; - void *tret; - int i; - cycles_t tot_rtime = 0; - cycles_t tot_wtime = 0; - - if (argc < 2) { - printf("Usage : %s nr_readers nr_writers\n", argv[0]); - exit(-1); - } - num_read = atoi(argv[1]); - num_write = atoi(argv[2]); - - reader_time = calloc(num_read, sizeof(*reader_time)); - writer_time = calloc(num_write, sizeof(*writer_time)); - tid_reader = calloc(num_read, sizeof(*tid_reader)); - tid_writer = calloc(num_write, sizeof(*tid_writer)); - - printf("thread %-6s, tid %lu\n", - "main", urcu_get_thread_id()); - - for (i = 0; i < NR_READ; i++) { - err = pthread_create(&tid_reader[i], NULL, thr_reader, - (void *)(long)i); - if (err != 0) - exit(1); - } - for (i = 0; i < NR_WRITE; i++) { - err = pthread_create(&tid_writer[i], NULL, thr_writer, - (void *)(long)i); - if (err != 0) - exit(1); - } - - sleep(10); - - for (i = 0; i < NR_READ; i++) { - err = pthread_join(tid_reader[i], &tret); - if (err != 0) - exit(1); - tot_rtime += reader_time[i]; - } - for (i = 0; i < NR_WRITE; i++) { - err = pthread_join(tid_writer[i], &tret); - if (err != 0) - exit(1); - tot_wtime += writer_time[i]; - } - free(test_rcu_pointer); - printf("Time per read : %g cycles\n", - (double)tot_rtime / ((double)NR_READ * (double)READ_LOOP)); - printf("Time per write : %g cycles\n", - (double)tot_wtime / ((double)NR_WRITE * (double)WRITE_LOOP)); - - free(reader_time); - free(writer_time); - free(tid_reader); - free(tid_writer); - - return 0; -} diff --git a/tests/test_urcu_wfcq.c b/tests/test_urcu_wfcq.c deleted file mode 100644 index f04c9bd..0000000 --- a/tests/test_urcu_wfcq.c +++ /dev/null @@ -1,581 +0,0 @@ -/* - * test_urcu_wfcq.c - * - * Userspace RCU library - example RCU-based lock-free concurrent queue - * - * Copyright February 2010 - Mathieu Desnoyers - * Copyright February 2010 - Paolo Bonzini - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include "../config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include "cpuset.h" -#include "thread-id.h" - -/* hardcoded number of CPUs */ -#define NR_CPUS 16384 - -#ifndef DYNAMIC_LINK_TEST -#define _LGPL_SOURCE -#endif -#include - -enum test_sync { - TEST_SYNC_NONE = 0, - TEST_SYNC_MUTEX, -}; - -static enum test_sync test_sync; - -static int test_force_sync; - -static volatile int test_go, test_stop_enqueue, test_stop_dequeue; - -static unsigned long rduration; - -static unsigned long duration; - -/* read-side C.S. duration, in loops */ -static unsigned long wdelay; - -static inline void loop_sleep(unsigned long loops) -{ - while (loops-- != 0) - caa_cpu_relax(); -} - -static int verbose_mode; - -static int test_dequeue, test_splice, test_wait_empty; -static int test_enqueue_stopped; - -#define printf_verbose(fmt, args...) \ - do { \ - if (verbose_mode) \ - printf(fmt, ## args); \ - } while (0) - -static unsigned int cpu_affinities[NR_CPUS]; -static unsigned int next_aff = 0; -static int use_affinity = 0; - -pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; - -static void set_affinity(void) -{ -#if HAVE_SCHED_SETAFFINITY - cpu_set_t mask; - int cpu, ret; -#endif /* HAVE_SCHED_SETAFFINITY */ - - if (!use_affinity) - return; - -#if HAVE_SCHED_SETAFFINITY - ret = pthread_mutex_lock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } - cpu = cpu_affinities[next_aff++]; - ret = pthread_mutex_unlock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } - - CPU_ZERO(&mask); - CPU_SET(cpu, &mask); -#if SCHED_SETAFFINITY_ARGS == 2 - sched_setaffinity(0, &mask); -#else - sched_setaffinity(0, sizeof(mask), &mask); -#endif -#endif /* HAVE_SCHED_SETAFFINITY */ -} - -/* - * returns 0 if test should end. - */ -static int test_duration_dequeue(void) -{ - return !test_stop_dequeue; -} - -static int test_duration_enqueue(void) -{ - return !test_stop_enqueue; -} - -static DEFINE_URCU_TLS(unsigned long long, nr_dequeues); -static DEFINE_URCU_TLS(unsigned long long, nr_enqueues); - -static DEFINE_URCU_TLS(unsigned long long, nr_successful_dequeues); -static DEFINE_URCU_TLS(unsigned long long, nr_successful_enqueues); -static DEFINE_URCU_TLS(unsigned long long, nr_empty_dest_enqueues); -static DEFINE_URCU_TLS(unsigned long long, nr_splice); -static DEFINE_URCU_TLS(unsigned long long, nr_dequeue_last); - -static unsigned int nr_enqueuers; -static unsigned int nr_dequeuers; - -static struct cds_wfcq_head __attribute__((aligned(CAA_CACHE_LINE_SIZE))) head; -static struct cds_wfcq_tail __attribute__((aligned(CAA_CACHE_LINE_SIZE))) tail; - -static void *thr_enqueuer(void *_count) -{ - unsigned long long *count = _count; - bool was_nonempty; - - printf_verbose("thread_begin %s, tid %lu\n", - "enqueuer", urcu_get_thread_id()); - - set_affinity(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - struct cds_wfcq_node *node = malloc(sizeof(*node)); - if (!node) - goto fail; - cds_wfcq_node_init(node); - was_nonempty = cds_wfcq_enqueue(&head, &tail, node); - URCU_TLS(nr_successful_enqueues)++; - if (!was_nonempty) - URCU_TLS(nr_empty_dest_enqueues)++; - - if (caa_unlikely(wdelay)) - loop_sleep(wdelay); -fail: - URCU_TLS(nr_enqueues)++; - if (caa_unlikely(!test_duration_enqueue())) - break; - } - - uatomic_inc(&test_enqueue_stopped); - count[0] = URCU_TLS(nr_enqueues); - count[1] = URCU_TLS(nr_successful_enqueues); - count[2] = URCU_TLS(nr_empty_dest_enqueues); - printf_verbose("enqueuer thread_end, tid %lu, " - "enqueues %llu successful_enqueues %llu, " - "empty_dest_enqueues %llu\n", - urcu_get_thread_id(), - URCU_TLS(nr_enqueues), - URCU_TLS(nr_successful_enqueues), - URCU_TLS(nr_empty_dest_enqueues)); - return ((void*)1); - -} - -static void do_test_dequeue(enum test_sync sync) -{ - struct cds_wfcq_node *node; - int state; - - if (sync == TEST_SYNC_MUTEX) - node = cds_wfcq_dequeue_with_state_blocking(&head, &tail, - &state); - else - node = __cds_wfcq_dequeue_with_state_blocking(&head, &tail, - &state); - - if (state & CDS_WFCQ_STATE_LAST) - URCU_TLS(nr_dequeue_last)++; - - if (node) { - free(node); - URCU_TLS(nr_successful_dequeues)++; - } - URCU_TLS(nr_dequeues)++; -} - -static void do_test_splice(enum test_sync sync) -{ - struct cds_wfcq_head tmp_head; - struct cds_wfcq_tail tmp_tail; - struct cds_wfcq_node *node, *n; - enum cds_wfcq_ret ret; - - cds_wfcq_init(&tmp_head, &tmp_tail); - - if (sync == TEST_SYNC_MUTEX) - ret = cds_wfcq_splice_blocking(&tmp_head, &tmp_tail, - &head, &tail); - else - ret = __cds_wfcq_splice_blocking(&tmp_head, &tmp_tail, - &head, &tail); - - switch (ret) { - case CDS_WFCQ_RET_WOULDBLOCK: - assert(0); /* blocking call */ - break; - case CDS_WFCQ_RET_DEST_EMPTY: - URCU_TLS(nr_splice)++; - URCU_TLS(nr_dequeue_last)++; - /* ok */ - break; - case CDS_WFCQ_RET_DEST_NON_EMPTY: - assert(0); /* entirely unexpected */ - break; - case CDS_WFCQ_RET_SRC_EMPTY: - /* ok, we could even skip iteration on dest if we wanted */ - break; - } - - __cds_wfcq_for_each_blocking_safe(&tmp_head, &tmp_tail, node, n) { - free(node); - URCU_TLS(nr_successful_dequeues)++; - URCU_TLS(nr_dequeues)++; - } -} - -static void *thr_dequeuer(void *_count) -{ - unsigned long long *count = _count; - unsigned int counter = 0; - - printf_verbose("thread_begin %s, tid %lu\n", - "dequeuer", urcu_get_thread_id()); - - set_affinity(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - if (test_dequeue && test_splice) { - if (counter & 1) - do_test_dequeue(test_sync); - else - do_test_splice(test_sync); - counter++; - } else { - if (test_dequeue) - do_test_dequeue(test_sync); - else - do_test_splice(test_sync); - } - if (caa_unlikely(!test_duration_dequeue())) - break; - if (caa_unlikely(rduration)) - loop_sleep(rduration); - } - - printf_verbose("dequeuer thread_end, tid %lu, " - "dequeues %llu, successful_dequeues %llu, " - "nr_splice %llu\n", - urcu_get_thread_id(), - URCU_TLS(nr_dequeues), URCU_TLS(nr_successful_dequeues), - URCU_TLS(nr_splice)); - count[0] = URCU_TLS(nr_dequeues); - count[1] = URCU_TLS(nr_successful_dequeues); - count[2] = URCU_TLS(nr_splice); - count[3] = URCU_TLS(nr_dequeue_last); - return ((void*)2); -} - -static void test_end(unsigned long long *nr_dequeues, - unsigned long long *nr_dequeue_last) -{ - struct cds_wfcq_node *node; - int state; - - do { - node = cds_wfcq_dequeue_with_state_blocking(&head, &tail, - &state); - if (node) { - if (state & CDS_WFCQ_STATE_LAST) - (*nr_dequeue_last)++; - free(node); - (*nr_dequeues)++; - } - } while (node); -} - -static void show_usage(int argc, char **argv) -{ - printf("Usage : %s nr_dequeuers nr_enqueuers duration (s) \n", - argv[0]); - printf("OPTIONS:\n"); - printf(" [-d delay] (enqueuer period (in loops))\n"); - printf(" [-c duration] (dequeuer period (in loops))\n"); - printf(" [-v] (verbose output)\n"); - printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); - printf(" [-q] (test dequeue)\n"); - printf(" [-s] (test splice, enabled by default)\n"); - printf(" [-M] (use mutex external synchronization)\n"); - printf(" Note: default: no external synchronization used.\n"); - printf(" [-f] (force user-provided synchronization)\n"); - printf(" [-w] Wait for dequeuer to empty queue\n"); - printf("\n"); -} - -int main(int argc, char **argv) -{ - int err; - pthread_t *tid_enqueuer, *tid_dequeuer; - void *tret; - unsigned long long *count_enqueuer, *count_dequeuer; - unsigned long long tot_enqueues = 0, tot_dequeues = 0; - unsigned long long tot_successful_enqueues = 0, - tot_successful_dequeues = 0, - tot_empty_dest_enqueues = 0, - tot_splice = 0, tot_dequeue_last = 0; - unsigned long long end_dequeues = 0; - int i, a, retval = 0; - - if (argc < 4) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[1], "%u", &nr_dequeuers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[2], "%u", &nr_enqueuers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[3], "%lu", &duration); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - for (i = 4; i < argc; i++) { - if (argv[i][0] != '-') - continue; - switch (argv[i][1]) { - case 'a': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - a = atoi(argv[++i]); - cpu_affinities[next_aff++] = a; - use_affinity = 1; - printf_verbose("Adding CPU %d affinity\n", a); - break; - case 'c': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - rduration = atol(argv[++i]); - break; - case 'd': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wdelay = atol(argv[++i]); - break; - case 'v': - verbose_mode = 1; - break; - case 'q': - test_dequeue = 1; - break; - case 's': - test_splice = 1; - break; - case 'M': - test_sync = TEST_SYNC_MUTEX; - break; - case 'w': - test_wait_empty = 1; - break; - case 'f': - test_force_sync = 1; - break; - } - } - - /* activate splice test by default */ - if (!test_dequeue && !test_splice) - test_splice = 1; - - if (test_sync == TEST_SYNC_NONE && nr_dequeuers > 1 && test_dequeue) { - if (test_force_sync) { - fprintf(stderr, "[WARNING] Using dequeue concurrently " - "with other dequeue or splice without external " - "synchronization. Expect run-time failure.\n"); - } else { - printf("Enforcing mutex synchronization\n"); - test_sync = TEST_SYNC_MUTEX; - } - } - - printf_verbose("running test for %lu seconds, %u enqueuers, " - "%u dequeuers.\n", - duration, nr_enqueuers, nr_dequeuers); - if (test_dequeue) - printf_verbose("dequeue test activated.\n"); - else - printf_verbose("splice test activated.\n"); - if (test_sync == TEST_SYNC_MUTEX) - printf_verbose("External sync: mutex.\n"); - else - printf_verbose("External sync: none.\n"); - if (test_wait_empty) - printf_verbose("Wait for dequeuers to empty queue.\n"); - printf_verbose("Writer delay : %lu loops.\n", rduration); - printf_verbose("Reader duration : %lu loops.\n", wdelay); - printf_verbose("thread %-6s, tid %lu\n", - "main", urcu_get_thread_id()); - - tid_enqueuer = calloc(nr_enqueuers, sizeof(*tid_enqueuer)); - tid_dequeuer = calloc(nr_dequeuers, sizeof(*tid_dequeuer)); - count_enqueuer = calloc(nr_enqueuers, 3 * sizeof(*count_enqueuer)); - count_dequeuer = calloc(nr_dequeuers, 4 * sizeof(*count_dequeuer)); - cds_wfcq_init(&head, &tail); - - next_aff = 0; - - for (i = 0; i < nr_enqueuers; i++) { - err = pthread_create(&tid_enqueuer[i], NULL, thr_enqueuer, - &count_enqueuer[3 * i]); - if (err != 0) - exit(1); - } - for (i = 0; i < nr_dequeuers; i++) { - err = pthread_create(&tid_dequeuer[i], NULL, thr_dequeuer, - &count_dequeuer[4 * i]); - if (err != 0) - exit(1); - } - - cmm_smp_mb(); - - test_go = 1; - - for (i = 0; i < duration; i++) { - sleep(1); - if (verbose_mode) - write (1, ".", 1); - } - - test_stop_enqueue = 1; - - if (test_wait_empty) { - while (nr_enqueuers != uatomic_read(&test_enqueue_stopped)) { - sleep(1); - } - while (!cds_wfcq_empty(&head, &tail)) { - sleep(1); - } - } - - test_stop_dequeue = 1; - - for (i = 0; i < nr_enqueuers; i++) { - err = pthread_join(tid_enqueuer[i], &tret); - if (err != 0) - exit(1); - tot_enqueues += count_enqueuer[3 * i]; - tot_successful_enqueues += count_enqueuer[3 * i + 1]; - tot_empty_dest_enqueues += count_enqueuer[3 * i + 2]; - } - for (i = 0; i < nr_dequeuers; i++) { - err = pthread_join(tid_dequeuer[i], &tret); - if (err != 0) - exit(1); - tot_dequeues += count_dequeuer[4 * i]; - tot_successful_dequeues += count_dequeuer[4 * i + 1]; - tot_splice += count_dequeuer[4 * i + 2]; - tot_dequeue_last += count_dequeuer[4 * i + 3]; - } - - test_end(&end_dequeues, &tot_dequeue_last); - - printf_verbose("total number of enqueues : %llu, dequeues %llu\n", - tot_enqueues, tot_dequeues); - printf_verbose("total number of successful enqueues : %llu, " - "enqueues to empty dest : %llu, " - "successful dequeues %llu, " - "splice : %llu, dequeue_last : %llu\n", - tot_successful_enqueues, - tot_empty_dest_enqueues, - tot_successful_dequeues, - tot_splice, tot_dequeue_last); - printf("SUMMARY %-25s testdur %4lu nr_enqueuers %3u wdelay %6lu " - "nr_dequeuers %3u " - "rdur %6lu nr_enqueues %12llu nr_dequeues %12llu " - "successful enqueues %12llu enqueues to empty dest %12llu " - "successful dequeues %12llu splice %12llu " - "dequeue_last %llu " - "end_dequeues %llu nr_ops %12llu\n", - argv[0], duration, nr_enqueuers, wdelay, - nr_dequeuers, rduration, tot_enqueues, tot_dequeues, - tot_successful_enqueues, - tot_empty_dest_enqueues, - tot_successful_dequeues, tot_splice, tot_dequeue_last, - end_dequeues, - tot_enqueues + tot_dequeues); - - if (tot_successful_enqueues != tot_successful_dequeues + end_dequeues) { - printf("WARNING! Discrepancy between nr succ. enqueues %llu vs " - "succ. dequeues + end dequeues %llu.\n", - tot_successful_enqueues, - tot_successful_dequeues + end_dequeues); - retval = 1; - } - - /* - * If only using splice to dequeue, the enqueuer should see - * exactly as many empty queues than the number of non-empty - * src splice. - */ - if (tot_empty_dest_enqueues != tot_dequeue_last) { - printf("WARNING! Discrepancy between empty enqueue (%llu) and " - "number of dequeue of last element (%llu)\n", - tot_empty_dest_enqueues, - tot_dequeue_last); - retval = 1; - } - free(count_enqueuer); - free(count_dequeuer); - free(tid_enqueuer); - free(tid_dequeuer); - return retval; -} diff --git a/tests/test_urcu_wfq.c b/tests/test_urcu_wfq.c deleted file mode 100644 index 382a1f6..0000000 --- a/tests/test_urcu_wfq.c +++ /dev/null @@ -1,401 +0,0 @@ -/* - * test_urcu_wfq.c - * - * Userspace RCU library - example RCU-based lock-free queue - * - * Copyright February 2010 - Mathieu Desnoyers - * Copyright February 2010 - Paolo Bonzini - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include "../config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include "cpuset.h" -#include "thread-id.h" - -/* hardcoded number of CPUs */ -#define NR_CPUS 16384 - -#ifndef DYNAMIC_LINK_TEST -#define _LGPL_SOURCE -#endif - -/* Remove deprecation warnings from test build. */ -#define CDS_WFQ_DEPRECATED - -#include -#include - -static volatile int test_go, test_stop; - -static unsigned long rduration; - -static unsigned long duration; - -/* read-side C.S. duration, in loops */ -static unsigned long wdelay; - -static inline void loop_sleep(unsigned long loops) -{ - while (loops-- != 0) - caa_cpu_relax(); -} - -static int verbose_mode; - -#define printf_verbose(fmt, args...) \ - do { \ - if (verbose_mode) \ - printf(fmt, args); \ - } while (0) - -static unsigned int cpu_affinities[NR_CPUS]; -static unsigned int next_aff = 0; -static int use_affinity = 0; - -pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; - -static void set_affinity(void) -{ -#if HAVE_SCHED_SETAFFINITY - cpu_set_t mask; - int cpu, ret; -#endif /* HAVE_SCHED_SETAFFINITY */ - - if (!use_affinity) - return; - -#if HAVE_SCHED_SETAFFINITY - ret = pthread_mutex_lock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } - cpu = cpu_affinities[next_aff++]; - ret = pthread_mutex_unlock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } - - CPU_ZERO(&mask); - CPU_SET(cpu, &mask); -#if SCHED_SETAFFINITY_ARGS == 2 - sched_setaffinity(0, &mask); -#else - sched_setaffinity(0, sizeof(mask), &mask); -#endif -#endif /* HAVE_SCHED_SETAFFINITY */ -} - -/* - * returns 0 if test should end. - */ -static int test_duration_dequeue(void) -{ - return !test_stop; -} - -static int test_duration_enqueue(void) -{ - return !test_stop; -} - -static DEFINE_URCU_TLS(unsigned long long, nr_dequeues); -static DEFINE_URCU_TLS(unsigned long long, nr_enqueues); - -static DEFINE_URCU_TLS(unsigned long long, nr_successful_dequeues); -static DEFINE_URCU_TLS(unsigned long long, nr_successful_enqueues); - -static unsigned int nr_enqueuers; -static unsigned int nr_dequeuers; - -static struct cds_wfq_queue q; - -void *thr_enqueuer(void *_count) -{ - unsigned long long *count = _count; - - printf_verbose("thread_begin %s, tid %lu\n", - "enqueuer", urcu_get_thread_id()); - - set_affinity(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - struct cds_wfq_node *node = malloc(sizeof(*node)); - if (!node) - goto fail; - cds_wfq_node_init(node); - cds_wfq_enqueue(&q, node); - URCU_TLS(nr_successful_enqueues)++; - - if (caa_unlikely(wdelay)) - loop_sleep(wdelay); -fail: - URCU_TLS(nr_enqueues)++; - if (caa_unlikely(!test_duration_enqueue())) - break; - } - - count[0] = URCU_TLS(nr_enqueues); - count[1] = URCU_TLS(nr_successful_enqueues); - printf_verbose("enqueuer thread_end, tid %lu, " - "enqueues %llu successful_enqueues %llu\n", - urcu_get_thread_id(), - URCU_TLS(nr_enqueues), - URCU_TLS(nr_successful_enqueues)); - return ((void*)1); - -} - -void *thr_dequeuer(void *_count) -{ - unsigned long long *count = _count; - - printf_verbose("thread_begin %s, tid %lu\n", - "dequeuer", urcu_get_thread_id()); - - set_affinity(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - struct cds_wfq_node *node = cds_wfq_dequeue_blocking(&q); - - if (node) { - free(node); - URCU_TLS(nr_successful_dequeues)++; - } - - URCU_TLS(nr_dequeues)++; - if (caa_unlikely(!test_duration_dequeue())) - break; - if (caa_unlikely(rduration)) - loop_sleep(rduration); - } - - printf_verbose("dequeuer thread_end, tid %lu, " - "dequeues %llu, successful_dequeues %llu\n", - urcu_get_thread_id(), - URCU_TLS(nr_dequeues), - URCU_TLS(nr_successful_dequeues)); - count[0] = URCU_TLS(nr_dequeues); - count[1] = URCU_TLS(nr_successful_dequeues); - return ((void*)2); -} - -void test_end(struct cds_wfq_queue *q, unsigned long long *nr_dequeues) -{ - struct cds_wfq_node *node; - - do { - node = cds_wfq_dequeue_blocking(q); - if (node) { - free(node); - (*nr_dequeues)++; - } - } while (node); -} - -void show_usage(int argc, char **argv) -{ - printf("Usage : %s nr_dequeuers nr_enqueuers duration (s) \n", - argv[0]); - printf("OPTIONS:\n"); - printf(" [-d delay] (enqueuer period (in loops))\n"); - printf(" [-c duration] (dequeuer period (in loops))\n"); - printf(" [-v] (verbose output)\n"); - printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); - printf("\n"); -} - -int main(int argc, char **argv) -{ - int err; - pthread_t *tid_enqueuer, *tid_dequeuer; - void *tret; - unsigned long long *count_enqueuer, *count_dequeuer; - unsigned long long tot_enqueues = 0, tot_dequeues = 0; - unsigned long long tot_successful_enqueues = 0, - tot_successful_dequeues = 0; - unsigned long long end_dequeues = 0; - int i, a; - - if (argc < 4) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[1], "%u", &nr_dequeuers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[2], "%u", &nr_enqueuers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[3], "%lu", &duration); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - for (i = 4; i < argc; i++) { - if (argv[i][0] != '-') - continue; - switch (argv[i][1]) { - case 'a': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - a = atoi(argv[++i]); - cpu_affinities[next_aff++] = a; - use_affinity = 1; - printf_verbose("Adding CPU %d affinity\n", a); - break; - case 'c': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - rduration = atol(argv[++i]); - break; - case 'd': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wdelay = atol(argv[++i]); - break; - case 'v': - verbose_mode = 1; - break; - } - } - - printf_verbose("running test for %lu seconds, %u enqueuers, " - "%u dequeuers.\n", - duration, nr_enqueuers, nr_dequeuers); - printf_verbose("Writer delay : %lu loops.\n", rduration); - printf_verbose("Reader duration : %lu loops.\n", wdelay); - printf_verbose("thread %-6s, tid %lu\n", - "main", urcu_get_thread_id()); - - tid_enqueuer = calloc(nr_enqueuers, sizeof(*tid_enqueuer)); - tid_dequeuer = calloc(nr_dequeuers, sizeof(*tid_dequeuer)); - count_enqueuer = calloc(nr_enqueuers, 2 * sizeof(*count_enqueuer)); - count_dequeuer = calloc(nr_dequeuers, 2 * sizeof(*count_dequeuer)); - cds_wfq_init(&q); - - next_aff = 0; - - for (i = 0; i < nr_enqueuers; i++) { - err = pthread_create(&tid_enqueuer[i], NULL, thr_enqueuer, - &count_enqueuer[2 * i]); - if (err != 0) - exit(1); - } - for (i = 0; i < nr_dequeuers; i++) { - err = pthread_create(&tid_dequeuer[i], NULL, thr_dequeuer, - &count_dequeuer[2 * i]); - if (err != 0) - exit(1); - } - - cmm_smp_mb(); - - test_go = 1; - - for (i = 0; i < duration; i++) { - sleep(1); - if (verbose_mode) - write (1, ".", 1); - } - - test_stop = 1; - - for (i = 0; i < nr_enqueuers; i++) { - err = pthread_join(tid_enqueuer[i], &tret); - if (err != 0) - exit(1); - tot_enqueues += count_enqueuer[2 * i]; - tot_successful_enqueues += count_enqueuer[2 * i + 1]; - } - for (i = 0; i < nr_dequeuers; i++) { - err = pthread_join(tid_dequeuer[i], &tret); - if (err != 0) - exit(1); - tot_dequeues += count_dequeuer[2 * i]; - tot_successful_dequeues += count_dequeuer[2 * i + 1]; - } - - test_end(&q, &end_dequeues); - - printf_verbose("total number of enqueues : %llu, dequeues %llu\n", - tot_enqueues, tot_dequeues); - printf_verbose("total number of successful enqueues : %llu, " - "successful dequeues %llu\n", - tot_successful_enqueues, tot_successful_dequeues); - printf("SUMMARY %-25s testdur %4lu nr_enqueuers %3u wdelay %6lu " - "nr_dequeuers %3u " - "rdur %6lu nr_enqueues %12llu nr_dequeues %12llu " - "successful enqueues %12llu successful dequeues %12llu " - "end_dequeues %llu nr_ops %12llu\n", - argv[0], duration, nr_enqueuers, wdelay, - nr_dequeuers, rduration, tot_enqueues, tot_dequeues, - tot_successful_enqueues, - tot_successful_dequeues, end_dequeues, - tot_enqueues + tot_dequeues); - if (tot_successful_enqueues != tot_successful_dequeues + end_dequeues) - printf("WARNING! Discrepancy between nr succ. enqueues %llu vs " - "succ. dequeues + end dequeues %llu.\n", - tot_successful_enqueues, - tot_successful_dequeues + end_dequeues); - - free(count_enqueuer); - free(count_dequeuer); - free(tid_enqueuer); - free(tid_dequeuer); - return 0; -} diff --git a/tests/test_urcu_wfs.c b/tests/test_urcu_wfs.c deleted file mode 100644 index 0fe7d4c..0000000 --- a/tests/test_urcu_wfs.c +++ /dev/null @@ -1,565 +0,0 @@ -/* - * test_urcu_wfs.c - * - * Userspace RCU library - example RCU-based lock-free stack - * - * Copyright February 2010 - Mathieu Desnoyers - * Copyright February 2010 - Paolo Bonzini - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include "../config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include "cpuset.h" -#include "thread-id.h" - -/* hardcoded number of CPUs */ -#define NR_CPUS 16384 - -#ifndef DYNAMIC_LINK_TEST -#define _LGPL_SOURCE -#endif -#include - -/* - * External synchronization used. - */ -enum test_sync { - TEST_SYNC_NONE = 0, - TEST_SYNC_MUTEX, -}; - -static enum test_sync test_sync; - -static int test_force_sync; - -static volatile int test_go, test_stop_enqueue, test_stop_dequeue; - -static unsigned long rduration; - -static unsigned long duration; - -/* read-side C.S. duration, in loops */ -static unsigned long wdelay; - -static inline void loop_sleep(unsigned long loops) -{ - while (loops-- != 0) - caa_cpu_relax(); -} - -static int verbose_mode; - -static int test_pop, test_pop_all, test_wait_empty; -static int test_enqueue_stopped; - -#define printf_verbose(fmt, args...) \ - do { \ - if (verbose_mode) \ - printf(fmt, ## args); \ - } while (0) - -static unsigned int cpu_affinities[NR_CPUS]; -static unsigned int next_aff = 0; -static int use_affinity = 0; - -pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; - -static void set_affinity(void) -{ -#if HAVE_SCHED_SETAFFINITY - cpu_set_t mask; - int cpu, ret; -#endif /* HAVE_SCHED_SETAFFINITY */ - - if (!use_affinity) - return; - -#if HAVE_SCHED_SETAFFINITY - ret = pthread_mutex_lock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex lock"); - exit(-1); - } - cpu = cpu_affinities[next_aff++]; - ret = pthread_mutex_unlock(&affinity_mutex); - if (ret) { - perror("Error in pthread mutex unlock"); - exit(-1); - } - - CPU_ZERO(&mask); - CPU_SET(cpu, &mask); -#if SCHED_SETAFFINITY_ARGS == 2 - sched_setaffinity(0, &mask); -#else - sched_setaffinity(0, sizeof(mask), &mask); -#endif -#endif /* HAVE_SCHED_SETAFFINITY */ -} - -/* - * returns 0 if test should end. - */ -static int test_duration_dequeue(void) -{ - return !test_stop_dequeue; -} - -static int test_duration_enqueue(void) -{ - return !test_stop_enqueue; -} - -static DEFINE_URCU_TLS(unsigned long long, nr_dequeues); -static DEFINE_URCU_TLS(unsigned long long, nr_enqueues); - -static DEFINE_URCU_TLS(unsigned long long, nr_successful_dequeues); -static DEFINE_URCU_TLS(unsigned long long, nr_successful_enqueues); -static DEFINE_URCU_TLS(unsigned long long, nr_empty_dest_enqueues); -static DEFINE_URCU_TLS(unsigned long long, nr_pop_all); -static DEFINE_URCU_TLS(unsigned long long, nr_pop_last); - -static unsigned int nr_enqueuers; -static unsigned int nr_dequeuers; - -static struct cds_wfs_stack s; - -static void *thr_enqueuer(void *_count) -{ - unsigned long long *count = _count; - bool was_nonempty; - - printf_verbose("thread_begin %s, tid %lu\n", - "enqueuer", urcu_get_thread_id()); - - set_affinity(); - - while (!test_go) - { - } - cmm_smp_mb(); - - for (;;) { - struct cds_wfs_node *node = malloc(sizeof(*node)); - if (!node) - goto fail; - cds_wfs_node_init(node); - was_nonempty = cds_wfs_push(&s, node); - URCU_TLS(nr_successful_enqueues)++; - if (!was_nonempty) - URCU_TLS(nr_empty_dest_enqueues)++; - - if (caa_unlikely(wdelay)) - loop_sleep(wdelay); -fail: - URCU_TLS(nr_enqueues)++; - if (caa_unlikely(!test_duration_enqueue())) - break; - } - - uatomic_inc(&test_enqueue_stopped); - count[0] = URCU_TLS(nr_enqueues); - count[1] = URCU_TLS(nr_successful_enqueues); - count[2] = URCU_TLS(nr_empty_dest_enqueues); - printf_verbose("enqueuer thread_end, tid %lu, " - "enqueues %llu successful_enqueues %llu, " - "empty_dest_enqueues %llu\n", - urcu_get_thread_id(), - URCU_TLS(nr_enqueues), - URCU_TLS(nr_successful_enqueues), - URCU_TLS(nr_empty_dest_enqueues)); - return ((void*)1); - -} - -static void do_test_pop(enum test_sync sync) -{ - struct cds_wfs_node *node; - int state; - - if (sync == TEST_SYNC_MUTEX) - cds_wfs_pop_lock(&s); - node = __cds_wfs_pop_with_state_blocking(&s, &state); - if (sync == TEST_SYNC_MUTEX) - cds_wfs_pop_unlock(&s); - - if (node) { - if (state & CDS_WFS_STATE_LAST) - URCU_TLS(nr_pop_last)++; - free(node); - URCU_TLS(nr_successful_dequeues)++; - } - URCU_TLS(nr_dequeues)++; -} - -static void do_test_pop_all(enum test_sync sync) -{ - struct cds_wfs_head *head; - struct cds_wfs_node *node, *n; - - if (sync == TEST_SYNC_MUTEX) - cds_wfs_pop_lock(&s); - head = __cds_wfs_pop_all(&s); - if (sync == TEST_SYNC_MUTEX) - cds_wfs_pop_unlock(&s); - - /* Check if empty */ - if (cds_wfs_first(head) == NULL) - return; - - URCU_TLS(nr_pop_all)++; - URCU_TLS(nr_pop_last)++; - - cds_wfs_for_each_blocking_safe(head, node, n) { - free(node); - URCU_TLS(nr_successful_dequeues)++; - URCU_TLS(nr_dequeues)++; - } -} - -static void *thr_dequeuer(void *_count) -{ - unsigned long long *count = _count; - unsigned int counter = 0; - - printf_verbose("thread_begin %s, tid %lu\n", - "dequeuer", urcu_get_thread_id()); - - set_affinity(); - - while (!test_go) - { - } - cmm_smp_mb(); - - assert(test_pop || test_pop_all); - - for (;;) { - if (test_pop && test_pop_all) { - if (counter & 1) - do_test_pop(test_sync); - else - do_test_pop_all(test_sync); - counter++; - } else { - if (test_pop) - do_test_pop(test_sync); - else - do_test_pop_all(test_sync); - } - - if (caa_unlikely(!test_duration_dequeue())) - break; - if (caa_unlikely(rduration)) - loop_sleep(rduration); - } - - printf_verbose("dequeuer thread_end, tid %lu, " - "dequeues %llu, successful_dequeues %llu " - "pop_all %llu pop_last %llu\n", - urcu_get_thread_id(), - URCU_TLS(nr_dequeues), URCU_TLS(nr_successful_dequeues), - URCU_TLS(nr_pop_all), - URCU_TLS(nr_pop_last)); - count[0] = URCU_TLS(nr_dequeues); - count[1] = URCU_TLS(nr_successful_dequeues); - count[2] = URCU_TLS(nr_pop_all); - count[3] = URCU_TLS(nr_pop_last); - return ((void*)2); -} - -static void test_end(struct cds_wfs_stack *s, unsigned long long *nr_dequeues, - unsigned long long *nr_pop_last) -{ - struct cds_wfs_node *node; - int state; - - do { - node = cds_wfs_pop_with_state_blocking(s, &state); - if (node) { - if (state & CDS_WFS_STATE_LAST) - (*nr_pop_last)++; - free(node); - (*nr_dequeues)++; - } - } while (node); -} - -static void show_usage(int argc, char **argv) -{ - printf("Usage : %s nr_dequeuers nr_enqueuers duration (s) \n", - argv[0]); - printf("OPTIONS:\n"); - printf(" [-d delay] (enqueuer period (in loops))\n"); - printf(" [-c duration] (dequeuer period (in loops))\n"); - printf(" [-v] (verbose output)\n"); - printf(" [-a cpu#] [-a cpu#]... (affinity)\n"); - printf(" [-p] (test pop)\n"); - printf(" [-P] (test pop_all, enabled by default)\n"); - printf(" [-M] (use mutex external synchronization)\n"); - printf(" Note: default: no external synchronization used.\n"); - printf(" [-f] (force user-provided synchronization)\n"); - printf(" [-w] Wait for dequeuer to empty stack\n"); - printf("\n"); -} - -int main(int argc, char **argv) -{ - int err; - pthread_t *tid_enqueuer, *tid_dequeuer; - void *tret; - unsigned long long *count_enqueuer, *count_dequeuer; - unsigned long long tot_enqueues = 0, tot_dequeues = 0; - unsigned long long tot_successful_enqueues = 0, - tot_successful_dequeues = 0, - tot_empty_dest_enqueues = 0, - tot_pop_all = 0, tot_pop_last = 0; - unsigned long long end_dequeues = 0; - int i, a, retval = 0; - - if (argc < 4) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[1], "%u", &nr_dequeuers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[2], "%u", &nr_enqueuers); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - err = sscanf(argv[3], "%lu", &duration); - if (err != 1) { - show_usage(argc, argv); - return -1; - } - - for (i = 4; i < argc; i++) { - if (argv[i][0] != '-') - continue; - switch (argv[i][1]) { - case 'a': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - a = atoi(argv[++i]); - cpu_affinities[next_aff++] = a; - use_affinity = 1; - printf_verbose("Adding CPU %d affinity\n", a); - break; - case 'c': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - rduration = atol(argv[++i]); - break; - case 'd': - if (argc < i + 2) { - show_usage(argc, argv); - return -1; - } - wdelay = atol(argv[++i]); - break; - case 'v': - verbose_mode = 1; - break; - case 'p': - test_pop = 1; - break; - case 'P': - test_pop_all = 1; - break; - case 'M': - test_sync = TEST_SYNC_MUTEX; - break; - case 'w': - test_wait_empty = 1; - break; - case 'f': - test_force_sync = 1; - break; - } - } - - /* activate pop_all test by default */ - if (!test_pop && !test_pop_all) - test_pop_all = 1; - - if (test_sync == TEST_SYNC_NONE && nr_dequeuers > 1 && test_pop) { - if (test_force_sync) { - fprintf(stderr, "[WARNING] Using pop concurrently " - "with other pop or pop_all without external " - "synchronization. Expect run-time failure.\n"); - } else { - printf("Enforcing mutex synchronization\n"); - test_sync = TEST_SYNC_MUTEX; - } - } - - printf_verbose("running test for %lu seconds, %u enqueuers, " - "%u dequeuers.\n", - duration, nr_enqueuers, nr_dequeuers); - if (test_pop) - printf_verbose("pop test activated.\n"); - if (test_pop_all) - printf_verbose("pop_all test activated.\n"); - if (test_sync == TEST_SYNC_MUTEX) - printf_verbose("External sync: mutex.\n"); - else - printf_verbose("External sync: none.\n"); - if (test_wait_empty) - printf_verbose("Wait for dequeuers to empty stack.\n"); - printf_verbose("Writer delay : %lu loops.\n", rduration); - printf_verbose("Reader duration : %lu loops.\n", wdelay); - printf_verbose("thread %-6s, tid %lu\n", - "main", urcu_get_thread_id()); - - tid_enqueuer = calloc(nr_enqueuers, sizeof(*tid_enqueuer)); - tid_dequeuer = calloc(nr_dequeuers, sizeof(*tid_dequeuer)); - count_enqueuer = calloc(nr_enqueuers, 3 * sizeof(*count_enqueuer)); - count_dequeuer = calloc(nr_dequeuers, 4 * sizeof(*count_dequeuer)); - cds_wfs_init(&s); - - next_aff = 0; - - for (i = 0; i < nr_enqueuers; i++) { - err = pthread_create(&tid_enqueuer[i], NULL, thr_enqueuer, - &count_enqueuer[3 * i]); - if (err != 0) - exit(1); - } - for (i = 0; i < nr_dequeuers; i++) { - err = pthread_create(&tid_dequeuer[i], NULL, thr_dequeuer, - &count_dequeuer[4 * i]); - if (err != 0) - exit(1); - } - - cmm_smp_mb(); - - test_go = 1; - - for (i = 0; i < duration; i++) { - sleep(1); - if (verbose_mode) - write (1, ".", 1); - } - - test_stop_enqueue = 1; - - if (test_wait_empty) { - while (nr_enqueuers != uatomic_read(&test_enqueue_stopped)) { - sleep(1); - } - while (!cds_wfs_empty(&s)) { - sleep(1); - } - } - - test_stop_dequeue = 1; - - for (i = 0; i < nr_enqueuers; i++) { - err = pthread_join(tid_enqueuer[i], &tret); - if (err != 0) - exit(1); - tot_enqueues += count_enqueuer[3 * i]; - tot_successful_enqueues += count_enqueuer[3 * i + 1]; - tot_empty_dest_enqueues += count_enqueuer[3 * i + 2]; - } - for (i = 0; i < nr_dequeuers; i++) { - err = pthread_join(tid_dequeuer[i], &tret); - if (err != 0) - exit(1); - tot_dequeues += count_dequeuer[4 * i]; - tot_successful_dequeues += count_dequeuer[4 * i + 1]; - tot_pop_all += count_dequeuer[4 * i + 2]; - tot_pop_last += count_dequeuer[4 * i + 3]; - } - - test_end(&s, &end_dequeues, &tot_pop_last); - - printf_verbose("total number of enqueues : %llu, dequeues %llu\n", - tot_enqueues, tot_dequeues); - printf_verbose("total number of successful enqueues : %llu, " - "enqueues to empty dest : %llu, " - "successful dequeues %llu, " - "pop_all : %llu, pop_last : %llu\n", - tot_successful_enqueues, - tot_empty_dest_enqueues, - tot_successful_dequeues, - tot_pop_all, tot_pop_last); - printf("SUMMARY %-25s testdur %4lu nr_enqueuers %3u wdelay %6lu " - "nr_dequeuers %3u " - "rdur %6lu nr_enqueues %12llu nr_dequeues %12llu " - "successful enqueues %12llu enqueues to empty dest %12llu " - "successful dequeues %12llu pop_all %12llu " - "pop_last %llu end_dequeues %llu nr_ops %12llu\n", - argv[0], duration, nr_enqueuers, wdelay, - nr_dequeuers, rduration, tot_enqueues, tot_dequeues, - tot_successful_enqueues, - tot_empty_dest_enqueues, - tot_successful_dequeues, tot_pop_all, tot_pop_last, - end_dequeues, - tot_enqueues + tot_dequeues); - if (tot_successful_enqueues != tot_successful_dequeues + end_dequeues) { - printf("WARNING! Discrepancy between nr succ. enqueues %llu vs " - "succ. dequeues + end dequeues %llu.\n", - tot_successful_enqueues, - tot_successful_dequeues + end_dequeues); - retval = 1; - } - /* - * The enqueuer should see exactly as many empty queues than the - * number of non-empty stacks dequeued. - */ - if (tot_empty_dest_enqueues != tot_pop_last) { - printf("WARNING! Discrepancy between empty enqueue (%llu) and " - "number of pop last (%llu)\n", - tot_empty_dest_enqueues, - tot_pop_last); - retval = 1; - } - free(count_enqueuer); - free(count_dequeuer); - free(tid_enqueuer); - free(tid_dequeuer); - return retval; -} diff --git a/tests/thread-id.h b/tests/thread-id.h deleted file mode 100644 index 9378edc..0000000 --- a/tests/thread-id.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef _TEST_THREAD_ID_H -#define _TEST_THREAD_ID_H - -/* - * thread-id.h - * - * Userspace RCU library - thread ID - * - * Copyright 2013 - Mathieu Desnoyers - * - * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED - * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. - * - * Permission is hereby granted to use or copy this program - * for any purpose, provided the above notices are retained on all copies. - * Permission to modify the code and to distribute modified code is granted, - * provided the above notices are retained, and a notice that the code was - * modified is included with the above copyright notice. - */ - -#ifdef __linux__ -# include - -# if defined(_syscall0) -_syscall0(pid_t, gettid) -# elif defined(__NR_gettid) -static inline pid_t gettid(void) -{ - return syscall(__NR_gettid); -} -# endif - -static inline -unsigned long urcu_get_thread_id(void) -{ - return (unsigned long) gettid(); -} -#elif defined(__FreeBSD__) -# include - -static inline -unsigned long urcu_get_thread_id(void) -{ - return (unsigned long) pthread_getthreadid_np(); -} -#else -# warning "use pid as thread ID" -static inline -unsigned long urcu_get_thread_id(void) -{ - return (unsigned long) getpid(); -} -#endif - -#endif /* _TEST_THREAD_ID_H */ diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am new file mode 100644 index 0000000..1fa8b71 --- /dev/null +++ b/tests/unit/Makefile.am @@ -0,0 +1,61 @@ +AM_LDFLAGS=-lpthread +AM_CFLAGS=-I$(top_srcdir) -I$(top_builddir) -I$(top_srcdir)/tests/common -g + +noinst_PROGRAMS = test_uatomic \ + test_urcu_multiflavor \ + test_urcu_multiflavor_dynlink + +noinst_HEADERS = test_urcu_multiflavor.h + +if COMPAT_ARCH +COMPAT=$(top_srcdir)/compat_arch_@ARCHTYPE@.c +else +COMPAT= +endif + +if COMPAT_FUTEX +COMPAT+=$(top_srcdir)/compat_futex.c +endif + +URCU=$(top_srcdir)/urcu.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT) +URCU_QSBR=$(top_srcdir)/urcu-qsbr.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT) +# URCU_MB uses urcu.c but -DRCU_MB must be defined +URCU_MB=$(top_srcdir)/urcu.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT) +# URCU_SIGNAL uses urcu.c but -DRCU_SIGNAL must be defined +URCU_SIGNAL=$(top_srcdir)/urcu.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT) +URCU_BP=$(top_srcdir)/urcu-bp.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT) +URCU_DEFER=$(top_srcdir)/urcu.c $(top_srcdir)/urcu-pointer.c $(top_srcdir)/wfcqueue.c $(COMPAT) + +URCU_COMMON_LIB=$(top_builddir)/liburcu-common.la +URCU_LIB=$(top_builddir)/liburcu.la +URCU_QSBR_LIB=$(top_builddir)/liburcu-qsbr.la +URCU_MB_LIB=$(top_builddir)/liburcu-mb.la +URCU_SIGNAL_LIB=$(top_builddir)/liburcu-signal.la +URCU_BP_LIB=$(top_builddir)/liburcu-bp.la +URCU_CDS_LIB=$(top_builddir)/liburcu-cds.la + +test_uatomic_SOURCES = test_uatomic.c $(COMPAT) + +test_urcu_multiflavor_SOURCES = test_urcu_multiflavor.c \ + test_urcu_multiflavor-memb.c \ + test_urcu_multiflavor-mb.c \ + test_urcu_multiflavor-signal.c \ + test_urcu_multiflavor-qsbr.c \ + test_urcu_multiflavor-bp.c +test_urcu_multiflavor_LDADD = $(URCU_LIB) $(URCU_MB_LIB) \ + $(URCU_SIGNAL_LIB) $(URCU_QSBR_LIB) $(URCU_BP_LIB) + +test_urcu_multiflavor_dynlink_SOURCES = test_urcu_multiflavor.c \ + test_urcu_multiflavor-memb.c \ + test_urcu_multiflavor-mb.c \ + test_urcu_multiflavor-signal.c \ + test_urcu_multiflavor-qsbr.c \ + test_urcu_multiflavor-bp.c +test_urcu_multiflavor_dynlink_CFLAGS = -DDYNAMIC_LINK_TEST $(AM_CFLAGS) +test_urcu_multiflavor_dynlink_LDADD = $(URCU_LIB) $(URCU_MB_LIB) \ + $(URCU_SIGNAL_LIB) $(URCU_QSBR_LIB) $(URCU_BP_LIB) + +check-am: + ./test_uatomic + ./test_urcu_multiflavor + ./test_urcu_multiflavor_dynlink diff --git a/tests/unit/test_uatomic.c b/tests/unit/test_uatomic.c new file mode 100644 index 0000000..804ce7b --- /dev/null +++ b/tests/unit/test_uatomic.c @@ -0,0 +1,91 @@ +/* + * test_uatomic.c + * + * Userspace RCU library - test atomic operations + * + * Copyright February 2009 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include + +struct testvals { +#ifdef UATOMIC_HAS_ATOMIC_BYTE + unsigned char c; +#endif +#ifdef UATOMIC_HAS_ATOMIC_SHORT + unsigned short s; +#endif + unsigned int i; + unsigned long l; +}; + +static struct testvals vals; + +#define do_test(ptr) \ +do { \ + __typeof__(*(ptr)) v; \ + \ + uatomic_add(ptr, 10); \ + assert(uatomic_read(ptr) == 10); \ + uatomic_add(ptr, -11UL); \ + assert(uatomic_read(ptr) == (__typeof__(*(ptr)))-1UL); \ + v = uatomic_cmpxchg(ptr, -1UL, 22); \ + assert(uatomic_read(ptr) == 22); \ + assert(v == (__typeof__(*(ptr)))-1UL); \ + v = uatomic_cmpxchg(ptr, 33, 44); \ + assert(uatomic_read(ptr) == 22); \ + assert(v == 22); \ + v = uatomic_xchg(ptr, 55); \ + assert(uatomic_read(ptr) == 55); \ + assert(v == 22); \ + uatomic_set(ptr, 22); \ + uatomic_inc(ptr); \ + assert(uatomic_read(ptr) == 23); \ + uatomic_dec(ptr); \ + assert(uatomic_read(ptr) == 22); \ + v = uatomic_add_return(ptr, 74); \ + assert(v == 96); \ + assert(uatomic_read(ptr) == 96); \ + uatomic_or(ptr, 58); \ + assert(uatomic_read(ptr) == 122); \ + v = uatomic_sub_return(ptr, 1); \ + assert(v == 121); \ + uatomic_sub(ptr, (unsigned int) 2); \ + assert(uatomic_read(ptr) == 119); \ + uatomic_inc(ptr); \ + uatomic_inc(ptr); \ + assert(uatomic_read(ptr) == 121); \ + uatomic_and(ptr, 129); \ + assert(uatomic_read(ptr) == 1); \ +} while (0) + +int main(int argc, char **argv) +{ +#ifdef UATOMIC_HAS_ATOMIC_BYTE + do_test(&vals.c); +#endif +#ifdef UATOMIC_HAS_ATOMIC_SHORT + do_test(&vals.s); +#endif + do_test(&vals.i); + do_test(&vals.l); + printf("Atomic ops test OK\n"); + + return 0; +} diff --git a/tests/unit/test_urcu_multiflavor-bp.c b/tests/unit/test_urcu_multiflavor-bp.c new file mode 100644 index 0000000..199818b --- /dev/null +++ b/tests/unit/test_urcu_multiflavor-bp.c @@ -0,0 +1,39 @@ +/* + * test_urcu_multiflavor-bp.c + * + * Userspace RCU library - test multiple RCU flavors into one program + * + * Copyright February 2012 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef DYNAMIC_LINK_TEST +#define _LGPL_SOURCE +#endif + +#define RCU_SIGNAL +#include +#include "test_urcu_multiflavor.h" + +int test_mf_bp(void) +{ + rcu_register_thread(); + rcu_read_lock(); + rcu_read_unlock(); + synchronize_rcu(); + rcu_unregister_thread(); + return 0; +} diff --git a/tests/unit/test_urcu_multiflavor-mb.c b/tests/unit/test_urcu_multiflavor-mb.c new file mode 100644 index 0000000..e4ba6a4 --- /dev/null +++ b/tests/unit/test_urcu_multiflavor-mb.c @@ -0,0 +1,39 @@ +/* + * test_urcu_multiflavor-mb.c + * + * Userspace RCU library - test multiple RCU flavors into one program + * + * Copyright February 2012 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef DYNAMIC_LINK_TEST +#define _LGPL_SOURCE +#endif + +#define RCU_MB +#include +#include "test_urcu_multiflavor.h" + +int test_mf_mb(void) +{ + rcu_register_thread(); + rcu_read_lock(); + rcu_read_unlock(); + synchronize_rcu(); + rcu_unregister_thread(); + return 0; +} diff --git a/tests/unit/test_urcu_multiflavor-memb.c b/tests/unit/test_urcu_multiflavor-memb.c new file mode 100644 index 0000000..583f2e1 --- /dev/null +++ b/tests/unit/test_urcu_multiflavor-memb.c @@ -0,0 +1,38 @@ +/* + * test_urcu_multiflavor-memb.c + * + * Userspace RCU library - test multiple RCU flavors into one program + * + * Copyright February 2012 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef DYNAMIC_LINK_TEST +#define _LGPL_SOURCE +#endif + +#include +#include "test_urcu_multiflavor.h" + +int test_mf_memb(void) +{ + rcu_register_thread(); + rcu_read_lock(); + rcu_read_unlock(); + synchronize_rcu(); + rcu_unregister_thread(); + return 0; +} diff --git a/tests/unit/test_urcu_multiflavor-qsbr.c b/tests/unit/test_urcu_multiflavor-qsbr.c new file mode 100644 index 0000000..64f32f4 --- /dev/null +++ b/tests/unit/test_urcu_multiflavor-qsbr.c @@ -0,0 +1,38 @@ +/* + * test_urcu_multiflavor-qsbr.c + * + * Userspace RCU library - test multiple RCU flavors into one program + * + * Copyright February 2012 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef DYNAMIC_LINK_TEST +#define _LGPL_SOURCE +#endif + +#include +#include "test_urcu_multiflavor.h" + +int test_mf_qsbr(void) +{ + rcu_register_thread(); + rcu_read_lock(); + rcu_read_unlock(); + synchronize_rcu(); + rcu_unregister_thread(); + return 0; +} diff --git a/tests/unit/test_urcu_multiflavor-signal.c b/tests/unit/test_urcu_multiflavor-signal.c new file mode 100644 index 0000000..816c615 --- /dev/null +++ b/tests/unit/test_urcu_multiflavor-signal.c @@ -0,0 +1,39 @@ +/* + * test_urcu_multiflavor-signal.c + * + * Userspace RCU library - test multiple RCU flavors into one program + * + * Copyright February 2012 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef DYNAMIC_LINK_TEST +#define _LGPL_SOURCE +#endif + +#define RCU_SIGNAL +#include +#include "test_urcu_multiflavor.h" + +int test_mf_signal(void) +{ + rcu_register_thread(); + rcu_read_lock(); + rcu_read_unlock(); + synchronize_rcu(); + rcu_unregister_thread(); + return 0; +} diff --git a/tests/unit/test_urcu_multiflavor.c b/tests/unit/test_urcu_multiflavor.c new file mode 100644 index 0000000..cd573ca --- /dev/null +++ b/tests/unit/test_urcu_multiflavor.c @@ -0,0 +1,50 @@ +/* + * test_urcu_multiflavor.c + * + * Userspace RCU library - test multiple RCU flavors into one program + * + * Copyright February 2012 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include "test_urcu_multiflavor.h" + +int main(int argc, char **argv) +{ + int ret; + + ret = test_mf_memb(); + if (ret) + goto failure; + ret = test_mf_mb(); + if (ret) + goto failure; + ret = test_mf_signal(); + if (ret) + goto failure; + ret = test_mf_qsbr(); + if (ret) + goto failure; + ret = test_mf_bp(); + if (ret) + goto failure; + + exit(EXIT_SUCCESS); + +failure: + exit(EXIT_FAILURE); +} diff --git a/tests/unit/test_urcu_multiflavor.h b/tests/unit/test_urcu_multiflavor.h new file mode 100644 index 0000000..419ad5d --- /dev/null +++ b/tests/unit/test_urcu_multiflavor.h @@ -0,0 +1,29 @@ +/* + * test_urcu_multiflavor.h + * + * Userspace RCU library - test multiple RCU flavors into one program + * + * Copyright February 2012 - Mathieu Desnoyers + * Copyright February 2012 - Lai Jiangshan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +extern int test_mf_memb(void); +extern int test_mf_mb(void); +extern int test_mf_signal(void); +extern int test_mf_qsbr(void); +extern int test_mf_bp(void); + diff --git a/tests/unit/urcu-asm.c b/tests/unit/urcu-asm.c new file mode 100644 index 0000000..b616d34 --- /dev/null +++ b/tests/unit/urcu-asm.c @@ -0,0 +1,37 @@ +/* + * urcu-asm.c + * + * Userspace RCU library - assembly dump of primitives + * + * Copyright February 2009 - Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + +void show_read_lock(void) +{ + asm volatile ("/* start */"); + rcu_read_lock(); + asm volatile ("/* end */"); +} + +void show_read_unlock(void) +{ + asm volatile ("/* start */"); + rcu_read_unlock(); + asm volatile ("/* end */"); +} diff --git a/tests/urcu-asm.c b/tests/urcu-asm.c deleted file mode 100644 index b616d34..0000000 --- a/tests/urcu-asm.c +++ /dev/null @@ -1,37 +0,0 @@ -/* - * urcu-asm.c - * - * Userspace RCU library - assembly dump of primitives - * - * Copyright February 2009 - Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include - -void show_read_lock(void) -{ - asm volatile ("/* start */"); - rcu_read_lock(); - asm volatile ("/* end */"); -} - -void show_read_unlock(void) -{ - asm volatile ("/* start */"); - rcu_read_unlock(); - asm volatile ("/* end */"); -} diff --git a/tests/urcutorture.c b/tests/urcutorture.c deleted file mode 100644 index 35096a6..0000000 --- a/tests/urcutorture.c +++ /dev/null @@ -1,30 +0,0 @@ -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include "api.h" -#define _LGPL_SOURCE - -#ifdef RCU_MEMBARRIER -#include -#endif -#ifdef RCU_SIGNAL -#include -#endif -#ifdef RCU_MB -#include -#endif -#ifdef RCU_QSBR -#include -#endif -#ifdef RCU_BP -#include -#endif - -#include -#include -#include "rcutorture.h"