proxygen
Benchmark.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2014-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 
17 #include <folly/Benchmark.h>
19 #include <folly/futures/Future.h>
20 #include <folly/futures/Promise.h>
24 
25 #include <vector>
26 
27 using namespace folly;
28 
29 namespace {
30 
31 template <class T>
32 T incr(Try<T>&& t) {
33  return t.value() + 1;
34 }
35 
36 void someThens(size_t n) {
37  auto f = makeFuture<int>(42);
38  for (size_t i = 0; i < n; i++) {
39  f = std::move(f).then(incr<int>);
40  }
41 }
42 
43 } // namespace
44 
45 BENCHMARK(constantFuture) {
46  makeFuture(42);
47 }
48 
49 BENCHMARK_RELATIVE(promiseAndFuture) {
50  Promise<int> p;
51  Future<int> f = p.getFuture();
52  p.setValue(42);
53  f.value();
54 }
55 
56 BENCHMARK_RELATIVE(withThen) {
57  Promise<int> p;
58  Future<int> f = p.getFuture().then(incr<int>);
59  p.setValue(42);
60  f.value();
61 }
62 
63 // thens
65 
66 BENCHMARK(oneThen) {
67  someThens(1);
68 }
69 
70 // look for >= 50% relative
71 BENCHMARK_RELATIVE(twoThens) {
72  someThens(2);
73 }
74 
75 // look for >= 25% relative
76 BENCHMARK_RELATIVE(fourThens) {
77  someThens(4);
78 }
79 
80 // look for >= 1% relative
81 BENCHMARK_RELATIVE(hundredThens) {
82  someThens(100);
83 }
84 
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.
89 
90 BENCHMARK(no_contention) {
91  std::vector<Promise<int>> promises(10000);
92  std::vector<Future<int>> futures;
93  std::thread producer, consumer;
94 
96  folly::Baton<> b1, b2;
97  for (auto& p : promises) {
98  futures.push_back(p.getFuture());
99  }
100 
101  consumer = std::thread([&] {
102  b1.post();
103  for (auto& f : futures) {
104  std::move(f).then(incr<int>);
105  }
106  });
107  consumer.join();
108 
109  producer = std::thread([&] {
110  b2.post();
111  for (auto& p : promises) {
112  p.setValue(42);
113  }
114  });
115 
116  b1.wait();
117  b2.wait();
118  }
119 
120  // The only thing we are measuring is how long fulfill + callbacks take
121  producer.join();
122 }
123 
124 BENCHMARK_RELATIVE(contention) {
125  std::vector<Promise<int>> promises(10000);
126  std::vector<Future<int>> futures;
127  std::thread producer, consumer;
128  sem_t sem;
129  sem_init(&sem, 0, 0);
130 
132  folly::Baton<> b1, b2;
133  for (auto& p : promises) {
134  futures.push_back(p.getFuture());
135  }
136 
137  consumer = std::thread([&] {
138  b1.post();
139  for (auto& f : futures) {
140  sem_wait(&sem);
141  std::move(f).then(incr<int>);
142  }
143  });
144 
145  producer = std::thread([&] {
146  b2.post();
147  for (auto& p : promises) {
148  sem_post(&sem);
149  p.setValue(42);
150  }
151  });
152 
153  b1.wait();
154  b2.wait();
155  }
156 
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.
164  //
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?
169  consumer.join();
170  producer.join();
171 }
172 
174 
175 // The old way. Throw an exception, and rethrow to access it upstream.
177  makeFuture()
178  .then([](Try<Unit>&&) { throw std::runtime_error("oh no"); })
179  .then([](Try<Unit>&& t) {
180  try {
181  t.value();
182  } catch (const std::runtime_error& e) {
183  // ...
184  return;
185  }
186  CHECK(false);
187  });
188 }
189 
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.
197  makeFuture()
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 */) {
202  // ...
203  });
204  CHECK(caught);
205  });
206 }
207 
208 // Better. Wrap an exception, and rethrow to access it upstream.
210  makeFuture()
211  .then([](Try<Unit>&&) {
212  return makeFuture<Unit>(std::runtime_error("oh no"));
213  })
214  .then([](Try<Unit>&& t) {
215  try {
216  t.value();
217  } catch (const std::runtime_error& e) {
218  // ...
219  return;
220  }
221  CHECK(false);
222  });
223 }
224 
225 // The new way. Wrap an exception, and access it via the wrapper upstream
227  makeFuture()
228  .then([](Try<Unit>&&) {
229  return makeFuture<Unit>(std::runtime_error("oh no"));
230  })
231  .then([](Try<Unit>&& t) {
232  auto caught = t.withException<std::runtime_error>(
233  [](const std::runtime_error& /* e */) {
234  // ...
235  });
236  CHECK(caught);
237  });
238 }
239 
240 // Simulate heavy contention on func
241 void contend(void (*func)()) {
243  const int N = 100;
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++) {
252  func();
253  }
254  }));
255  }
256  pthread_barrier_wait(&barrier);
257  s.dismiss();
258  for (auto& t : threads) {
259  t.join();
260  }
261  s.rehire();
262  pthread_barrier_destroy(&barrier);
263 }
264 
267 }
268 
269 BENCHMARK_RELATIVE(throwAndCatchWrapped) {
271 }
272 
273 BENCHMARK_RELATIVE(throwWrappedAndCatch) {
275 }
276 
277 BENCHMARK_RELATIVE(throwWrappedAndCatchWrapped) {
279 }
280 
282 
283 BENCHMARK(throwAndCatchContended) {
285 }
286 
287 BENCHMARK_RELATIVE(throwAndCatchWrappedContended) {
289 }
290 
291 BENCHMARK_RELATIVE(throwWrappedAndCatchContended) {
293 }
294 
295 BENCHMARK_RELATIVE(throwWrappedAndCatchWrappedContended) {
297 }
298 
300 
301 namespace {
302 struct Bulky {
303  explicit Bulky(std::string message) : message_(message) {}
304  std::string message() & {
305  return message_;
306  }
307  std::string&& message() && {
308  return std::move(message_);
309  }
310 
311  private:
312  std::string message_;
313  std::array<int, 1024> ints_;
314 };
315 } // anonymous namespace
316 
317 BENCHMARK(lvalue_get) {
318  BenchmarkSuspender suspender;
319  Optional<Future<Bulky>> future;
320  future = makeFuture(Bulky("Hello"));
321  suspender.dismissing([&] {
322  std::string message = std::move(future.value()).get().message();
323  doNotOptimizeAway(message);
324  });
325 }
326 
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);
334  });
335 }
336 
338 
339 template <class T>
341  Promise<T> p;
342  auto f = p.getFuture()
343  .then([](T&& t) { return std::move(t); })
344  .then([](T&& t) { return makeFuture(std::move(t)); })
345  .via(&exe)
346  .then([](T&& t) { return std::move(t); })
347  .then([](T&& t) { return makeFuture(std::move(t)); });
348  p.setValue(T());
349  return f;
350 }
351 
352 template <class T>
353 std::vector<Future<T>> fsGen() {
354  std::vector<Future<T>> fs;
355  for (auto i = 0; i < 10; i++) {
356  fs.push_back(fGen<T>());
357  }
358  return fs;
359 }
360 
361 template <class T>
363  collect(fsGen<T>());
364  collectAll(fsGen<T>());
365  collectAny(fsGen<T>());
366  futures::map(fsGen<T>(), [](const T& t) { return t; });
367  futures::map(fsGen<T>(), [](const T& t) { return makeFuture(T(t)); });
368 }
369 
371 
372 template <size_t S>
373 struct Blob {
374  char buf[S];
375 };
376 
377 BENCHMARK(complexUnit) {
378  complexBenchmark<Unit>();
379 }
380 
381 BENCHMARK_RELATIVE(complexBlob4) {
382  complexBenchmark<Blob<4>>();
383 }
384 
385 BENCHMARK_RELATIVE(complexBlob8) {
386  complexBenchmark<Blob<8>>();
387 }
388 
389 BENCHMARK_RELATIVE(complexBlob64) {
390  complexBenchmark<Blob<64>>();
391 }
392 
393 BENCHMARK_RELATIVE(complexBlob128) {
394  complexBenchmark<Blob<128>>();
395 }
396 
397 BENCHMARK_RELATIVE(complexBlob256) {
398  complexBenchmark<Blob<256>>();
399 }
400 
401 BENCHMARK_RELATIVE(complexBlob512) {
402  complexBenchmark<Blob<512>>();
403 }
404 
405 BENCHMARK_RELATIVE(complexBlob1024) {
406  complexBenchmark<Blob<1024>>();
407 }
408 
409 BENCHMARK_RELATIVE(complexBlob2048) {
410  complexBenchmark<Blob<2048>>();
411 }
412 
413 BENCHMARK_RELATIVE(complexBlob4096) {
414  complexBenchmark<Blob<4096>>();
415 }
416 
417 int main(int argc, char** argv) {
418  gflags::ParseCommandLineFlags(&argc, &argv, true);
420  return 0;
421 }
Definition: test.c:42
auto f
int main(int argc, char **argv)
Definition: Benchmark.cpp:417
void throwAndCatchImpl()
Definition: Benchmark.cpp:176
#define BENCHMARK_SUSPEND
Definition: Benchmark.h:576
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
folly::std T
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
void runBenchmarks()
Definition: Benchmark.cpp:456
Future< T > fGen()
Definition: Benchmark.cpp:340
std::vector< Future< Result > > map(It first, It last, F func)
Definition: Future-inl.h:2358
std::vector< std::thread::id > threads
FOLLY_ALWAYS_INLINE void wait(const WaitOptions &opt=wait_options()) noexcept
Definition: Baton.h:170
char ** argv
void throwAndCatchWrappedImpl()
Definition: Benchmark.cpp:196
Future< std::pair< size_t, Try< typename std::iterator_traits< InputIterator >::value_type::value_type > > > collectAny(InputIterator first, InputIterator last)
Definition: Future-inl.h:1618
Future< T > getFuture()
Definition: Promise-inl.h:97
std::string message
Definition: SPDYCodec.cpp:133
Future< std::tuple< Try< typename remove_cvref_t< Fs >::value_type >... > > collectAll(Fs &&...fs)
Definition: Future-inl.h:1477
BENCHMARK_RELATIVE(promiseAndFuture)
Definition: Benchmark.cpp:49
Definition: Try.h:51
void post() noexcept
Definition: Baton.h:123
void throwWrappedAndCatchWrappedImpl()
Definition: Benchmark.cpp:226
void contend(void(*func)())
Definition: Benchmark.cpp:241
std::enable_if< std::is_same< Unit, B >::value, void >::type setValue()
Definition: Promise.h:326
Future< std::vector< typename std::iterator_traits< InputIterator >::value_type::value_type > > collect(InputIterator first, InputIterator last)
Definition: Future-inl.h:1536
BENCHMARK(fbFollyGlobalBenchmarkBaseline)
Definition: Benchmark.cpp:84
auto dismissing(F f) -> invoke_result_t< F >
Definition: Benchmark.h:130
const char * string
Definition: Conv.cpp:212
BENCHMARK_DRAW_LINE()
static set< string > s
void throwWrappedAndCatchImpl()
Definition: Benchmark.cpp:209
auto via(Executor *x, Func &&func) -> Future< typename isFutureOrSemiFuture< decltype(std::declval< Func >()())>::Inner >
Definition: Future-inl.h:1290
FOLLY_CPP14_CONSTEXPR const Value & value() const &
Definition: Optional.h:268
std::vector< Future< T > > fsGen()
Definition: Benchmark.cpp:353
InlineExecutor exe
Definition: Benchmark.cpp:337
Future< typename std::decay< T >::type > makeFuture(T &&t)
Definition: Future-inl.h:1310
auto doNotOptimizeAway(const T &datum) -> typename std::enable_if< !detail::DoNotOptimizeAwayNeedsIndirect< T >::value >::type
Definition: Benchmark.h:258
void complexBenchmark()
Definition: Benchmark.cpp:362
void throwAndCatch(F f)