summaryrefslogtreecommitdiff
path: root/Source/WTF/benchmarks
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2016-05-24 08:28:08 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2016-05-24 08:28:08 +0000
commita4e969f4965059196ca948db781e52f7cfebf19e (patch)
tree6ca352808c8fdc52006a0f33f6ae3c593b23867d /Source/WTF/benchmarks
parent41386e9cb918eed93b3f13648cbef387e371e451 (diff)
downloadWebKitGtk-tarball-a4e969f4965059196ca948db781e52f7cfebf19e.tar.gz
webkitgtk-2.12.3webkitgtk-2.12.3
Diffstat (limited to 'Source/WTF/benchmarks')
-rw-r--r--Source/WTF/benchmarks/ConditionSpeedTest.cpp245
-rw-r--r--Source/WTF/benchmarks/LockSpeedTest.cpp173
2 files changed, 418 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/LockSpeedTest.cpp b/Source/WTF/benchmarks/LockSpeedTest.cpp
new file mode 100644
index 000000000..19baceaa8
--- /dev/null
+++ b/Source/WTF/benchmarks/LockSpeedTest.cpp
@@ -0,0 +1,173 @@
+/*
+ * 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 LockSpeedTest Source/WTF/benchmarks/LockSpeedTest.cpp -O3 -W -ISource/WTF -LWebKitBuild/Release -lWTF -framework Foundation -licucore -std=c++11
+
+#include "config.h"
+
+#include <unistd.h>
+#include <wtf/CurrentTime.h>
+#include <wtf/Lock.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/Threading.h>
+#include <wtf/ThreadingPrimitives.h>
+#include <wtf/WordLock.h>
+
+namespace {
+
+// This is the old WTF::SpinLock class, included here so that we can still compare our new locks to a
+// spinlock baseline.
+class SpinLock {
+public:
+ SpinLock()
+ {
+ 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;
+};
+
+unsigned numThreadGroups;
+unsigned numThreadsPerGroup;
+unsigned workPerCriticalSection;
+unsigned numNoiseThreads;
+unsigned numIterations;
+
+NO_RETURN void usage()
+{
+ printf("Usage: LockSpeedTest spinlock|wordlock|lock|mutex|all <num thread groups> <num threads per group> <work per critical section> <num noise threads> <num iterations>\n");
+ exit(1);
+}
+
+template<typename LockType>
+void runBenchmark(const char* name)
+{
+ std::unique_ptr<LockType[]> locks = std::make_unique<LockType[]>(numThreadGroups);
+ std::unique_ptr<double[]> words = std::make_unique<double[]>(numThreadGroups);
+ std::unique_ptr<ThreadIdentifier[]> threads = std::make_unique<ThreadIdentifier[]>(numThreadGroups * numThreadsPerGroup);
+ std::unique_ptr<ThreadIdentifier[]> noiseThreads = std::make_unique<ThreadIdentifier[]>(numNoiseThreads);
+ std::unique_ptr<double[]> noiseCounts = std::make_unique<double[]>(numNoiseThreads);
+
+ volatile bool shouldStop = false;
+ for (unsigned threadIndex = numNoiseThreads; threadIndex--;) {
+ noiseCounts[threadIndex] = 0;
+ noiseThreads[threadIndex] = createThread(
+ "Noise Thread",
+ [&shouldStop, &noiseCounts, threadIndex] () {
+ while (!shouldStop)
+ noiseCounts[threadIndex]++;
+ });
+ }
+
+ double before = monotonicallyIncreasingTimeMS();
+
+ for (unsigned threadGroupIndex = numThreadGroups; threadGroupIndex--;) {
+ words[threadGroupIndex] = 0;
+
+ for (unsigned threadIndex = numThreadsPerGroup; threadIndex--;) {
+ threads[threadGroupIndex * numThreadsPerGroup + threadIndex] = createThread(
+ "Benchmark thread",
+ [threadGroupIndex, &locks, &words] () {
+ for (unsigned i = numIterations; i--;) {
+ locks[threadGroupIndex].lock();
+ for (unsigned j = workPerCriticalSection; j--;) {
+ words[threadGroupIndex]++;
+ words[threadGroupIndex] *= 1.01;
+ }
+ locks[threadGroupIndex].unlock();
+ }
+ });
+ }
+ }
+
+ for (unsigned threadIndex = numThreadGroups * numThreadsPerGroup; threadIndex--;)
+ waitForThreadCompletion(threads[threadIndex]);
+ shouldStop = true;
+ double noiseCount = 0;
+ for (unsigned threadIndex = numNoiseThreads; threadIndex--;) {
+ waitForThreadCompletion(noiseThreads[threadIndex]);
+ noiseCount += noiseCounts[threadIndex];
+ }
+
+ double after = monotonicallyIncreasingTimeMS();
+
+ printf("%s: %.3lf ms, %.0lf noise.\n", name, after - before, noiseCount);
+}
+
+} // anonymous namespace
+
+int main(int argc, char** argv)
+{
+ WTF::initializeThreading();
+
+ if (argc != 7
+ || sscanf(argv[2], "%u", &numThreadGroups) != 1
+ || sscanf(argv[3], "%u", &numThreadsPerGroup) != 1
+ || sscanf(argv[4], "%u", &workPerCriticalSection) != 1
+ || sscanf(argv[5], "%u", &numNoiseThreads) != 1
+ || sscanf(argv[6], "%u", &numIterations) != 1)
+ usage();
+
+ bool didRun = false;
+ if (!strcmp(argv[1], "spinlock") || !strcmp(argv[1], "all")) {
+ runBenchmark<SpinLock>("SpinLock");
+ didRun = true;
+ }
+ if (!strcmp(argv[1], "wordlock") || !strcmp(argv[1], "all")) {
+ runBenchmark<WordLock>("WTF WordLock");
+ didRun = true;
+ }
+ if (!strcmp(argv[1], "lock") || !strcmp(argv[1], "all")) {
+ runBenchmark<Lock>("WTF Lock");
+ didRun = true;
+ }
+ if (!strcmp(argv[1], "mutex") || !strcmp(argv[1], "all")) {
+ runBenchmark<Mutex>("Platform Mutex");
+ didRun = true;
+ }
+
+ if (!didRun)
+ usage();
+
+ return 0;
+}