1 
2 /**
3  *    Copyright (C) 2018-present MongoDB, Inc.
4  *
5  *    This program is free software: you can redistribute it and/or modify
6  *    it under the terms of the Server Side Public License, version 1,
7  *    as published by MongoDB, Inc.
8  *
9  *    This program is distributed in the hope that it will be useful,
10  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *    Server Side Public License for more details.
13  *
14  *    You should have received a copy of the Server Side Public License
15  *    along with this program. If not, see
16  *    <http://www.mongodb.com/licensing/server-side-public-license>.
17  *
18  *    As a special exception, the copyright holders give permission to link the
19  *    code of portions of this program with the OpenSSL library under certain
20  *    conditions as described in each individual source file and distribute
21  *    linked combinations including the program with the OpenSSL library. You
22  *    must comply with the Server Side Public License in all respects for
23  *    all of the code used other than as permitted herein. If you modify file(s)
24  *    with this exception, you may extend this exception to your version of the
25  *    file(s), but you are not obligated to do so. If you do not wish to do so,
26  *    delete this exception statement from your version. If you delete this
27  *    exception statement from all source files in the program, then also delete
28  *    it in the license file.
29  */
30 #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kDefault
31 
32 #include "mongo/platform/basic.h"
33 #include <benchmark/benchmark.h>
34 
35 #include "mongo/db/concurrency/d_concurrency.h"
36 #include "mongo/db/concurrency/lock_manager_test_help.h"
37 #include "mongo/db/storage/recovery_unit_noop.h"
38 #include "mongo/stdx/mutex.h"
39 #include "mongo/unittest/unittest.h"
40 
41 namespace mongo {
42 namespace {
43 
44 const int kMaxPerfThreads = 16;  // max number of threads to use for lock perf
45 
46 
47 class DConcurrencyTest : public benchmark::Fixture {
48 public:
49     /**
50      * Returns a vector of Clients of length 'k', each of which has an OperationContext with its
51      * lockState set to a DefaultLockerImpl.
52      */
53     template <typename LockerType>
makeKClientsWithLockers(int k)54     void makeKClientsWithLockers(int k) {
55         clients.reserve(k);
56         for (int i = 0; i < k; ++i) {
57             auto client = getGlobalServiceContext()->makeClient(
58                 str::stream() << "test client for thread " << i);
59             auto opCtx = client->makeOperationContext();
60             opCtx->releaseLockState();
61             opCtx->setLockState(stdx::make_unique<LockerType>());
62             clients.emplace_back(std::move(client), std::move(opCtx));
63         }
64     }
65 
66 protected:
67     std::vector<std::pair<ServiceContext::UniqueClient, ServiceContext::UniqueOperationContext>>
68         clients;
69     std::array<DefaultLockerImpl, kMaxPerfThreads> locker;
70 };
71 
BENCHMARK_DEFINE_F(DConcurrencyTest,BM_StdMutex)72 BENCHMARK_DEFINE_F(DConcurrencyTest, BM_StdMutex)(benchmark::State& state) {
73     static stdx::mutex mtx;
74 
75     for (auto keepRunning : state) {
76         stdx::unique_lock<stdx::mutex> lk(mtx);
77     }
78 }
79 
BENCHMARK_DEFINE_F(DConcurrencyTest,BM_ResourceMutexShared)80 BENCHMARK_DEFINE_F(DConcurrencyTest, BM_ResourceMutexShared)(benchmark::State& state) {
81     static Lock::ResourceMutex mtx("testMutex");
82 
83     for (auto keepRunning : state) {
84         Lock::SharedLock lk(&locker[state.thread_index], mtx);
85     }
86 }
87 
BENCHMARK_DEFINE_F(DConcurrencyTest,BM_ResourceMutexExclusive)88 BENCHMARK_DEFINE_F(DConcurrencyTest, BM_ResourceMutexExclusive)(benchmark::State& state) {
89     static Lock::ResourceMutex mtx("testMutex");
90 
91     for (auto keepRunning : state) {
92         Lock::ExclusiveLock lk(&locker[state.thread_index], mtx);
93     }
94 }
95 
BENCHMARK_DEFINE_F(DConcurrencyTest,BM_CollectionIntentSharedLock)96 BENCHMARK_DEFINE_F(DConcurrencyTest, BM_CollectionIntentSharedLock)(benchmark::State& state) {
97     std::unique_ptr<ForceSupportsDocLocking> supportDocLocking;
98 
99     if (state.thread_index == 0) {
100         makeKClientsWithLockers<DefaultLockerImpl>(state.threads);
101         supportDocLocking = std::make_unique<ForceSupportsDocLocking>(true);
102     }
103 
104     for (auto keepRunning : state) {
105         Lock::DBLock dlk(clients[state.thread_index].second.get(), "test", MODE_IS);
106         Lock::CollectionLock clk(
107             clients[state.thread_index].second->lockState(), "test.coll", MODE_IS);
108     }
109 
110     if (state.thread_index == 0) {
111         clients.clear();
112     }
113 }
114 
BENCHMARK_DEFINE_F(DConcurrencyTest,BM_CollectionIntentExclusiveLock)115 BENCHMARK_DEFINE_F(DConcurrencyTest, BM_CollectionIntentExclusiveLock)(benchmark::State& state) {
116     std::unique_ptr<ForceSupportsDocLocking> supportDocLocking;
117 
118     if (state.thread_index == 0) {
119         makeKClientsWithLockers<DefaultLockerImpl>(state.threads);
120         supportDocLocking = std::make_unique<ForceSupportsDocLocking>(true);
121     }
122 
123     for (auto keepRunning : state) {
124         Lock::DBLock dlk(clients[state.thread_index].second.get(), "test", MODE_IX);
125         Lock::CollectionLock clk(
126             clients[state.thread_index].second->lockState(), "test.coll", MODE_IX);
127     }
128 
129     if (state.thread_index == 0) {
130         clients.clear();
131     }
132 }
133 
BENCHMARK_DEFINE_F(DConcurrencyTest,BM_MMAPv1CollectionSharedLock)134 BENCHMARK_DEFINE_F(DConcurrencyTest, BM_MMAPv1CollectionSharedLock)(benchmark::State& state) {
135     std::unique_ptr<ForceSupportsDocLocking> supportDocLocking;
136 
137     if (state.thread_index == 0) {
138         makeKClientsWithLockers<DefaultLockerImpl>(state.threads);
139         supportDocLocking = std::make_unique<ForceSupportsDocLocking>(false);
140     }
141 
142     for (auto keepRunning : state) {
143         Lock::DBLock dlk(clients[state.thread_index].second.get(), "test", MODE_IS);
144         Lock::CollectionLock clk(
145             clients[state.thread_index].second->lockState(), "test.coll", MODE_S);
146     }
147 
148     if (state.thread_index == 0) {
149         clients.clear();
150     }
151 }
152 
BENCHMARK_DEFINE_F(DConcurrencyTest,BM_MMAPv1CollectionExclusiveLock)153 BENCHMARK_DEFINE_F(DConcurrencyTest, BM_MMAPv1CollectionExclusiveLock)(benchmark::State& state) {
154     std::unique_ptr<ForceSupportsDocLocking> supportDocLocking;
155 
156     if (state.thread_index == 0) {
157         makeKClientsWithLockers<DefaultLockerImpl>(state.threads);
158         supportDocLocking = std::make_unique<ForceSupportsDocLocking>(false);
159     }
160 
161     for (auto keepRunning : state) {
162         Lock::DBLock dlk(clients[state.thread_index].second.get(), "test", MODE_IX);
163         Lock::CollectionLock clk(
164             clients[state.thread_index].second->lockState(), "test.coll", MODE_X);
165     }
166 
167     if (state.thread_index == 0) {
168         clients.clear();
169     }
170 }
171 
172 BENCHMARK_REGISTER_F(DConcurrencyTest, BM_StdMutex)->ThreadRange(1, kMaxPerfThreads);
173 
174 BENCHMARK_REGISTER_F(DConcurrencyTest, BM_ResourceMutexShared)->ThreadRange(1, kMaxPerfThreads);
175 BENCHMARK_REGISTER_F(DConcurrencyTest, BM_ResourceMutexExclusive)->ThreadRange(1, kMaxPerfThreads);
176 
177 BENCHMARK_REGISTER_F(DConcurrencyTest, BM_CollectionIntentSharedLock)
178     ->ThreadRange(1, kMaxPerfThreads);
179 BENCHMARK_REGISTER_F(DConcurrencyTest, BM_CollectionIntentExclusiveLock)
180     ->ThreadRange(1, kMaxPerfThreads);
181 
182 BENCHMARK_REGISTER_F(DConcurrencyTest, BM_MMAPv1CollectionSharedLock)
183     ->ThreadRange(1, kMaxPerfThreads);
184 BENCHMARK_REGISTER_F(DConcurrencyTest, BM_MMAPv1CollectionExclusiveLock)
185     ->ThreadRange(1, kMaxPerfThreads);
186 
187 }  // namespace
188 }  // namespace mongo
189