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>
18 #include <folly/Baton.h>
19 #include <folly/Benchmark.h>
20 #include <folly/futures/Future.h>
21 #include <folly/futures/Promise.h>
22 #include <semaphore.h>
25 using namespace folly;
35 void someThens(size_t n) {
36 auto f = makeFuture<int>(42);
37 for (size_t i = 0; i < n; i++) {
38 f = f.then(incr<int>);
42 } // anonymous namespace
44 BENCHMARK(constantFuture) {
48 // This shouldn't get too far below 100%
49 BENCHMARK_RELATIVE(promiseAndFuture) {
51 Future<int> f = p.getFuture();
56 // The higher the better. At the time of writing, it's only about 40% :(
57 BENCHMARK_RELATIVE(withThen) {
59 Future<int> f = p.getFuture().then(incr<int>);
71 // look for >= 50% relative
72 BENCHMARK_RELATIVE(twoThens) {
76 // look for >= 25% relative
77 BENCHMARK_RELATIVE(fourThens) {
81 // look for >= 1% relative
82 BENCHMARK_RELATIVE(hundredThens) {
86 // Lock contention. Although in practice fulfills tend to be temporally
87 // separate from then()s, still sometimes they will be concurrent. So the
88 // higher this number is, the better.
91 BENCHMARK(no_contention) {
92 vector<Promise<int>> promises(10000);
93 vector<Future<int>> futures;
94 std::thread producer, consumer;
97 folly::Baton<> b1, b2;
98 for (auto& p : promises)
99 futures.push_back(p.getFuture());
101 consumer = std::thread([&]{
103 for (auto& f : futures) f.then(incr<int>);
107 producer = std::thread([&]{
109 for (auto& p : promises) p.setValue(42);
116 // The only thing we are measuring is how long fulfill + callbacks take
120 BENCHMARK_RELATIVE(contention) {
121 vector<Promise<int>> promises(10000);
122 vector<Future<int>> futures;
123 std::thread producer, consumer;
125 sem_init(&sem, 0, 0);
128 folly::Baton<> b1, b2;
129 for (auto& p : promises)
130 futures.push_back(p.getFuture());
132 consumer = std::thread([&]{
134 for (auto& f : futures) {
140 producer = std::thread([&]{
142 for (auto& p : promises) {
152 // The astute reader will notice that we're not *precisely* comparing apples
153 // to apples here. Well, maybe it's like comparing Granny Smith to
154 // Braeburn or something. In the serial version, we waited for the futures
155 // to be all set up, but here we are probably still doing that work
156 // (although in parallel). But even though there is more work (on the order
157 // of 2x), it is being done by two threads. Hopefully most of the difference
158 // we see is due to lock contention and not false parallelism.
160 // Be warned that if the box is under heavy load, this will greatly skew
161 // these results (scheduling overhead will begin to dwarf lock contention).
162 // I'm not sure but I'd guess in Windtunnel this will mean large variance,
163 // because I expect they load the boxes as much as they can?
168 BENCHMARK_DRAW_LINE();
170 // The old way. Throw an exception, and rethrow to access it upstream.
171 void throwAndCatchImpl() {
173 .then([](Try<void>&&){ throw std::runtime_error("oh no"); })
174 .then([](Try<void>&& t) {
177 } catch(const std::runtime_error& e) {
185 // Not much better. Throw an exception, and access it via the wrapper upstream.
186 // Actually a little worse due to wrapper overhead. then() won't know that the
187 // exception is a runtime_error, so will have to store it as an exception_ptr
188 // anyways. withException will therefore have to rethrow. Note that if we threw
189 // std::exception instead, we would see some wins, as that's the type then()
190 // will try to wrap, so no exception_ptrs/rethrows are necessary.
191 void throwAndCatchWrappedImpl() {
193 .then([](Try<void>&&){ throw std::runtime_error("oh no"); })
194 .then([](Try<void>&& t) {
195 auto caught = t.withException<std::runtime_error>(
196 [](const std::runtime_error& e){
203 // Better. Wrap an exception, and rethrow to access it upstream.
204 void throwWrappedAndCatchImpl() {
206 .then([](Try<void>&&){
207 return makeFuture<void>(std::runtime_error("oh no"));
209 .then([](Try<void>&& t) {
212 } catch(const std::runtime_error& e) {
220 // The new way. Wrap an exception, and access it via the wrapper upstream
221 void throwWrappedAndCatchWrappedImpl() {
223 .then([](Try<void>&&){
224 return makeFuture<void>(std::runtime_error("oh no"));
226 .then([](Try<void>&& t){
227 auto caught = t.withException<std::runtime_error>(
228 [](const std::runtime_error& e){
235 // Simulate heavy contention on func
236 void contend(void(*func)()) {
237 folly::BenchmarkSuspender s;
239 const int iters = 1000;
240 pthread_barrier_t barrier;
241 pthread_barrier_init(&barrier, nullptr, N+1);
242 std::vector<std::thread> threads;
243 for (int i = 0; i < N; i++) {
244 threads.push_back(std::thread([&](){
245 pthread_barrier_wait(&barrier);
246 for (int j = 0; j < iters; j++) {
251 pthread_barrier_wait(&barrier);
253 for (auto& t : threads) {
257 pthread_barrier_destroy(&barrier);
260 BENCHMARK(throwAndCatch) {
264 BENCHMARK_RELATIVE(throwAndCatchWrapped) {
265 throwAndCatchWrappedImpl();
268 BENCHMARK_RELATIVE(throwWrappedAndCatch) {
269 throwWrappedAndCatchImpl();
272 BENCHMARK_RELATIVE(throwWrappedAndCatchWrapped) {
273 throwWrappedAndCatchWrappedImpl();
276 BENCHMARK_DRAW_LINE();
278 BENCHMARK(throwAndCatchContended) {
279 contend(throwAndCatchImpl);
282 BENCHMARK_RELATIVE(throwAndCatchWrappedContended) {
283 contend(throwAndCatchWrappedImpl);
286 BENCHMARK_RELATIVE(throwWrappedAndCatchContended) {
287 contend(throwWrappedAndCatchImpl);
290 BENCHMARK_RELATIVE(throwWrappedAndCatchWrappedContended) {
291 contend(throwWrappedAndCatchWrappedImpl);
294 int main(int argc, char** argv) {
295 gflags::ParseCommandLineFlags(&argc, &argv, true);
296 folly::runBenchmarks();