2 * Copyright 2015 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 <gflags/gflags.h>
19 #include <folly/Baton.h>
20 #include <folly/Benchmark.h>
21 #include <folly/futures/Future.h>
22 #include <folly/futures/Promise.h>
24 #include <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>);
43 } // anonymous namespace
45 BENCHMARK(constantFuture) {
49 // This shouldn't get too far below 100%
50 BENCHMARK_RELATIVE(promiseAndFuture) {
52 Future<int> f = p.getFuture();
57 // The higher the better. At the time of writing, it's only about 40% :(
58 BENCHMARK_RELATIVE(withThen) {
60 Future<int> f = p.getFuture().then(incr<int>);
72 // look for >= 50% relative
73 BENCHMARK_RELATIVE(twoThens) {
77 // look for >= 25% relative
78 BENCHMARK_RELATIVE(fourThens) {
82 // look for >= 1% relative
83 BENCHMARK_RELATIVE(hundredThens) {
87 // Lock contention. Although in practice fulfills tend to be temporally
88 // separate from then()s, still sometimes they will be concurrent. So the
89 // higher this number is, the better.
92 BENCHMARK(no_contention) {
93 std::vector<Promise<int>> promises(10000);
94 std::vector<Future<int>> futures;
95 std::thread producer, consumer;
98 folly::Baton<> b1, b2;
99 for (auto& p : promises)
100 futures.push_back(p.getFuture());
102 consumer = std::thread([&]{
104 for (auto& f : futures) f.then(incr<int>);
108 producer = std::thread([&]{
110 for (auto& p : promises) p.setValue(42);
117 // The only thing we are measuring is how long fulfill + callbacks take
121 BENCHMARK_RELATIVE(contention) {
122 std::vector<Promise<int>> promises(10000);
123 std::vector<Future<int>> futures;
124 std::thread producer, consumer;
126 sem_init(&sem, 0, 0);
129 folly::Baton<> b1, b2;
130 for (auto& p : promises)
131 futures.push_back(p.getFuture());
133 consumer = std::thread([&]{
135 for (auto& f : futures) {
141 producer = std::thread([&]{
143 for (auto& p : promises) {
153 // The astute reader will notice that we're not *precisely* comparing apples
154 // to apples here. Well, maybe it's like comparing Granny Smith to
155 // Braeburn or something. In the serial version, we waited for the futures
156 // to be all set up, but here we are probably still doing that work
157 // (although in parallel). But even though there is more work (on the order
158 // of 2x), it is being done by two threads. Hopefully most of the difference
159 // we see is due to lock contention and not false parallelism.
161 // Be warned that if the box is under heavy load, this will greatly skew
162 // these results (scheduling overhead will begin to dwarf lock contention).
163 // I'm not sure but I'd guess in Windtunnel this will mean large variance,
164 // because I expect they load the boxes as much as they can?
169 BENCHMARK_DRAW_LINE();
171 // The old way. Throw an exception, and rethrow to access it upstream.
172 void throwAndCatchImpl() {
174 .then([](Try<void>&&){ throw std::runtime_error("oh no"); })
175 .then([](Try<void>&& t) {
178 } catch(const std::runtime_error& e) {
186 // Not much better. Throw an exception, and access it via the wrapper upstream.
187 // Actually a little worse due to wrapper overhead. then() won't know that the
188 // exception is a runtime_error, so will have to store it as an exception_ptr
189 // anyways. withException will therefore have to rethrow. Note that if we threw
190 // std::exception instead, we would see some wins, as that's the type then()
191 // will try to wrap, so no exception_ptrs/rethrows are necessary.
192 void throwAndCatchWrappedImpl() {
194 .then([](Try<void>&&){ throw std::runtime_error("oh no"); })
195 .then([](Try<void>&& t) {
196 auto caught = t.withException<std::runtime_error>(
197 [](const std::runtime_error& e){
204 // Better. Wrap an exception, and rethrow to access it upstream.
205 void throwWrappedAndCatchImpl() {
207 .then([](Try<void>&&){
208 return makeFuture<void>(std::runtime_error("oh no"));
210 .then([](Try<void>&& t) {
213 } catch(const std::runtime_error& e) {
221 // The new way. Wrap an exception, and access it via the wrapper upstream
222 void throwWrappedAndCatchWrappedImpl() {
224 .then([](Try<void>&&){
225 return makeFuture<void>(std::runtime_error("oh no"));
227 .then([](Try<void>&& t){
228 auto caught = t.withException<std::runtime_error>(
229 [](const std::runtime_error& e){
236 // Simulate heavy contention on func
237 void contend(void(*func)()) {
238 folly::BenchmarkSuspender s;
240 const int iters = 1000;
241 pthread_barrier_t barrier;
242 pthread_barrier_init(&barrier, nullptr, N+1);
243 std::vector<std::thread> threads;
244 for (int i = 0; i < N; i++) {
245 threads.push_back(std::thread([&](){
246 pthread_barrier_wait(&barrier);
247 for (int j = 0; j < iters; j++) {
252 pthread_barrier_wait(&barrier);
254 for (auto& t : threads) {
258 pthread_barrier_destroy(&barrier);
261 BENCHMARK(throwAndCatch) {
265 BENCHMARK_RELATIVE(throwAndCatchWrapped) {
266 throwAndCatchWrappedImpl();
269 BENCHMARK_RELATIVE(throwWrappedAndCatch) {
270 throwWrappedAndCatchImpl();
273 BENCHMARK_RELATIVE(throwWrappedAndCatchWrapped) {
274 throwWrappedAndCatchWrappedImpl();
277 BENCHMARK_DRAW_LINE();
279 BENCHMARK(throwAndCatchContended) {
280 contend(throwAndCatchImpl);
283 BENCHMARK_RELATIVE(throwAndCatchWrappedContended) {
284 contend(throwAndCatchWrappedImpl);
287 BENCHMARK_RELATIVE(throwWrappedAndCatchContended) {
288 contend(throwWrappedAndCatchImpl);
291 BENCHMARK_RELATIVE(throwWrappedAndCatchWrappedContended) {
292 contend(throwWrappedAndCatchWrappedImpl);
295 int main(int argc, char** argv) {
296 gflags::ParseCommandLineFlags(&argc, &argv, true);
297 folly::runBenchmarks();