diff options
Diffstat (limited to 'Source/WTF/benchmarks')
-rw-r--r-- | Source/WTF/benchmarks/ConditionSpeedTest.cpp | 245 | ||||
-rw-r--r-- | Source/WTF/benchmarks/LockFairnessTest.cpp | 129 | ||||
-rw-r--r-- | Source/WTF/benchmarks/LockSpeedTest.cpp | 200 | ||||
-rw-r--r-- | Source/WTF/benchmarks/ToyLocks.h | 508 |
4 files changed, 1082 insertions, 0 deletions
diff --git a/Source/WTF/benchmarks/ConditionSpeedTest.cpp b/Source/WTF/benchmarks/ConditionSpeedTest.cpp new file mode 100644 index 000000000..cb1d101e8 --- /dev/null +++ b/Source/WTF/benchmarks/ConditionSpeedTest.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// On Mac, you can build this like so: +// clang++ -o ConditionSpeedTest Source/WTF/benchmarks/ConditionSpeedTest.cpp -O3 -W -ISource/WTF -LWebKitBuild/Release -lWTF -framework Foundation -licucore -std=c++11 + +#include "config.h" + +#include <mutex> +#include <thread> +#include <unistd.h> +#include <wtf/Condition.h> +#include <wtf/CurrentTime.h> +#include <wtf/DataLog.h> +#include <wtf/Deque.h> +#include <wtf/Lock.h> +#include <wtf/StdLibExtras.h> +#include <wtf/StringPrintStream.h> +#include <wtf/Threading.h> +#include <wtf/ThreadingPrimitives.h> +#include <wtf/Vector.h> + +namespace { + +const bool verbose = false; + +unsigned numProducers; +unsigned numConsumers; +unsigned maxQueueSize; +unsigned numMessagesPerProducer; + +NO_RETURN void usage() +{ + printf("Usage: ConditionSpeedTest lock|mutex|all <num producers> <num consumers> <max queue size> <num messages per producer>\n"); + exit(1); +} + +template<typename Functor, typename ConditionType, typename LockType> +void wait(ConditionType& condition, LockType& lock, const Functor& predicate) +{ + while (!predicate()) + condition.wait(lock); +} + +template<typename LockType, typename ConditionType, typename NotifyFunctor, typename NotifyAllFunctor> +void runTest( + unsigned numProducers, + unsigned numConsumers, + unsigned maxQueueSize, + unsigned numMessagesPerProducer, + const NotifyFunctor& notify, + const NotifyAllFunctor& notifyAll) +{ + Deque<unsigned> queue; + bool shouldContinue = true; + LockType lock; + ConditionType emptyCondition; + ConditionType fullCondition; + + Vector<ThreadIdentifier> consumerThreads; + Vector<ThreadIdentifier> producerThreads; + + Vector<unsigned> received; + LockType receivedLock; + + for (unsigned i = numConsumers; i--;) { + ThreadIdentifier threadIdentifier = createThread( + "Consumer thread", + [&] () { + for (;;) { + unsigned result; + unsigned mustNotify = false; + { + std::unique_lock<LockType> locker(lock); + wait( + emptyCondition, lock, + [&] () { + if (verbose) + dataLog(toString(currentThread(), ": Checking consumption predicate with shouldContinue = ", shouldContinue, ", queue.size() == ", queue.size(), "\n")); + return !shouldContinue || !queue.isEmpty(); + }); + if (!shouldContinue && queue.isEmpty()) + return; + mustNotify = queue.size() == maxQueueSize; + result = queue.takeFirst(); + } + notify(fullCondition, mustNotify); + + { + std::lock_guard<LockType> locker(receivedLock); + received.append(result); + } + } + }); + consumerThreads.append(threadIdentifier); + } + + for (unsigned i = numProducers; i--;) { + ThreadIdentifier threadIdentifier = createThread( + "Producer Thread", + [&] () { + for (unsigned i = 0; i < numMessagesPerProducer; ++i) { + bool mustNotify = false; + { + std::unique_lock<LockType> locker(lock); + wait( + fullCondition, lock, + [&] () { + if (verbose) + dataLog(toString(currentThread(), ": Checking production predicate with shouldContinue = ", shouldContinue, ", queue.size() == ", queue.size(), "\n")); + return queue.size() < maxQueueSize; + }); + mustNotify = queue.isEmpty(); + queue.append(i); + } + notify(emptyCondition, mustNotify); + } + }); + producerThreads.append(threadIdentifier); + } + + for (ThreadIdentifier threadIdentifier : producerThreads) + waitForThreadCompletion(threadIdentifier); + + { + std::lock_guard<LockType> locker(lock); + shouldContinue = false; + } + notifyAll(emptyCondition); + + for (ThreadIdentifier threadIdentifier : consumerThreads) + waitForThreadCompletion(threadIdentifier); + + RELEASE_ASSERT(numProducers * numMessagesPerProducer == received.size()); + std::sort(received.begin(), received.end()); + for (unsigned messageIndex = 0; messageIndex < numMessagesPerProducer; ++messageIndex) { + for (unsigned producerIndex = 0; producerIndex < numProducers; ++producerIndex) + RELEASE_ASSERT(messageIndex == received[messageIndex * numProducers + producerIndex]); + } +} + +template<typename LockType, typename ConditionType, typename NotifyFunctor, typename NotifyAllFunctor> +void runBenchmark( + const char* name, + const NotifyFunctor& notify, + const NotifyAllFunctor& notifyAll) +{ + double before = monotonicallyIncreasingTimeMS(); + + runTest<LockType, ConditionType>( + numProducers, + numConsumers, + maxQueueSize, + numMessagesPerProducer, + notify, + notifyAll); + + double after = monotonicallyIncreasingTimeMS(); + + printf("%s: %.3lf ms.\n", name, after - before); +} + +} // anonymous namespace + +int main(int argc, char** argv) +{ + WTF::initializeThreading(); + + if (argc != 6 + || sscanf(argv[2], "%u", &numProducers) != 1 + || sscanf(argv[3], "%u", &numConsumers) != 1 + || sscanf(argv[4], "%u", &maxQueueSize) != 1 + || sscanf(argv[5], "%u", &numMessagesPerProducer) != 1) + usage(); + + bool didRun = false; + if (!strcmp(argv[1], "lock") || !strcmp(argv[1], "all")) { + runBenchmark<Lock, Condition>( + "WTF Lock NotifyOne", + [&] (Condition& condition, bool mustNotify) { + condition.notifyOne(); + }, + [&] (Condition& condition) { + condition.notifyAll(); + }); + runBenchmark<Lock, Condition>( + "WTF Lock NotifyAll", + [&] (Condition& condition, bool mustNotify) { + if (mustNotify) + condition.notifyAll(); + }, + [&] (Condition& condition) { + condition.notifyAll(); + }); + didRun = true; + } + if (!strcmp(argv[1], "mutex") || !strcmp(argv[1], "all")) { + runBenchmark<Mutex, ThreadCondition>( + "Platform Mutex NotifyOne", + [&] (ThreadCondition& condition, bool mustNotify) { + condition.signal(); + }, + [&] (ThreadCondition& condition) { + condition.broadcast(); + }); + runBenchmark<Mutex, ThreadCondition>( + "Platform Mutex NotifyAll", + [&] (ThreadCondition& condition, bool mustNotify) { + if (mustNotify) + condition.broadcast(); + }, + [&] (ThreadCondition& condition) { + condition.broadcast(); + }); + didRun = true; + } + + if (!didRun) + usage(); + + return 0; +} + diff --git a/Source/WTF/benchmarks/LockFairnessTest.cpp b/Source/WTF/benchmarks/LockFairnessTest.cpp new file mode 100644 index 000000000..f08321c0f --- /dev/null +++ b/Source/WTF/benchmarks/LockFairnessTest.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// On Mac, you can build this like so: +// xcrun clang++ -o LockFairnessTest Source/WTF/benchmarks/LockFairnessTest.cpp -O3 -W -ISource/WTF -ISource/WTF/benchmarks -LWebKitBuild/Release -lWTF -framework Foundation -licucore -std=c++11 -fvisibility=hidden + +#include "config.h" + +#include "ToyLocks.h" +#include <thread> +#include <unistd.h> +#include <wtf/CommaPrinter.h> +#include <wtf/CurrentTime.h> +#include <wtf/DataLog.h> +#include <wtf/HashMap.h> +#include <wtf/Lock.h> +#include <wtf/ParkingLot.h> +#include <wtf/StdLibExtras.h> +#include <wtf/Threading.h> +#include <wtf/ThreadingPrimitives.h> +#include <wtf/Vector.h> +#include <wtf/WordLock.h> +#include <wtf/text/CString.h> + +namespace { + +NO_RETURN void usage() +{ + printf("Usage: LockFairnessTest yieldspinlock|pausespinlock|wordlock|lock|barginglock|bargingwordlock|thunderlock|thunderwordlock|cascadelock|cascadewordlockhandofflock|mutex|all <num threads> <seconds per test> <microseconds in critical section>\n"); + exit(1); +} + +unsigned numThreads; +double secondsPerTest; +unsigned microsecondsInCriticalSection; + +struct Benchmark { + template<typename LockType> + static void run(const char* name) + { + LockType lock; + std::unique_ptr<unsigned[]> counts = std::make_unique<unsigned[]>(numThreads); + std::unique_ptr<ThreadIdentifier[]> threads = std::make_unique<ThreadIdentifier[]>(numThreads); + + volatile bool keepGoing = true; + + lock.lock(); + + for (unsigned threadIndex = numThreads; threadIndex--;) { + counts[threadIndex] = 0; + threads[threadIndex] = createThread( + "Benchmark Thread", + [&, threadIndex] () { + if (!microsecondsInCriticalSection) { + while (keepGoing) { + lock.lock(); + counts[threadIndex]++; + lock.unlock(); + } + return; + } + + while (keepGoing) { + lock.lock(); + counts[threadIndex]++; + usleep(microsecondsInCriticalSection); + lock.unlock(); + } + }); + } + + sleepMS(100); + lock.unlock(); + + sleep(secondsPerTest); + + keepGoing = false; + lock.lock(); + + dataLog(name, ": "); + CommaPrinter comma; + for (unsigned threadIndex = numThreads; threadIndex--;) + dataLog(comma, counts[threadIndex]); + dataLog("\n"); + + lock.unlock(); + for (unsigned threadIndex = numThreads; threadIndex--;) + waitForThreadCompletion(threads[threadIndex]); + } +}; + +} // anonymous namespace + +int main(int argc, char** argv) +{ + WTF::initializeThreading(); + + if (argc != 5 + || sscanf(argv[2], "%u", &numThreads) != 1 + || sscanf(argv[3], "%lf", &secondsPerTest) != 1 + || sscanf(argv[4], "%u", µsecondsInCriticalSection) != 1) + usage(); + + runEverything<Benchmark>(argv[1]); + + return 0; +} diff --git a/Source/WTF/benchmarks/LockSpeedTest.cpp b/Source/WTF/benchmarks/LockSpeedTest.cpp new file mode 100644 index 000000000..545d2e0b0 --- /dev/null +++ b/Source/WTF/benchmarks/LockSpeedTest.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2015-2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// On Mac, you can build this like so: +// xcrun clang++ -o LockSpeedTest Source/WTF/benchmarks/LockSpeedTest.cpp -O3 -W -ISource/WTF -ISource/WTF/benchmarks -LWebKitBuild/Release -lWTF -framework Foundation -licucore -std=c++11 -fvisibility=hidden + +#include "config.h" + +#include "ToyLocks.h" +#include <thread> +#include <unistd.h> +#include <wtf/CurrentTime.h> +#include <wtf/DataLog.h> +#include <wtf/HashMap.h> +#include <wtf/Lock.h> +#include <wtf/ParkingLot.h> +#include <wtf/StdLibExtras.h> +#include <wtf/Threading.h> +#include <wtf/ThreadingPrimitives.h> +#include <wtf/Vector.h> +#include <wtf/WordLock.h> +#include <wtf/text/CString.h> + +namespace { + +unsigned numThreadGroups; +unsigned numThreadsPerGroup; +unsigned workPerCriticalSection; +unsigned workBetweenCriticalSections; +double secondsPerTest; + +NO_RETURN void usage() +{ + printf("Usage: LockSpeedTest yieldspinlock|pausespinlock|wordlock|lock|barginglock|bargingwordlock|thunderlock|thunderwordlock|cascadelock|cascadewordlockhandofflock|mutex|all <num thread groups> <num threads per group> <work per critical section> <work between critical sections> <spin limit> <seconds per test>\n"); + exit(1); +} + +template<typename Type> +struct WithPadding { + Type value; + char buf[300]; // It's best if this isn't perfect to avoid false sharing. +}; + +HashMap<CString, Vector<double>> results; + +void reportResult(const char* name, double value) +{ + dataLogF("%s: %.3lf KHz\n", name, value); + results.add(name, Vector<double>()).iterator->value.append(value); +} + +struct Benchmark { + template<typename LockType> + static void run(const char* name) + { + std::unique_ptr<WithPadding<LockType>[]> locks = std::make_unique<WithPadding<LockType>[]>(numThreadGroups); + std::unique_ptr<WithPadding<double>[]> words = std::make_unique<WithPadding<double>[]>(numThreadGroups); + std::unique_ptr<ThreadIdentifier[]> threads = std::make_unique<ThreadIdentifier[]>(numThreadGroups * numThreadsPerGroup); + + volatile bool keepGoing = true; + + double before = monotonicallyIncreasingTime(); + + Lock numIterationsLock; + uint64_t numIterations = 0; + + for (unsigned threadGroupIndex = numThreadGroups; threadGroupIndex--;) { + words[threadGroupIndex].value = 0; + + for (unsigned threadIndex = numThreadsPerGroup; threadIndex--;) { + threads[threadGroupIndex * numThreadsPerGroup + threadIndex] = createThread( + "Benchmark thread", + [threadGroupIndex, &locks, &words, &keepGoing, &numIterationsLock, &numIterations] () { + double localWord = 0; + double value = 1; + unsigned myNumIterations = 0; + while (keepGoing) { + locks[threadGroupIndex].value.lock(); + for (unsigned j = workPerCriticalSection; j--;) { + words[threadGroupIndex].value += value; + words[threadGroupIndex].value *= 1.01; + value = words[threadGroupIndex].value; + } + locks[threadGroupIndex].value.unlock(); + for (unsigned j = workBetweenCriticalSections; j--;) { + localWord += value; + localWord *= 1.01; + value = localWord; + } + myNumIterations++; + } + LockHolder locker(numIterationsLock); + numIterations += myNumIterations; + }); + } + } + + sleep(secondsPerTest); + keepGoing = false; + + for (unsigned threadIndex = numThreadGroups * numThreadsPerGroup; threadIndex--;) + waitForThreadCompletion(threads[threadIndex]); + + double after = monotonicallyIncreasingTime(); + + reportResult(name, numIterations / (after - before) / 1000); + } +}; + +unsigned rangeMin; +unsigned rangeMax; +unsigned rangeStep; +unsigned* rangeVariable; + +bool parseValue(const char* string, unsigned* variable) +{ + unsigned myRangeMin; + unsigned myRangeMax; + unsigned myRangeStep; + if (sscanf(string, "%u-%u:%u", &myRangeMin, &myRangeMax, &myRangeStep) == 3) { + if (rangeVariable) { + fprintf(stderr, "Can only have one variable with a range.\n"); + return false; + } + + rangeMin = myRangeMin; + rangeMax = myRangeMax; + rangeStep = myRangeStep; + rangeVariable = variable; + return true; + } + + if (sscanf(string, "%u", variable) == 1) + return true; + + return false; +} + +} // anonymous namespace + +int main(int argc, char** argv) +{ + WTF::initializeThreading(); + + if (argc != 8 + || !parseValue(argv[2], &numThreadGroups) + || !parseValue(argv[3], &numThreadsPerGroup) + || !parseValue(argv[4], &workPerCriticalSection) + || !parseValue(argv[5], &workBetweenCriticalSections) + || !parseValue(argv[6], &toyLockSpinLimit) + || sscanf(argv[7], "%lf", &secondsPerTest) != 1) + usage(); + + if (rangeVariable) { + dataLog("Running with rangeMin = ", rangeMin, ", rangeMax = ", rangeMax, ", rangeStep = ", rangeStep, "\n"); + for (unsigned value = rangeMin; value <= rangeMax; value += rangeStep) { + dataLog("Running with value = ", value, "\n"); + *rangeVariable = value; + runEverything<Benchmark>(argv[1]); + } + } else + runEverything<Benchmark>(argv[1]); + + for (auto& entry : results) { + printf("%s = {", entry.key.data()); + bool first = true; + for (double value : entry.value) { + if (first) + first = false; + else + printf(", "); + printf("%.3lf", value); + } + printf("};\n"); + } + + return 0; +} diff --git a/Source/WTF/benchmarks/ToyLocks.h b/Source/WTF/benchmarks/ToyLocks.h new file mode 100644 index 000000000..43ad7960a --- /dev/null +++ b/Source/WTF/benchmarks/ToyLocks.h @@ -0,0 +1,508 @@ +/* + * Copyright (C) 2015-2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ToyLocks_h +#define ToyLocks_h + +#include <thread> +#include <wtf/Atomics.h> +#include <wtf/Lock.h> +#include <wtf/ParkingLot.h> +#include <wtf/WordLock.h> + +#if defined(EXTRA_LOCKS) && EXTRA_LOCKS +#include <synchronic> +#endif + +namespace { + +unsigned toyLockSpinLimit = 40; + +// This is the old WTF::SpinLock class, included here so that we can still compare our new locks to a +// spinlock baseline. +class YieldSpinLock { +public: + YieldSpinLock() + { + m_lock.store(0, std::memory_order_relaxed); + } + + void lock() + { + while (!m_lock.compareExchangeWeak(0, 1, std::memory_order_acquire)) + std::this_thread::yield(); + } + + void unlock() + { + m_lock.store(0, std::memory_order_release); + } + + bool isLocked() const + { + return m_lock.load(std::memory_order_acquire); + } + +private: + Atomic<unsigned> m_lock; +}; + +class PauseSpinLock { +public: + PauseSpinLock() + { + m_lock.store(0, std::memory_order_relaxed); + } + + void lock() + { + while (!m_lock.compareExchangeWeak(0, 1, std::memory_order_acquire)) + asm volatile ("pause"); + } + + void unlock() + { + m_lock.store(0, std::memory_order_release); + } + + bool isLocked() const + { + return m_lock.load(std::memory_order_acquire); + } + +private: + Atomic<unsigned> m_lock; +}; + +#if defined(EXTRA_LOCKS) && EXTRA_LOCKS +class TransactionalSpinLock { +public: + TransactionalSpinLock() + { + m_lock = 0; + } + + void lock() + { + for (;;) { + unsigned char result; + unsigned expected = 0; + unsigned desired = 1; + asm volatile ( + "xacquire; lock; cmpxchgl %3, %2\n\t" + "sete %1" + : "+a"(expected), "=q"(result), "+m"(m_lock) + : "r"(desired) + : "memory"); + if (result) + return; + std::this_thread::yield(); + } + } + + void unlock() + { + asm volatile ( + "xrelease; movl $0, %0" + : + : "m"(m_lock) + : "memory"); + } + + bool isLocked() const + { + return m_lock; + } + +private: + unsigned m_lock; +}; + +class SynchronicLock { +public: + SynchronicLock() + : m_locked(0) + { + } + + void lock() + { + for (;;) { + int state = 0; + if (m_locked.compare_exchange_weak(state, 1, std::memory_order_acquire)) + return; + m_sync.wait_for_change(m_locked, state, std::memory_order_relaxed); + } + } + + void unlock() + { + m_sync.notify_one(m_locked, 0, std::memory_order_release); + } + + bool isLocked() + { + return m_locked.load(); + } + +private: + std::atomic<int> m_locked; + std::experimental::synchronic<int> m_sync; +}; +#endif + +template<typename StateType> +class BargingLock { +public: + BargingLock() + { + m_state.store(0); + } + + void lock() + { + if (LIKELY(m_state.compareExchangeWeak(0, isLockedBit, std::memory_order_acquire))) + return; + + lockSlow(); + } + + void unlock() + { + if (LIKELY(m_state.compareExchangeWeak(isLockedBit, 0, std::memory_order_release))) + return; + + unlockSlow(); + } + + bool isLocked() const + { + return m_state.load(std::memory_order_acquire) & isLockedBit; + } + +private: + NEVER_INLINE void lockSlow() + { + for (unsigned i = toyLockSpinLimit; i--;) { + StateType currentState = m_state.load(); + + if (!(currentState & isLockedBit) + && m_state.compareExchangeWeak(currentState, currentState | isLockedBit)) + return; + + if (currentState & hasParkedBit) + break; + + std::this_thread::yield(); + } + + for (;;) { + StateType currentState = m_state.load(); + + if (!(currentState & isLockedBit) + && m_state.compareExchangeWeak(currentState, currentState | isLockedBit)) + return; + + m_state.compareExchangeWeak(isLockedBit, isLockedBit | hasParkedBit); + + ParkingLot::compareAndPark(&m_state, isLockedBit | hasParkedBit); + } + } + + NEVER_INLINE void unlockSlow() + { + ParkingLot::unparkOne( + &m_state, + [this] (ParkingLot::UnparkResult result) -> intptr_t { + if (result.mayHaveMoreThreads) + m_state.store(hasParkedBit); + else + m_state.store(0); + return 0; + }); + } + + static const StateType isLockedBit = 1; + static const StateType hasParkedBit = 2; + + Atomic<StateType> m_state; +}; + +template<typename StateType> +class ThunderLock { +public: + ThunderLock() + { + m_state.store(Unlocked); + } + + void lock() + { + if (LIKELY(m_state.compareExchangeWeak(Unlocked, Locked, std::memory_order_acquire))) + return; + + lockSlow(); + } + + void unlock() + { + if (LIKELY(m_state.compareExchangeWeak(Locked, Unlocked, std::memory_order_release))) + return; + + unlockSlow(); + } + + bool isLocked() const + { + return m_state.load(std::memory_order_acquire) != Unlocked; + } + +private: + NEVER_INLINE void lockSlow() + { + for (unsigned i = toyLockSpinLimit; i--;) { + State currentState = m_state.load(); + + if (currentState == Unlocked + && m_state.compareExchangeWeak(Unlocked, Locked)) + return; + + if (currentState == LockedAndParked) + break; + + std::this_thread::yield(); + } + + for (;;) { + if (m_state.compareExchangeWeak(Unlocked, Locked)) + return; + + m_state.compareExchangeWeak(Locked, LockedAndParked); + ParkingLot::compareAndPark(&m_state, LockedAndParked); + } + } + + NEVER_INLINE void unlockSlow() + { + if (m_state.exchange(Unlocked) == LockedAndParked) + ParkingLot::unparkAll(&m_state); + } + + enum State : StateType { + Unlocked, + Locked, + LockedAndParked + }; + + Atomic<State> m_state; +}; + +template<typename StateType> +class CascadeLock { +public: + CascadeLock() + { + m_state.store(Unlocked); + } + + void lock() + { + if (LIKELY(m_state.compareExchangeWeak(Unlocked, Locked, std::memory_order_acquire))) + return; + + lockSlow(); + } + + void unlock() + { + if (LIKELY(m_state.compareExchangeWeak(Locked, Unlocked, std::memory_order_release))) + return; + + unlockSlow(); + } + + bool isLocked() const + { + return m_state.load(std::memory_order_acquire) != Unlocked; + } + +private: + NEVER_INLINE void lockSlow() + { + for (unsigned i = toyLockSpinLimit; i--;) { + State currentState = m_state.load(); + + if (currentState == Unlocked + && m_state.compareExchangeWeak(Unlocked, Locked)) + return; + + if (currentState == LockedAndParked) + break; + + std::this_thread::yield(); + } + + State desiredState = Locked; + for (;;) { + if (m_state.compareExchangeWeak(Unlocked, desiredState)) + return; + + desiredState = LockedAndParked; + m_state.compareExchangeWeak(Locked, LockedAndParked); + ParkingLot::compareAndPark(&m_state, LockedAndParked); + } + } + + NEVER_INLINE void unlockSlow() + { + if (m_state.exchange(Unlocked) == LockedAndParked) + ParkingLot::unparkOne(&m_state); + } + + enum State : StateType { + Unlocked, + Locked, + LockedAndParked + }; + + Atomic<State> m_state; +}; + +class HandoffLock { +public: + HandoffLock() + { + m_state.store(0); + } + + void lock() + { + if (LIKELY(m_state.compareExchangeWeak(0, isLockedBit, std::memory_order_acquire))) + return; + + lockSlow(); + } + + void unlock() + { + if (LIKELY(m_state.compareExchangeWeak(isLockedBit, 0, std::memory_order_release))) + return; + + unlockSlow(); + } + + bool isLocked() const + { + return m_state.load(std::memory_order_acquire) & isLockedBit; + } + +private: + NEVER_INLINE void lockSlow() + { + for (;;) { + unsigned state = m_state.load(); + + if (!(state & isLockedBit)) { + if (m_state.compareExchangeWeak(state, state | isLockedBit)) + return; + continue; + } + + if (m_state.compareExchangeWeak(state, state + parkedCountUnit)) { + bool result = ParkingLot::compareAndPark(&m_state, state + parkedCountUnit).wasUnparked; + m_state.exchangeAndAdd(-parkedCountUnit); + if (result) + return; + } + } + } + + NEVER_INLINE void unlockSlow() + { + for (;;) { + unsigned state = m_state.load(); + + if (!(state >> parkedCountShift)) { + RELEASE_ASSERT(state == isLockedBit); + if (m_state.compareExchangeWeak(isLockedBit, 0)) + return; + continue; + } + + if (ParkingLot::unparkOne(&m_state).didUnparkThread) { + // We unparked someone. There are now running and they hold the lock. + return; + } + + // Nobody unparked. Maybe there isn't anyone waiting. Just try again. + } + } + + static const unsigned isLockedBit = 1; + static const unsigned parkedCountShift = 1; + static const unsigned parkedCountUnit = 1 << parkedCountShift; + + Atomic<unsigned> m_state; +}; + +template<typename Benchmark> +void runEverything(const char* what) +{ + if (!strcmp(what, "yieldspinlock") || !strcmp(what, "all")) + Benchmark::template run<YieldSpinLock>("YieldSpinLock"); + if (!strcmp(what, "pausespinlock") || !strcmp(what, "all")) + Benchmark::template run<PauseSpinLock>("PauseSpinLock"); +#if defined(EXTRA_LOCKS) && EXTRA_LOCKS + if (!strcmp(what, "transactionalspinlock") || !strcmp(what, "all")) + Benchmark::template run<TransactionalSpinLock>("TransactionalSpinLock"); + if (!strcmp(what, "synchroniclock") || !strcmp(what, "all")) + Benchmark::template run<SynchronicLock>("SynchronicLock"); +#endif + if (!strcmp(what, "wordlock") || !strcmp(what, "all")) + Benchmark::template run<WordLock>("WTFWordLock"); + if (!strcmp(what, "lock") || !strcmp(what, "all")) + Benchmark::template run<Lock>("WTFLock"); + if (!strcmp(what, "barginglock") || !strcmp(what, "all")) + Benchmark::template run<BargingLock<uint8_t>>("ByteBargingLock"); + if (!strcmp(what, "bargingwordlock") || !strcmp(what, "all")) + Benchmark::template run<BargingLock<uint32_t>>("WordBargingLock"); + if (!strcmp(what, "thunderlock") || !strcmp(what, "all")) + Benchmark::template run<ThunderLock<uint8_t>>("ByteThunderLock"); + if (!strcmp(what, "thunderwordlock") || !strcmp(what, "all")) + Benchmark::template run<ThunderLock<uint32_t>>("WordThunderLock"); + if (!strcmp(what, "cascadelock") || !strcmp(what, "all")) + Benchmark::template run<CascadeLock<uint8_t>>("ByteCascadeLock"); + if (!strcmp(what, "cascadewordlock") || !strcmp(what, "all")) + Benchmark::template run<CascadeLock<uint32_t>>("WordCascadeLock"); + if (!strcmp(what, "handofflock") || !strcmp(what, "all")) + Benchmark::template run<HandoffLock>("HandoffLock"); + if (!strcmp(what, "mutex") || !strcmp(what, "all")) + Benchmark::template run<Mutex>("PlatformMutex"); +} + +} // anonymous namespace + +#endif // ToyLocks_h + |