#include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef NDEBUG #define VERIFY(e) (e) #else #define VERIFY(e) assert(e) #endif /**************************************************************************/ /* Helper functions */ /**************************************************************************/ static uint64_t now_us() { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec * 1000 * 1000 + ts.tv_nsec / 1000; } static int ctxtsw() { struct rusage ru; getrusage(RUSAGE_SELF, &ru); return ru.ru_nvcsw + ru.ru_nivcsw; } static void* pid_thread_func(void* p) { *(pid_t*)(p) = getpid(); return 0; } static void print_thread_lib_name() { pid_t thread_pid; pthread_t thread; pthread_create(&thread, 0, pid_thread_func, &thread_pid); pthread_join(thread, 0); puts(thread_pid == getpid() ? "NPTL" : "linuxthreads"); } static void run_test(const char* const descr, void (*f)(int), const int iterations) { const int start_ct = ctxtsw(); const uint64_t start_us = now_us(); (*f)(iterations); const uint64_t elapsed_us = now_us() - start_us; const int elapsed_ct = ctxtsw() - start_ct; printf("%s elapsed: %8lld us; per iteration: %4lld ns / %.2g csw\n", descr, elapsed_us, elapsed_us * 1000 / iterations, elapsed_ct * 1.0 / iterations); } /**************************************************************************/ /* Test 1: single-threaded libpthread mutex performance test */ /**************************************************************************/ static void test1(const int iterations) { pthread_mutex_t mutex; pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutex_init(&mutex, &attr); pthread_mutexattr_destroy(&attr); for (unsigned i = iterations; i > 0; i--) { pthread_mutex_lock(&mutex); pthread_mutex_unlock(&mutex); } pthread_mutex_destroy(&mutex); } /**************************************************************************/ /* Test 2: condition variable ping-pong test */ /**************************************************************************/ struct test2args { int iterations; pthread_mutex_t mutex; pthread_cond_t cv; }; static void* test2_thread(struct test2args* p) { pthread_mutex_lock(&p->mutex); pthread_cond_signal(&p->cv); for (unsigned i = p->iterations; i > 0; i--) { pthread_cond_wait(&p->cv, &p->mutex); pthread_cond_signal(&p->cv); } pthread_mutex_unlock(&p->mutex); return 0; } static void test2(const int iterations) { struct test2args args; args.iterations = iterations; pthread_mutex_init(&args.mutex, 0); pthread_cond_init(&args.cv, 0); pthread_t thread; pthread_mutex_lock(&args.mutex); pthread_create(&thread, 0, (void*(*)(void*))test2_thread, &args); for (unsigned i = iterations; i > 0; i--) { pthread_cond_wait(&args.cv, &args.mutex); pthread_cond_signal(&args.cv); } pthread_mutex_unlock(&args.mutex); pthread_join(thread, 0); pthread_cond_destroy(&args.cv); pthread_mutex_destroy(&args.mutex); } /**************************************************************************/ /* Test 3: ping-pong test using plain old Unix signals */ /**************************************************************************/ struct test3args { pthread_t mainthread; int iterations; pthread_mutex_t mutex; }; static void* test3_thread(struct test3args* p) { int signo; sigset_t WaitSet; sigemptyset(&WaitSet); sigaddset(&WaitSet, SIGALRM); pthread_mutex_lock(&p->mutex); pthread_kill(p->mainthread, SIGALRM); for (unsigned i = p->iterations; i > 0; i--) { pthread_mutex_unlock(&p->mutex); VERIFY(sigwait(&WaitSet, &signo) == 0); pthread_mutex_lock(&p->mutex); pthread_kill(p->mainthread, SIGALRM); } pthread_mutex_unlock(&p->mutex); return 0; } static void test3(const int iterations) { int signo; sigset_t WaitSet; sigemptyset(&WaitSet); sigaddset(&WaitSet, SIGALRM); struct test3args args; args.mainthread = pthread_self(); args.iterations = iterations; pthread_mutex_init(&args.mutex, 0); pthread_t thread; pthread_mutex_lock(&args.mutex); pthread_create(&thread, 0, (void*(*)(void*))test3_thread, &args); for (unsigned i = iterations; i > 0; i--) { pthread_mutex_unlock(&args.mutex); VERIFY(sigwait(&WaitSet, &signo) == 0); pthread_mutex_lock(&args.mutex); pthread_kill(thread, SIGALRM); } pthread_mutex_unlock(&args.mutex); pthread_join(thread, 0); pthread_mutex_destroy(&args.mutex); } /**************************************************************************/ /* Test 4: ping-pong test using plain old Unix signals */ /**************************************************************************/ struct test4args { pthread_t mainthread; int iterations; }; static void* test4_thread(struct test4args* p) { int signo; sigset_t WaitSet; sigemptyset(&WaitSet); sigaddset(&WaitSet, SIGALRM); pthread_kill(p->mainthread, SIGALRM); for (unsigned i = p->iterations; i > 0; i--) { VERIFY(sigwait(&WaitSet, &signo) == 0); pthread_kill(p->mainthread, SIGALRM); } return 0; } static void test4(const int iterations) { int signo; sigset_t WaitSet; sigemptyset(&WaitSet); sigaddset(&WaitSet, SIGALRM); struct test4args args; args.mainthread = pthread_self(); args.iterations = iterations; pthread_t thread; pthread_create(&thread, 0, (void*(*)(void*))test4_thread, &args); for (unsigned i = iterations; i > 0; i--) { VERIFY(sigwait(&WaitSet, &signo) == 0); pthread_kill(thread, SIGALRM); } pthread_join(thread, 0); } /**************************************************************************/ /* Main program */ /**************************************************************************/ int main(int argc, char** argv) { const int iterations = argc > 1 ? atoi(argv[1]) : 100000; sigset_t BlockedSet; sigemptyset(&BlockedSet); sigaddset(&BlockedSet, SIGALRM); pthread_sigmask(SIG_BLOCK, &BlockedSet, 0); print_thread_lib_name(); run_test("mutex ", test1, iterations); run_test("c.v. ping-pong test ", test2, iterations); run_test("signal ping-pong test ", test3, iterations); run_test("signal ping-pong no mtx", test4, iterations); return 0; } // Local Variables: // compile-command: "for c in 'gcc' 'gcc -B/home/bart/glibc236' 'gcc -B/home/bart/glibc-cvs'; do p=${c#gcc -B/home/bart/}; p=${p#gcc}; if [ -z $p ]; then o=condvar-perf; else o=${p}-condvar-perf; fi; echo $o; $c -o $o -std=c99 -D_GNU_SOURCE -g -Wall -Werror -W -O3 condvar-perf.c -lpthread -lrt; done" // End: