diff options
author | Robert Guo <robert.guo@10gen.com> | 2018-02-19 15:59:34 -0500 |
---|---|---|
committer | Robert Guo <robert.guo@10gen.com> | 2018-03-02 15:56:33 -0500 |
commit | 721846a8b4b64d72338b46f91002d4a601c7dbba (patch) | |
tree | dc77b248f9039ad66bf0b803743d6bf2dd5c90a7 /src/mongo/db/concurrency | |
parent | f3a0cf8400ad738cc15f0ed21bd00eebe972da50 (diff) | |
download | mongo-721846a8b4b64d72338b46f91002d4a601c7dbba.tar.gz |
SERVER-33203 add benchmark canary tests
Diffstat (limited to 'src/mongo/db/concurrency')
-rw-r--r-- | src/mongo/db/concurrency/SConscript | 10 | ||||
-rw-r--r-- | src/mongo/db/concurrency/d_concurrency_bm.cpp | 185 | ||||
-rw-r--r-- | src/mongo/db/concurrency/d_concurrency_test.cpp | 117 |
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 { |