2 * Copyright 2017 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include <folly/Benchmark.h>
18 #include <folly/Baton.h>
19 #include <folly/futures/Future.h>
20 #include <folly/futures/InlineExecutor.h>
21 #include <folly/futures/Promise.h>
22 #include <folly/portability/GFlags.h>
23 #include <folly/portability/Semaphore.h>
27 using namespace folly;
36 void someThens(size_t n) {
37 auto f = makeFuture<int>(42);
38 for (size_t i = 0; i < n; i++) {
39 f = f.then(incr<int>);
45 BENCHMARK(constantFuture) {
49 BENCHMARK_RELATIVE(promiseAndFuture) {
51 Future<int> f = p.getFuture();
56 BENCHMARK_RELATIVE(withThen) {
58 Future<int> f = p.getFuture().then(incr<int>);
70 // look for >= 50% relative
71 BENCHMARK_RELATIVE(twoThens) {
75 // look for >= 25% relative
76 BENCHMARK_RELATIVE(fourThens) {
80 // look for >= 1% relative
81 BENCHMARK_RELATIVE(hundredThens) {
85 // Lock contention. Although in practice fulfills tend to be temporally
86 // separate from then()s, still sometimes they will be concurrent. So the
87 // higher this number is, the better.
90 BENCHMARK(no_contention) {
91 std::vector<Promise<int>> promises(10000);
92 std::vector<Future<int>> futures;
93 std::thread producer, consumer;
96 folly::Baton<> b1, b2;
97 for (auto& p : promises) {
98 futures.push_back(p.getFuture());
101 consumer = std::thread([&]{
103 for (auto& f : futures) {
109 producer = std::thread([&]{
111 for (auto& p : promises) {
120 // The only thing we are measuring is how long fulfill + callbacks take
124 BENCHMARK_RELATIVE(contention) {
125 std::vector<Promise<int>> promises(10000);
126 std::vector<Future<int>> futures;
127 std::thread producer, consumer;
129 sem_init(&sem, 0, 0);
132 folly::Baton<> b1, b2;
133 for (auto& p : promises) {
134 futures.push_back(p.getFuture());
137 consumer = std::thread([&]{
139 for (auto& f : futures) {
145 producer = std::thread([&]{
147 for (auto& p : promises) {
157 // The astute reader will notice that we're not *precisely* comparing apples
158 // to apples here. Well, maybe it's like comparing Granny Smith to
159 // Braeburn or something. In the serial version, we waited for the futures
160 // to be all set up, but here we are probably still doing that work
161 // (although in parallel). But even though there is more work (on the order
162 // of 2x), it is being done by two threads. Hopefully most of the difference
163 // we see is due to lock contention and not false parallelism.
165 // Be warned that if the box is under heavy load, this will greatly skew
166 // these results (scheduling overhead will begin to dwarf lock contention).
167 // I'm not sure but I'd guess in Windtunnel this will mean large variance,
168 // because I expect they load the boxes as much as they can?
173 BENCHMARK_DRAW_LINE();
175 // The old way. Throw an exception, and rethrow to access it upstream.
176 void throwAndCatchImpl() {
178 .then([](Try<Unit>&&){ throw std::runtime_error("oh no"); })
179 .then([](Try<Unit>&& t) {
182 } catch(const std::runtime_error& e) {
190 // Not much better. Throw an exception, and access it via the wrapper upstream.
191 // Actually a little worse due to wrapper overhead. then() won't know that the
192 // exception is a runtime_error, so will have to store it as an exception_ptr
193 // anyways. withException will therefore have to rethrow. Note that if we threw
194 // std::exception instead, we would see some wins, as that's the type then()
195 // will try to wrap, so no exception_ptrs/rethrows are necessary.
196 void throwAndCatchWrappedImpl() {
198 .then([](Try<Unit>&&) { throw std::runtime_error("oh no"); })
199 .then([](Try<Unit>&& t) {
200 auto caught = t.withException<std::runtime_error>(
201 [](const std::runtime_error& /* e */) {
208 // Better. Wrap an exception, and rethrow to access it upstream.
209 void throwWrappedAndCatchImpl() {
211 .then([](Try<Unit>&&){
212 return makeFuture<Unit>(std::runtime_error("oh no"));
214 .then([](Try<Unit>&& t) {
217 } catch(const std::runtime_error& e) {
225 // The new way. Wrap an exception, and access it via the wrapper upstream
226 void throwWrappedAndCatchWrappedImpl() {
228 .then([](Try<Unit>&&) {
229 return makeFuture<Unit>(std::runtime_error("oh no"));
231 .then([](Try<Unit>&& t) {
232 auto caught = t.withException<std::runtime_error>(
233 [](const std::runtime_error& /* e */) {
240 // Simulate heavy contention on func
241 void contend(void(*func)()) {
242 folly::BenchmarkSuspender s;
244 const int iters = 1000;
245 pthread_barrier_t barrier;
246 pthread_barrier_init(&barrier, nullptr, N+1);
247 std::vector<std::thread> threads;
248 for (int i = 0; i < N; i++) {
249 threads.push_back(std::thread([&](){
250 pthread_barrier_wait(&barrier);
251 for (int j = 0; j < iters; j++) {
256 pthread_barrier_wait(&barrier);
258 for (auto& t : threads) {
262 pthread_barrier_destroy(&barrier);
265 BENCHMARK(throwAndCatch) {
269 BENCHMARK_RELATIVE(throwAndCatchWrapped) {
270 throwAndCatchWrappedImpl();
273 BENCHMARK_RELATIVE(throwWrappedAndCatch) {
274 throwWrappedAndCatchImpl();
277 BENCHMARK_RELATIVE(throwWrappedAndCatchWrapped) {
278 throwWrappedAndCatchWrappedImpl();
281 BENCHMARK_DRAW_LINE();
283 BENCHMARK(throwAndCatchContended) {
284 contend(throwAndCatchImpl);
287 BENCHMARK_RELATIVE(throwAndCatchWrappedContended) {
288 contend(throwAndCatchWrappedImpl);
291 BENCHMARK_RELATIVE(throwWrappedAndCatchContended) {
292 contend(throwWrappedAndCatchImpl);
295 BENCHMARK_RELATIVE(throwWrappedAndCatchWrappedContended) {
296 contend(throwWrappedAndCatchWrappedImpl);
299 BENCHMARK_DRAW_LINE();
303 explicit Bulky(std::string message) : message_(message) {}
304 std::string message() & {
307 std::string&& message() && {
308 return std::move(message_);
312 std::string message_;
313 std::array<int, 1024> ints_;
315 } // anonymous namespace
317 BENCHMARK(lvalue_get) {
318 BenchmarkSuspender suspender;
319 Optional<Future<Bulky>> future;
320 future = makeFuture(Bulky("Hello"));
321 suspender.dismissing([&] {
322 std::string message = future.value().get().message();
323 doNotOptimizeAway(message);
327 BENCHMARK_RELATIVE(rvalue_get) {
328 BenchmarkSuspender suspender;
329 Optional<Future<Bulky>> future;
330 future = makeFuture(Bulky("Hello"));
331 suspender.dismissing([&] {
332 std::string message = std::move(future.value()).get().message();
333 doNotOptimizeAway(message);
342 auto f = p.getFuture()
347 return makeFuture(std::move(t));
354 return makeFuture(std::move(t));
361 std::vector<Future<T>> fsGen() {
362 std::vector<Future<T>> fs;
363 for (auto i = 0; i < 10; i++) {
364 fs.push_back(fGen<T>());
370 void complexBenchmark() {
372 collectAll(fsGen<T>());
373 collectAny(fsGen<T>());
374 futures::map(fsGen<T>(), [] (const T& t) {
377 futures::map(fsGen<T>(), [] (const T& t) {
378 return makeFuture(T(t));
382 BENCHMARK_DRAW_LINE();
389 BENCHMARK(complexUnit) {
390 complexBenchmark<Unit>();
393 BENCHMARK_RELATIVE(complexBlob4) {
394 complexBenchmark<Blob<4>>();
397 BENCHMARK_RELATIVE(complexBlob8) {
398 complexBenchmark<Blob<8>>();
401 BENCHMARK_RELATIVE(complexBlob64) {
402 complexBenchmark<Blob<64>>();
405 BENCHMARK_RELATIVE(complexBlob128) {
406 complexBenchmark<Blob<128>>();
409 BENCHMARK_RELATIVE(complexBlob256) {
410 complexBenchmark<Blob<256>>();
413 BENCHMARK_RELATIVE(complexBlob512) {
414 complexBenchmark<Blob<512>>();
417 BENCHMARK_RELATIVE(complexBlob1024) {
418 complexBenchmark<Blob<1024>>();
421 BENCHMARK_RELATIVE(complexBlob2048) {
422 complexBenchmark<Blob<2048>>();
425 BENCHMARK_RELATIVE(complexBlob4096) {
426 complexBenchmark<Blob<4096>>();
429 int main(int argc, char** argv) {
430 gflags::ParseCommandLineFlags(&argc, &argv, true);
431 folly::runBenchmarks();