/* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */ // vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4: #ident "$Id$" /*====== This file is part of PerconaFT. Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved. PerconaFT is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2, as published by the Free Software Foundation. PerconaFT 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 PerconaFT. If not, see . ---------------------------------------- PerconaFT is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License, version 3, as published by the Free Software Foundation. PerconaFT 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with PerconaFT. If not, see . ======= */ #ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved." // Here are some timing numbers: // (Note: The not-quite-working version with cas can be found in r22519 of https://svn.tokutek.com/tokudb/toku/tokudb.2825/) It's about as fast as "Best cas".) // // On ramie (2.53GHz E5540) // Best nop time= 1.074300ns // Best cas time= 8.595600ns // Best mutex time= 19.340201ns // Best rwlock time= 34.024799ns // Best util rwlock time= 38.680500ns // Best prelocked time= 2.148700ns // Best fair rwlock time= 45.127600ns // On laptop // Best nop time= 2.876000ns // Best cas time= 15.362500ns // Best mutex time= 51.951498ns // Best rwlock time= 97.721201ns // Best util rwlock time=110.456800ns // Best prelocked time= 4.240100ns // Best fair rwlock time=113.119102ns // // Analysis: If the mutex can be prelocked (as cachetable does, it uses the same mutex for the cachetable and for the condition variable protecting the cache table) // then you can save quite a bit. What does the cachetable do? // During pin: (In the common case:) It grabs the mutex, grabs a read lock, and releases the mutex. // During unpin: It grabs the mutex, unlocks the rwlock lock in the pair, and releases the mutex. // Both actions must acquire a cachetable lock during that time, so definitely saves time to do it that way. #include #include #include #include #include #include #include #include #include #include #include #include #include "rwlock_condvar.h" static int verbose=1; static int timing_only=0; static void parse_args (int argc, const char *argv[]) { const char *progname = argv[0]; argc--; argv++; while (argc>0) { if (strcmp(argv[0], "-v")==0) { verbose++; } else if (strcmp(argv[0], "-q")==0) { verbose--; } else if (strcmp(argv[0], "--timing-only")==0) { timing_only=1; } else { fprintf(stderr, "Usage: %s {-q}* {-v}* {--timing-only}\n", progname); exit(1); } argc--; argv++; } } static const int T=6; static const int N=10000000; static double best_nop_time=1e12; static double best_fcall_time=1e12; static double best_cas_time=1e12; static double best_mutex_time=1e12; static double best_rwlock_time=1e12; static double best_util_time=1e12; static double best_prelocked_time=1e12; static double best_frwlock_time=1e12; static double best_frwlock_prelocked_time=1e12; static double mind(double a, double b) { if (a1) fprintf(stderr, "nop = %.6fns/(lock+unlock)\n", diff); best_nop_time=mind(best_nop_time,diff); } } // This function is defined so we can measure the cost of a function call. int fcall_nop (int i) __attribute__((__noinline__)); int fcall_nop (int i) { return i; } void time_fcall (void) __attribute((__noinline__)); void time_fcall (void) { struct timeval start,end; for (int t=0; t1) fprintf(stderr, "fcall = %.6fns/(lock+unlock)\n", diff); best_fcall_time=mind(best_fcall_time,diff); } } void time_cas (void) __attribute__((__noinline__)); void time_cas (void) { volatile int64_t tval = 0; struct timeval start,end; for (int t=0; t1) fprintf(stderr, "cas = %.6fns/(lock+unlock)\n", diff); best_cas_time=mind(best_cas_time,diff); } } void time_pthread_mutex (void) __attribute__((__noinline__)); void time_pthread_mutex (void) { pthread_mutex_t mutex; { int r = pthread_mutex_init(&mutex, NULL); assert(r==0); } struct timeval start,end; pthread_mutex_lock(&mutex); pthread_mutex_unlock(&mutex); for (int t=0; t1) fprintf(stderr, "pthread_mutex = %.6fns/(lock+unlock)\n", diff); best_mutex_time=mind(best_mutex_time,diff); } { int r = pthread_mutex_destroy(&mutex); assert(r==0); } } void time_pthread_rwlock (void) __attribute__((__noinline__)); void time_pthread_rwlock (void) { pthread_rwlock_t mutex; { int r = pthread_rwlock_init(&mutex, NULL); assert(r==0); } struct timeval start,end; pthread_rwlock_rdlock(&mutex); pthread_rwlock_unlock(&mutex); for (int t=0; t1) fprintf(stderr, "pthread_rwlock(r) = %.6fns/(lock+unlock)\n", diff); best_rwlock_time=mind(best_rwlock_time,diff); } { int r = pthread_rwlock_destroy(&mutex); assert(r==0); } } static void util_rwlock_lock (RWLOCK rwlock, toku_mutex_t *mutex) { toku_mutex_lock(mutex); rwlock_read_lock(rwlock, mutex); toku_mutex_unlock(mutex); } static void util_rwlock_unlock (RWLOCK rwlock, toku_mutex_t *mutex) { toku_mutex_lock(mutex); rwlock_read_unlock(rwlock); toku_mutex_unlock(mutex); } // Time the read lock that's in util/rwlock.h void time_util_rwlock(void) __attribute((__noinline__)); void time_util_rwlock(void) { struct st_rwlock rwlock; toku_mutex_t external_mutex; toku_mutex_init(toku_uninstrumented, &external_mutex, nullptr); rwlock_init(toku_uninstrumented, &rwlock); struct timeval start, end; util_rwlock_lock(&rwlock, &external_mutex); util_rwlock_unlock(&rwlock, &external_mutex); for (int t=0; t1) fprintf(stderr, "util_rwlock(r) = %.6fns/(lock+unlock)\n", diff); best_util_time=mind(best_util_time,diff); } rwlock_destroy(&rwlock); toku_mutex_destroy(&external_mutex); } // Time the read lock that's in util/rwlock.h, assuming the mutex is already // held. void time_util_prelocked_rwlock(void) __attribute__((__noinline__)); void time_util_prelocked_rwlock(void) { struct st_rwlock rwlock; toku_mutex_t external_mutex; toku_mutex_init(toku_uninstrumented, &external_mutex, nullptr); toku_mutex_lock(&external_mutex); rwlock_init(toku_uninstrumented, &rwlock); struct timeval start, end; rwlock_read_lock(&rwlock, &external_mutex); rwlock_read_unlock(&rwlock); for (int t=0; t1) fprintf(stderr, "pre_util_rwlock(r) = %.6fns/(lock+unlock)\n", diff); best_prelocked_time=mind(best_prelocked_time,diff); } rwlock_destroy(&rwlock); toku_mutex_unlock(&external_mutex); toku_mutex_destroy(&external_mutex); } void time_frwlock_prelocked(void) __attribute__((__noinline__)); void time_frwlock_prelocked(void) { toku_mutex_t external_mutex; toku_mutex_init(toku_uninstrumented, &external_mutex, nullptr); struct timeval start, end; toku::frwlock x; x.init(&external_mutex); toku_mutex_lock(&external_mutex); bool got_lock; x.read_lock(); x.read_unlock(); got_lock = x.try_read_lock(); invariant(got_lock); x.read_unlock(); x.write_lock(true); x.write_unlock(); got_lock = x.try_write_lock(true); invariant(got_lock); x.write_unlock(); for (int t=0; t1) fprintf(stderr, "frwlock_prelocked = %.6fns/(lock+unlock)\n", diff); best_frwlock_prelocked_time=mind(best_frwlock_prelocked_time,diff); } x.deinit(); toku_mutex_unlock(&external_mutex); toku_mutex_destroy(&external_mutex); } void time_frwlock(void) __attribute__((__noinline__)); void time_frwlock(void) { toku_mutex_t external_mutex; toku_mutex_init(toku_uninstrumented, &external_mutex, nullptr); struct timeval start, end; toku::frwlock x; x.init(&external_mutex); toku_mutex_lock(&external_mutex); x.read_lock(); x.read_unlock(); toku_mutex_unlock(&external_mutex); for (int t=0; t1) fprintf(stderr, "frwlock = %.6fns/(lock+unlock)\n", diff); best_frwlock_time=mind(best_frwlock_time,diff); } x.deinit(); toku_mutex_destroy(&external_mutex); } int main (int argc, const char *argv[]) { parse_args(argc, argv); if (timing_only) { if (1) { // to make it easy to only time the templated frwlock time_nop(); time_fcall(); time_cas(); time_pthread_mutex(); time_pthread_rwlock(); time_util_rwlock(); time_util_prelocked_rwlock(); } time_frwlock(); time_frwlock_prelocked(); if (verbose>0) { if (1) { // to make it easy to only time the templated frwlock printf("// Best nop time=%10.6fns\n", best_nop_time); printf("// Best fcall time=%10.6fns\n", best_fcall_time); printf("// Best cas time=%10.6fns\n", best_cas_time); printf("// Best mutex time=%10.6fns\n", best_mutex_time); printf("// Best rwlock time=%10.6fns\n", best_rwlock_time); printf("// Best util rwlock time=%10.6fns\n", best_util_time); printf("// Best prelocked time=%10.6fns\n", best_prelocked_time); } printf("// Best frwlock time=%10.6fns\n", best_frwlock_time); printf("// Best frwlock_pre time=%10.6fns\n", best_frwlock_prelocked_time); } } return 0; }