summaryrefslogtreecommitdiff
path: root/src/mongo/db/concurrency
diff options
context:
space:
mode:
authorRobert Guo <robert.guo@10gen.com>2018-02-19 15:59:34 -0500
committerRobert Guo <robert.guo@10gen.com>2018-03-02 15:56:33 -0500
commit721846a8b4b64d72338b46f91002d4a601c7dbba (patch)
treedc77b248f9039ad66bf0b803743d6bf2dd5c90a7 /src/mongo/db/concurrency
parentf3a0cf8400ad738cc15f0ed21bd00eebe972da50 (diff)
downloadmongo-721846a8b4b64d72338b46f91002d4a601c7dbba.tar.gz
SERVER-33203 add benchmark canary tests
Diffstat (limited to 'src/mongo/db/concurrency')
-rw-r--r--src/mongo/db/concurrency/SConscript10
-rw-r--r--src/mongo/db/concurrency/d_concurrency_bm.cpp185
-rw-r--r--src/mongo/db/concurrency/d_concurrency_test.cpp117
3 files changed, 195 insertions, 117 deletions
diff --git a/src/mongo/db/concurrency/SConscript b/src/mongo/db/concurrency/SConscript
index ba2a08dae90..b14ddc7e836 100644
--- a/src/mongo/db/concurrency/SConscript
+++ b/src/mongo/db/concurrency/SConscript
@@ -53,6 +53,16 @@ env.Library(
],
)
+env.Benchmark(
+ target='lock_manager_bm',
+ source=[
+ 'd_concurrency_bm.cpp',
+ ],
+ LIBDEPS=[
+ '$BUILD_DIR/mongo/db/service_context_noop_init',
+ 'lock_manager',
+ ])
+
env.CppUnitTest(
target='lock_manager_test',
source=['d_concurrency_test.cpp',
diff --git a/src/mongo/db/concurrency/d_concurrency_bm.cpp b/src/mongo/db/concurrency/d_concurrency_bm.cpp
new file mode 100644
index 00000000000..341b4ff7b13
--- /dev/null
+++ b/src/mongo/db/concurrency/d_concurrency_bm.cpp
@@ -0,0 +1,185 @@
+/**
+ * Copyright (C) 2018 MongoDB Inc.
+ *
+ * This program 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.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kDefault
+
+#include "mongo/platform/basic.h"
+#include <benchmark/benchmark.h>
+
+#include "mongo/db/concurrency/d_concurrency.h"
+#include "mongo/db/concurrency/lock_manager_test_help.h"
+#include "mongo/db/storage/recovery_unit_noop.h"
+#include "mongo/stdx/mutex.h"
+#include "mongo/unittest/unittest.h"
+
+namespace mongo {
+namespace {
+
+const int kMaxPerfThreads = 16; // max number of threads to use for lock perf
+
+
+class DConcurrencyTest : public benchmark::Fixture {
+public:
+ /**
+ * Returns a vector of Clients of length 'k', each of which has an OperationContext with its
+ * lockState set to a DefaultLockerImpl.
+ */
+ template <typename LockerType>
+ void makeKClientsWithLockers(int k) {
+ clients.reserve(k);
+ for (int i = 0; i < k; ++i) {
+ auto client = getGlobalServiceContext()->makeClient(
+ str::stream() << "test client for thread " << i);
+ auto opCtx = client->makeOperationContext();
+ opCtx->swapLockState(std::make_unique<LockerType>());
+ clients.emplace_back(std::move(client), std::move(opCtx));
+ }
+ }
+
+protected:
+ std::vector<std::pair<ServiceContext::UniqueClient, ServiceContext::UniqueOperationContext>>
+ clients;
+ std::array<DefaultLockerImpl, kMaxPerfThreads> locker;
+};
+
+BENCHMARK_DEFINE_F(DConcurrencyTest, BM_StdMutex)(benchmark::State& state) {
+ static stdx::mutex mtx;
+
+ for (auto keepRunning : state) {
+ stdx::unique_lock<stdx::mutex> lk(mtx);
+ }
+}
+
+BENCHMARK_DEFINE_F(DConcurrencyTest, BM_ResourceMutexShared)(benchmark::State& state) {
+ static Lock::ResourceMutex mtx("testMutex");
+
+ for (auto keepRunning : state) {
+ Lock::SharedLock lk(&locker[state.thread_index], mtx);
+ }
+}
+
+BENCHMARK_DEFINE_F(DConcurrencyTest, BM_ResourceMutexExclusive)(benchmark::State& state) {
+ static Lock::ResourceMutex mtx("testMutex");
+
+ for (auto keepRunning : state) {
+ Lock::ExclusiveLock lk(&locker[state.thread_index], mtx);
+ }
+}
+
+BENCHMARK_DEFINE_F(DConcurrencyTest, BM_CollectionIntentSharedLock)(benchmark::State& state) {
+ std::unique_ptr<ForceSupportsDocLocking> supportDocLocking;
+
+ if (state.thread_index == 0) {
+ makeKClientsWithLockers<DefaultLockerImpl>(state.threads);
+ supportDocLocking = std::make_unique<ForceSupportsDocLocking>(true);
+ }
+
+ for (auto keepRunning : state) {
+ Lock::DBLock dlk(clients[state.thread_index].second.get(), "test", MODE_IS);
+ Lock::CollectionLock clk(
+ clients[state.thread_index].second->lockState(), "test.coll", MODE_IS);
+ }
+
+ if (state.thread_index == 0) {
+ clients.clear();
+ }
+}
+
+BENCHMARK_DEFINE_F(DConcurrencyTest, BM_CollectionIntentExclusiveLock)(benchmark::State& state) {
+ std::unique_ptr<ForceSupportsDocLocking> supportDocLocking;
+
+ if (state.thread_index == 0) {
+ makeKClientsWithLockers<DefaultLockerImpl>(state.threads);
+ supportDocLocking = std::make_unique<ForceSupportsDocLocking>(true);
+ }
+
+ for (auto keepRunning : state) {
+ Lock::DBLock dlk(clients[state.thread_index].second.get(), "test", MODE_IX);
+ Lock::CollectionLock clk(
+ clients[state.thread_index].second->lockState(), "test.coll", MODE_IX);
+ }
+
+ if (state.thread_index == 0) {
+ clients.clear();
+ }
+}
+
+BENCHMARK_DEFINE_F(DConcurrencyTest, BM_MMAPv1CollectionSharedLock)(benchmark::State& state) {
+ std::unique_ptr<ForceSupportsDocLocking> supportDocLocking;
+
+ if (state.thread_index == 0) {
+ makeKClientsWithLockers<DefaultLockerImpl>(state.threads);
+ supportDocLocking = std::make_unique<ForceSupportsDocLocking>(false);
+ }
+
+ for (auto keepRunning : state) {
+ Lock::DBLock dlk(clients[state.thread_index].second.get(), "test", MODE_IS);
+ Lock::CollectionLock clk(
+ clients[state.thread_index].second->lockState(), "test.coll", MODE_S);
+ }
+
+ if (state.thread_index == 0) {
+ clients.clear();
+ }
+}
+
+BENCHMARK_DEFINE_F(DConcurrencyTest, BM_MMAPv1CollectionExclusiveLock)(benchmark::State& state) {
+ std::unique_ptr<ForceSupportsDocLocking> supportDocLocking;
+
+ if (state.thread_index == 0) {
+ makeKClientsWithLockers<DefaultLockerImpl>(state.threads);
+ supportDocLocking = std::make_unique<ForceSupportsDocLocking>(false);
+ }
+
+ for (auto keepRunning : state) {
+ Lock::DBLock dlk(clients[state.thread_index].second.get(), "test", MODE_IX);
+ Lock::CollectionLock clk(
+ clients[state.thread_index].second->lockState(), "test.coll", MODE_X);
+ }
+
+ if (state.thread_index == 0) {
+ clients.clear();
+ }
+}
+
+BENCHMARK_REGISTER_F(DConcurrencyTest, BM_StdMutex)->ThreadRange(1, kMaxPerfThreads);
+
+BENCHMARK_REGISTER_F(DConcurrencyTest, BM_ResourceMutexShared)->ThreadRange(1, kMaxPerfThreads);
+BENCHMARK_REGISTER_F(DConcurrencyTest, BM_ResourceMutexExclusive)->ThreadRange(1, kMaxPerfThreads);
+
+BENCHMARK_REGISTER_F(DConcurrencyTest, BM_CollectionIntentSharedLock)
+ ->ThreadRange(1, kMaxPerfThreads);
+BENCHMARK_REGISTER_F(DConcurrencyTest, BM_CollectionIntentExclusiveLock)
+ ->ThreadRange(1, kMaxPerfThreads);
+
+BENCHMARK_REGISTER_F(DConcurrencyTest, BM_MMAPv1CollectionSharedLock)
+ ->ThreadRange(1, kMaxPerfThreads);
+BENCHMARK_REGISTER_F(DConcurrencyTest, BM_MMAPv1CollectionExclusiveLock)
+ ->ThreadRange(1, kMaxPerfThreads);
+
+} // namespace
+} // namespace mongo
diff --git a/src/mongo/db/concurrency/d_concurrency_test.cpp b/src/mongo/db/concurrency/d_concurrency_test.cpp
index b8560cd6129..81a9a15a7f8 100644
--- a/src/mongo/db/concurrency/d_concurrency_test.cpp
+++ b/src/mongo/db/concurrency/d_concurrency_test.cpp
@@ -53,9 +53,7 @@
namespace mongo {
namespace {
-const int kMaxPerfThreads = 16; // max number of threads to use for lock perf
const int kMaxStressThreads = 32; // max number of threads to use for lock stress
-const int kMinPerfMillis = 30; // min duration for reliable timing
/**
* A RAII object that instantiates a TicketHolder that limits number of allowed global lock
@@ -132,53 +130,6 @@ public:
return result;
}
- /**
- * Calls fn the given number of iterations, spread out over up to maxThreads threads.
- * The threadNr passed is an integer between 0 and maxThreads exclusive. Logs timing
- * statistics for for all power-of-two thread counts from 1 up to maxThreds.
- */
- void perfTest(stdx::function<void(int threadNr)> fn, int maxThreads) {
- for (int numThreads = 1; numThreads <= maxThreads; numThreads *= 2) {
- std::vector<stdx::thread> threads;
-
- AtomicInt32 ready{0};
- AtomicInt64 elapsedNanos{0};
- AtomicInt64 timedIters{0};
-
- for (int threadId = 0; threadId < numThreads; threadId++)
- threads.emplace_back([&, threadId]() {
- // Busy-wait until everybody is ready
- ready.fetchAndAdd(1);
- while (ready.load() < numThreads) {
- }
-
- uint64_t micros = 0;
- int iters;
- // Ensure at least 16 iterations are done and at least 25 milliseconds is timed
- for (iters = 16; iters < (1 << 30) && micros < kMinPerfMillis * 1000;
- iters *= 2) {
- // Measure the number of loops
- Timer t;
-
- for (int i = 0; i < iters; i++)
- fn(threadId);
-
- micros = t.micros();
- }
-
- elapsedNanos.fetchAndAdd(micros * 1000);
- timedIters.fetchAndAdd(iters);
- });
-
- for (auto& thread : threads)
- thread.join();
-
- log() << numThreads << " threads took: "
- << elapsedNanos.load() / static_cast<double>(timedIters.load()) << " ns per call"
- << (kDebugBuild ? " (DEBUG BUILD!)" : "");
- }
- }
-
private:
ServiceContext::UniqueClient _client;
};
@@ -1447,74 +1398,6 @@ TEST_F(DConcurrencyTestFixture, CompatibleFirstStress) {
}
}
-// These tests exercise single- and multi-threaded performance of uncontended lock acquisition. It
-// is neither practical nor useful to run them on debug builds.
-
-TEST_F(DConcurrencyTestFixture, PerformanceStdMutex) {
- stdx::mutex mtx;
- perfTest([&](int threadId) { stdx::unique_lock<stdx::mutex> lk(mtx); }, kMaxPerfThreads);
-}
-
-TEST_F(DConcurrencyTestFixture, PerformanceResourceMutexShared) {
- Lock::ResourceMutex mtx("testMutex");
- std::array<DefaultLockerImpl, kMaxPerfThreads> locker;
- perfTest([&](int threadId) { Lock::SharedLock lk(&locker[threadId], mtx); }, kMaxPerfThreads);
-}
-
-TEST_F(DConcurrencyTestFixture, PerformanceResourceMutexExclusive) {
- Lock::ResourceMutex mtx("testMutex");
- std::array<DefaultLockerImpl, kMaxPerfThreads> locker;
- perfTest([&](int threadId) { Lock::ExclusiveLock lk(&locker[threadId], mtx); },
- kMaxPerfThreads);
-}
-
-TEST_F(DConcurrencyTestFixture, PerformanceCollectionIntentSharedLock) {
- std::vector<std::pair<ServiceContext::UniqueClient, ServiceContext::UniqueOperationContext>>
- clients = makeKClientsWithLockers<DefaultLockerImpl>(kMaxPerfThreads);
- ForceSupportsDocLocking supported(true);
- perfTest(
- [&](int threadId) {
- Lock::DBLock dlk(clients[threadId].second.get(), "test", MODE_IS);
- Lock::CollectionLock clk(clients[threadId].second->lockState(), "test.coll", MODE_IS);
- },
- kMaxPerfThreads);
-}
-
-TEST_F(DConcurrencyTestFixture, PerformanceCollectionIntentExclusiveLock) {
- std::vector<std::pair<ServiceContext::UniqueClient, ServiceContext::UniqueOperationContext>>
- clients = makeKClientsWithLockers<DefaultLockerImpl>(kMaxPerfThreads);
- ForceSupportsDocLocking supported(true);
- perfTest(
- [&](int threadId) {
- Lock::DBLock dlk(clients[threadId].second.get(), "test", MODE_IX);
- Lock::CollectionLock clk(clients[threadId].second->lockState(), "test.coll", MODE_IX);
- },
- kMaxPerfThreads);
-}
-
-TEST_F(DConcurrencyTestFixture, PerformanceMMAPv1CollectionSharedLock) {
- std::vector<std::pair<ServiceContext::UniqueClient, ServiceContext::UniqueOperationContext>>
- clients = makeKClientsWithLockers<DefaultLockerImpl>(kMaxPerfThreads);
- ForceSupportsDocLocking supported(false);
- perfTest(
- [&](int threadId) {
- Lock::DBLock dlk(clients[threadId].second.get(), "test", MODE_IS);
- Lock::CollectionLock clk(clients[threadId].second->lockState(), "test.coll", MODE_S);
- },
- kMaxPerfThreads);
-}
-
-TEST_F(DConcurrencyTestFixture, PerformanceMMAPv1CollectionExclusive) {
- std::vector<std::pair<ServiceContext::UniqueClient, ServiceContext::UniqueOperationContext>>
- clients = makeKClientsWithLockers<DefaultLockerImpl>(kMaxPerfThreads);
- ForceSupportsDocLocking supported(false);
- perfTest(
- [&](int threadId) {
- Lock::DBLock dlk(clients[threadId].second.get(), "test", MODE_IX);
- Lock::CollectionLock clk(clients[threadId].second->lockState(), "test.coll", MODE_X);
- },
- kMaxPerfThreads);
-}
namespace {
class RecoveryUnitMock : public RecoveryUnitNoop {