From 6867235eb7f33aca43a9d8f4e4afc46c492dcf24 Mon Sep 17 00:00:00 2001 From: Eliot Horowitz Date: Thu, 13 Oct 2011 12:54:44 -0400 Subject: [PATCH] make spinlocks degrade better when under high contention --- dbtests/spin_lock_test.cpp | 10 ++++++++-- util/concurrency/spin_lock.cpp | 31 ++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/dbtests/spin_lock_test.cpp b/dbtests/spin_lock_test.cpp index dbd637ea0e5c3..ed1f1ae1ca53e 100644 --- a/dbtests/spin_lock_test.cpp +++ b/dbtests/spin_lock_test.cpp @@ -20,6 +20,7 @@ #include #include "dbtests.h" #include "../util/concurrency/spin_lock.h" +#include "../util/timer.h" namespace { @@ -73,8 +74,10 @@ namespace { int counter = 0; const int threads = 64; - const int incs = 10000; + const int incs = 50000; LockTester* testers[threads]; + + Timer timer; for ( int i = 0; i < threads; i++ ) { testers[i] = new LockTester( &spin, &counter ); @@ -87,7 +90,10 @@ namespace { ASSERT_EQUALS( testers[i]->requests(), incs ); delete testers[i]; } - + + int ms = timer.millis(); + log() << "spinlock ConcurrentIncs time: " << ms << endl; + ASSERT_EQUALS( counter, threads*incs ); #if defined(__linux__) ASSERT( SpinLock::isfast() ); diff --git a/util/concurrency/spin_lock.cpp b/util/concurrency/spin_lock.cpp index 18ebc97ffe661..eef003e8c23b0 100644 --- a/util/concurrency/spin_lock.cpp +++ b/util/concurrency/spin_lock.cpp @@ -45,7 +45,36 @@ namespace mongo { #if defined(_WIN32) EnterCriticalSection(&_cs); #elif defined(__USE_XOPEN2K) - pthread_spin_lock( &_lock ); + + /** + * this is designed to perform close to the default spin lock + * the reason for the mild insanity is to prevent horrible performance + * when contention spikes + * it allows spinlocks to be used in many more places + * which is good because even with this change they are about 8x faster on linux + */ + + if ( pthread_spin_trylock( &_lock ) == 0 ) + return; + + for ( int i=0; i<1000; i++ ) + if ( pthread_spin_trylock( &_lock ) == 0 ) + return; + + for ( int i=0; i<1000; i++ ) { + if ( pthread_spin_trylock( &_lock ) == 0 ) + return; + pthread_yield(); + } + + struct timespec t; + t.tv_sec = 0; + t.tv_nsec = 5000000; + + while ( pthread_spin_trylock( &_lock ) != 0 ) { + nanosleep(&t, NULL); + } + #elif defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) // fast path if (!_locked && !__sync_lock_test_and_set(&_locked, true)) {