parkinglot benchmark
[folly.git] / folly / synchronization / test / ParkingLotBenchmark.cpp
1 /*
2  * Copyright 2017-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include <thread>
17
18 #include <folly/synchronization/ParkingLot.h>
19
20 #include <folly/Benchmark.h>
21 #include <folly/detail/Futex.h>
22 #include <folly/synchronization/Baton.h>
23
24 DEFINE_uint64(threads, 32, "Number of threads for benchmark");
25
26 using namespace folly;
27
28 namespace {
29 struct SimpleBarrier {
30   explicit SimpleBarrier(size_t count) : lock_(), cv_(), count_(count) {}
31
32   void wait() {
33     std::unique_lock<std::mutex> lockHeld(lock_);
34     auto gen = gen_;
35     if (++num_ == count_) {
36       num_ = 0;
37       gen_++;
38       cv_.notify_all();
39     } else {
40       cv_.wait(lockHeld, [&]() { return gen == gen_; });
41     }
42   }
43
44  private:
45   std::mutex lock_;
46   std::condition_variable cv_;
47   size_t num_{0};
48   size_t count_;
49   size_t gen_{0};
50 };
51 } // namespace
52
53 ParkingLot<> lot;
54
55 BENCHMARK(FutexNoWaitersWake, iters) {
56   BenchmarkSuspender susp;
57   folly::detail::Futex<> fu;
58   SimpleBarrier b(FLAGS_threads + 1);
59
60   std::vector<std::thread> threads{FLAGS_threads};
61   for (auto& t : threads) {
62     t = std::thread([&]() {
63       b.wait();
64       for (int i = 0; i < iters; i++) {
65         fu.futexWake(1);
66       }
67     });
68   }
69   susp.dismiss();
70   b.wait();
71
72   for (auto& t : threads) {
73     t.join();
74   }
75 }
76
77 BENCHMARK_RELATIVE(ParkingLotNoWaitersWake, iters) {
78   BenchmarkSuspender susp;
79   folly::detail::Futex<> fu;
80   SimpleBarrier b(FLAGS_threads + 1);
81
82   std::vector<std::thread> threads{FLAGS_threads};
83   for (auto& t : threads) {
84     t = std::thread([&]() {
85       b.wait();
86       for (int i = 0; i < iters; i++) {
87         lot.unpark(&lot, [](Unit) { return UnparkControl::RetainContinue; });
88       }
89     });
90   }
91   susp.dismiss();
92   b.wait();
93
94   for (auto& t : threads) {
95     t.join();
96   }
97 }
98
99 BENCHMARK(FutexWakeOne, iters) {
100   BenchmarkSuspender susp;
101   folly::detail::Futex<> fu;
102   SimpleBarrier b(FLAGS_threads + 1);
103
104   std::vector<std::thread> threads{FLAGS_threads};
105   for (auto& t : threads) {
106     t = std::thread([&]() {
107       b.wait();
108       while (true) {
109         fu.futexWait(0);
110         if (fu.load(std::memory_order_relaxed)) {
111           return;
112         }
113       }
114     });
115   }
116   susp.dismiss();
117   b.wait();
118   for (int i = 0; i < iters; i++) {
119     fu.futexWake(1);
120   }
121   fu.store(1);
122   fu.futexWake(threads.size());
123
124   for (auto& t : threads) {
125     t.join();
126   }
127 }
128
129 BENCHMARK_RELATIVE(ParkingLotWakeOne, iters) {
130   BenchmarkSuspender susp;
131   std::atomic<bool> done{false};
132   SimpleBarrier b(FLAGS_threads + 1);
133
134   std::vector<std::thread> threads{FLAGS_threads};
135   for (auto& t : threads) {
136     t = std::thread([&]() {
137       b.wait();
138       while (true) {
139         Unit f;
140         lot.park(
141             &done,
142             f,
143             [&] { return done.load(std::memory_order_relaxed) == 0; },
144             [] {});
145         if (done.load(std::memory_order_relaxed)) {
146           return;
147         }
148       }
149     });
150   }
151   susp.dismiss();
152   b.wait();
153   for (int i = 0; i < iters; i++) {
154     lot.unpark(&done, [](Unit) { return UnparkControl::RemoveBreak; });
155   }
156   done = true;
157   lot.unpark(&done, [](Unit) { return UnparkControl::RemoveContinue; });
158
159   for (auto& t : threads) {
160     t.join();
161   }
162 }
163
164 BENCHMARK(FutexWakeAll, iters) {
165   BenchmarkSuspender susp;
166   SimpleBarrier b(FLAGS_threads + 1);
167   folly::detail::Futex<> fu;
168   std::atomic<bool> done{false};
169
170   std::vector<std::thread> threads{FLAGS_threads};
171   for (auto& t : threads) {
172     t = std::thread([&]() {
173       b.wait();
174       while (true) {
175         fu.futexWait(0);
176         if (done.load(std::memory_order_relaxed)) {
177           return;
178         }
179       }
180     });
181   }
182   susp.dismiss();
183   b.wait();
184   for (int i = 0; i < iters; i++) {
185     fu.futexWake(threads.size());
186   }
187   fu.store(1);
188   done = true;
189   fu.futexWake(threads.size());
190
191   for (auto& t : threads) {
192     t.join();
193   }
194 }
195
196 BENCHMARK_RELATIVE(ParkingLotWakeAll, iters) {
197   BenchmarkSuspender susp;
198   SimpleBarrier b(FLAGS_threads + 1);
199   std::atomic<bool> done{false};
200
201   std::vector<std::thread> threads{FLAGS_threads};
202   for (auto& t : threads) {
203     t = std::thread([&]() {
204       b.wait();
205       while (true) {
206         Unit f;
207         lot.park(
208             &done,
209             f,
210             [&] { return done.load(std::memory_order_relaxed) == 0; },
211             [] {});
212         if (done.load(std::memory_order_relaxed)) {
213           return;
214         }
215       }
216     });
217   }
218   susp.dismiss();
219   b.wait();
220   for (int i = 0; i < iters; i++) {
221     lot.unpark(&done, [](Unit) { return UnparkControl::RemoveContinue; });
222   }
223   done = true;
224   lot.unpark(&done, [](Unit) { return UnparkControl::RemoveContinue; });
225
226   for (auto& t : threads) {
227     t.join();
228   }
229 }
230
231 int main(int argc, char** argv) {
232   gflags::ParseCommandLineFlags(&argc, &argv, true);
233
234   folly::runBenchmarks();
235 }
236 /*
237
238 ./buck-out/gen/folly/synchronization/test/parking_lot_test --benchmark
239 --bm_min_iters=10000 --threads=4
240 ============================================================================
241 folly/synchronization/test/ParkingLotBenchmark.cpprelative  time/iter  iters/s
242 ============================================================================
243 FutexNoWaitersWake                                         163.43ns    6.12M
244 ParkingLotNoWaitersWake                           29.64%   551.43ns    1.81M
245 FutexWakeOne                                               156.78ns    6.38M
246 ParkingLotWakeOne                                 37.49%   418.21ns    2.39M
247 FutexWakeAll                                                 1.82us  549.52K
248 ParkingLotWakeAll                                449.63%   404.73ns    2.47M
249 ============================================================================
250
251 ./buck-out/gen/folly/synchronization/test/parking_lot_test --benchmark
252 --bm_min_iters=10000 --threads=32
253 ============================================================================
254 folly/synchronization/test/ParkingLotBenchmark.cpprelative  time/iter  iters/s
255 ============================================================================
256 FutexNoWaitersWake                                         379.59ns    2.63M
257 ParkingLotNoWaitersWake                            7.94%     4.78us  209.08K
258 FutexWakeOne                                               163.59ns    6.11M
259 ParkingLotWakeOne                                  6.41%     2.55us  392.07K
260 FutexWakeAll                                                12.46us   80.27K
261 ParkingLotWakeAll                                784.76%     1.59us  629.92K
262 ============================================================================ */