proxygen
Benchmark.h
Go to the documentation of this file.
1 /*
2  * Copyright 2012-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 #pragma once
18 
19 #include <folly/Portability.h>
20 #include <folly/Preprocessor.h> // for FB_ANONYMOUS_VARIABLE
21 #include <folly/ScopeGuard.h>
22 #include <folly/Traits.h>
25 
26 #include <cassert>
27 #include <chrono>
28 #include <functional>
29 #include <limits>
30 #include <type_traits>
31 
32 #include <boost/function_types/function_arity.hpp>
33 #include <glog/logging.h>
34 
36 
37 namespace folly {
38 
42 void runBenchmarks();
43 
48 inline bool runBenchmarksOnFlag() {
49  if (FLAGS_benchmark) {
50  runBenchmarks();
51  }
52  return FLAGS_benchmark;
53 }
54 
55 namespace detail {
56 
57 using TimeIterPair =
58  std::pair<std::chrono::high_resolution_clock::duration, unsigned int>;
59 using BenchmarkFun = std::function<detail::TimeIterPair(unsigned int)>;
60 
65 };
66 
70  double timeInNs;
71 };
72 
77 void addBenchmarkImpl(
78  const char* file,
79  const char* name,
80  std::function<TimeIterPair(unsigned int)>);
81 
82 } // namespace detail
83 
88  using Clock = std::chrono::high_resolution_clock;
89  using TimePoint = Clock::time_point;
90  using Duration = Clock::duration;
91 
93  start = Clock::now();
94  }
95 
96  BenchmarkSuspender(const BenchmarkSuspender&) = delete;
98  start = rhs.start;
99  rhs.start = {};
100  }
101 
102  BenchmarkSuspender& operator=(const BenchmarkSuspender&) = delete;
104  if (start != TimePoint{}) {
105  tally();
106  }
107  start = rhs.start;
108  rhs.start = {};
109  return *this;
110  }
111 
113  if (start != TimePoint{}) {
114  tally();
115  }
116  }
117 
118  void dismiss() {
119  assert(start != TimePoint{});
120  tally();
121  start = {};
122  }
123 
124  void rehire() {
125  assert(start == TimePoint{});
126  start = Clock::now();
127  }
128 
129  template <class F>
131  SCOPE_EXIT {
132  rehire();
133  };
134  dismiss();
135  return f();
136  }
137 
142  explicit operator bool() const {
143  return false;
144  }
145 
150 
151  private:
152  void tally() {
153  auto end = Clock::now();
154  timeSpent += end - start;
155  start = end;
156  }
157 
159 };
160 
168 template <typename Lambda>
169 typename std::enable_if<
170  boost::function_types::function_arity<
171  decltype(&Lambda::operator())>::value == 2>::type
172 addBenchmark(const char* file, const char* name, Lambda&& lambda) {
173  auto execute = [=](unsigned int times) {
175  unsigned int niter;
176 
177  // CORE MEASUREMENT STARTS
179  niter = lambda(times);
181  // CORE MEASUREMENT ENDS
182 
183  return detail::TimeIterPair(
185  };
186 
188  file, name, std::function<detail::TimeIterPair(unsigned int)>(execute));
189 }
190 
197 template <typename Lambda>
198 typename std::enable_if<
199  boost::function_types::function_arity<
200  decltype(&Lambda::operator())>::value == 1>::type
201 addBenchmark(const char* file, const char* name, Lambda&& lambda) {
202  addBenchmark(file, name, [=](unsigned int times) {
203  unsigned int niter = 0;
204  while (times-- > 0) {
205  niter += lambda();
206  }
207  return niter;
208  });
209 }
210 
224 #ifdef _MSC_VER
225 
226 #pragma optimize("", off)
227 
228 inline void doNotOptimizeDependencySink(const void*) {}
229 
230 #pragma optimize("", on)
231 
232 template <class T>
233 void doNotOptimizeAway(const T& datum) {
234  doNotOptimizeDependencySink(&datum);
235 }
236 
237 template <typename T>
238 void makeUnpredictable(T& datum) {
239  doNotOptimizeDependencySink(&datum);
240 }
241 
242 #else
243 
244 namespace detail {
245 template <typename T>
247  using Decayed = typename std::decay<T>::type;
248 
249  // First two constraints ensure it can be an "r" operand.
250  // std::is_pointer check is because callers seem to expect that
251  // doNotOptimizeAway(&x) is equivalent to doNotOptimizeAway(x).
253  sizeof(Decayed) > sizeof(long) || std::is_pointer<Decayed>::value;
254 };
255 } // namespace detail
256 
257 template <typename T>
258 auto doNotOptimizeAway(const T& datum) -> typename std::enable_if<
260  // The "r" constraint forces the compiler to make datum available
261  // in a register to the asm block, which means that it must have
262  // computed/loaded it. We use this path for things that are <=
263  // sizeof(long) (they have to fit), trivial (otherwise the compiler
264  // doesn't want to put them in a register), and not a pointer (because
265  // doNotOptimizeAway(&foo) would otherwise be a foot gun that didn't
266  // necessarily compute foo).
267  //
268  // An earlier version of this method had a more permissive input operand
269  // constraint, but that caused unnecessary variation between clang and
270  // gcc benchmarks.
271  asm volatile("" ::"r"(datum));
272 }
273 
274 template <typename T>
275 auto doNotOptimizeAway(const T& datum) -> typename std::enable_if<
277  // This version of doNotOptimizeAway tells the compiler that the asm
278  // block will read datum from memory, and that in addition it might read
279  // or write from any memory location. If the memory clobber could be
280  // separated into input and output that would be preferrable.
281  asm volatile("" ::"m"(datum) : "memory");
282 }
283 
284 template <typename T>
285 auto makeUnpredictable(T& datum) -> typename std::enable_if<
287  asm volatile("" : "+r"(datum));
288 }
289 
290 template <typename T>
291 auto makeUnpredictable(T& datum) -> typename std::enable_if<
293  asm volatile("" ::"m"(datum) : "memory");
294 }
295 
296 #endif
297 
298 struct dynamic;
299 
301  const std::vector<detail::BenchmarkResult>& data,
302  dynamic&);
303 
305  const dynamic&,
306  std::vector<detail::BenchmarkResult>&);
307 
309  const std::vector<detail::BenchmarkResult>& base,
310  const std::vector<detail::BenchmarkResult>& test);
311 
312 } // namespace folly
313 
318 #define BENCHMARK_IMPL(funName, stringName, rv, paramType, paramName) \
319  static void funName(paramType); \
320  static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) = \
321  (::folly::addBenchmark( \
322  __FILE__, \
323  stringName, \
324  [](paramType paramName) -> unsigned { \
325  funName(paramName); \
326  return rv; \
327  }), \
328  true); \
329  static void funName(paramType paramName)
330 
336 #define BENCHMARK_MULTI_IMPL(funName, stringName, paramType, paramName) \
337  static unsigned funName(paramType); \
338  static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) = \
339  (::folly::addBenchmark( \
340  __FILE__, \
341  stringName, \
342  [](paramType paramName) { return funName(paramName); }), \
343  true); \
344  static unsigned funName(paramType paramName)
345 
365 #define BENCHMARK(name, ...) \
366  BENCHMARK_IMPL( \
367  name, \
368  FB_STRINGIZE(name), \
369  FB_ARG_2_OR_1(1, ##__VA_ARGS__), \
370  FB_ONE_OR_NONE(unsigned, ##__VA_ARGS__), \
371  __VA_ARGS__)
372 
388 #define BENCHMARK_MULTI(name, ...) \
389  BENCHMARK_MULTI_IMPL( \
390  name, \
391  FB_STRINGIZE(name), \
392  FB_ONE_OR_NONE(unsigned, ##__VA_ARGS__), \
393  __VA_ARGS__)
394 
417 #define BENCHMARK_PARAM(name, param) BENCHMARK_NAMED_PARAM(name, param, param)
418 
423 #define BENCHMARK_PARAM_MULTI(name, param) \
424  BENCHMARK_NAMED_PARAM_MULTI(name, param, param)
425 
426 /*
427  * Like BENCHMARK_PARAM(), but allows a custom name to be specified for each
428  * parameter, rather than using the parameter value.
429  *
430  * Useful when the parameter value is not a valid token for string pasting,
431  * of when you want to specify multiple parameter arguments.
432  *
433  * For example:
434  *
435  * void addValue(uint32_t n, int64_t bucketSize, int64_t min, int64_t max) {
436  * Histogram<int64_t> hist(bucketSize, min, max);
437  * int64_t num = min;
438  * FOR_EACH_RANGE (i, 0, n) {
439  * hist.addValue(num);
440  * ++num;
441  * if (num > max) { num = min; }
442  * }
443  * }
444  *
445  * BENCHMARK_NAMED_PARAM(addValue, 0_to_100, 1, 0, 100)
446  * BENCHMARK_NAMED_PARAM(addValue, 0_to_1000, 10, 0, 1000)
447  * BENCHMARK_NAMED_PARAM(addValue, 5k_to_20k, 250, 5000, 20000)
448  */
449 #define BENCHMARK_NAMED_PARAM(name, param_name, ...) \
450  BENCHMARK_IMPL( \
451  FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)), \
452  FB_STRINGIZE(name) "(" FB_STRINGIZE(param_name) ")", \
453  iters, \
454  unsigned, \
455  iters) { \
456  name(iters, ##__VA_ARGS__); \
457  }
458 
463 #define BENCHMARK_NAMED_PARAM_MULTI(name, param_name, ...) \
464  BENCHMARK_MULTI_IMPL( \
465  FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)), \
466  FB_STRINGIZE(name) "(" FB_STRINGIZE(param_name) ")", \
467  unsigned, \
468  iters) { \
469  return name(iters, ##__VA_ARGS__); \
470  }
471 
496 #define BENCHMARK_RELATIVE(name, ...) \
497  BENCHMARK_IMPL( \
498  name, \
499  "%" FB_STRINGIZE(name), \
500  FB_ARG_2_OR_1(1, ##__VA_ARGS__), \
501  FB_ONE_OR_NONE(unsigned, ##__VA_ARGS__), \
502  __VA_ARGS__)
503 
508 #define BENCHMARK_RELATIVE_MULTI(name, ...) \
509  BENCHMARK_MULTI_IMPL( \
510  name, \
511  "%" FB_STRINGIZE(name), \
512  FB_ONE_OR_NONE(unsigned, ##__VA_ARGS__), \
513  __VA_ARGS__)
514 
518 #define BENCHMARK_RELATIVE_PARAM(name, param) \
519  BENCHMARK_RELATIVE_NAMED_PARAM(name, param, param)
520 
525 #define BENCHMARK_RELATIVE_PARAM_MULTI(name, param) \
526  BENCHMARK_RELATIVE_NAMED_PARAM_MULTI(name, param, param)
527 
531 #define BENCHMARK_RELATIVE_NAMED_PARAM(name, param_name, ...) \
532  BENCHMARK_IMPL( \
533  FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)), \
534  "%" FB_STRINGIZE(name) "(" FB_STRINGIZE(param_name) ")", \
535  iters, \
536  unsigned, \
537  iters) { \
538  name(iters, ##__VA_ARGS__); \
539  }
540 
545 #define BENCHMARK_RELATIVE_NAMED_PARAM_MULTI(name, param_name, ...) \
546  BENCHMARK_MULTI_IMPL( \
547  FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)), \
548  "%" FB_STRINGIZE(name) "(" FB_STRINGIZE(param_name) ")", \
549  unsigned, \
550  iters) { \
551  return name(iters, ##__VA_ARGS__); \
552  }
553 
557 #define BENCHMARK_DRAW_LINE() \
558  static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) = \
559  (::folly::addBenchmark(__FILE__, "-", []() -> unsigned { return 0; }), \
560  true)
561 
576 #define BENCHMARK_SUSPEND \
577  if (auto FB_ANONYMOUS_VARIABLE(BENCHMARK_SUSPEND) = \
578  ::folly::BenchmarkSuspender()) { \
579  } else
Clock::duration Duration
Definition: Benchmark.h:90
auto f
Clock::time_point TimePoint
Definition: Benchmark.h:89
typename invoke_result< F, Args... >::type invoke_result_t
Definition: Invoke.h:142
typename std::decay< T >::type Decayed
Definition: Benchmark.h:247
PskType type
std::chrono::steady_clock::time_point now()
void addBenchmarkImpl(const char *file, const char *name, std::function< TimeIterPair(unsigned int)>)
#define SCOPE_EXIT
Definition: ScopeGuard.h:274
folly::std T
void benchmarkResultsFromDynamic(const dynamic &d, vector< detail::BenchmarkResult > &results)
Definition: Benchmark.cpp:357
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
void runBenchmarks()
Definition: Benchmark.cpp:456
requires E e noexcept(noexcept(s.error(std::move(e))))
std::chrono::high_resolution_clock Clock
Definition: Benchmark.h:88
FOLLY_PUSH_WARNING RHS rhs
Definition: Traits.h:649
void benchmarkResultsToDynamic(const vector< detail::BenchmarkResult > &data, dynamic &out)
Definition: Benchmark.cpp:348
std::pair< std::chrono::high_resolution_clock::duration, unsigned int > TimeIterPair
Definition: Benchmark.h:58
auto end(TestAdlIterable &instance)
Definition: ForeachTest.cpp:62
constexpr auto data(C &c) -> decltype(c.data())
Definition: Access.h:71
DECLARE_bool(benchmark)
std::is_trivially_copyable< T > is_trivially_copyable
Definition: Traits.h:313
static const char *const value
Definition: Conv.cpp:50
BenchmarkSuspender & operator=(BenchmarkSuspender &&rhs)
Definition: Benchmark.h:103
auto start
std::function< detail::TimeIterPair(unsigned int)> BenchmarkFun
Definition: Benchmark.h:59
BenchmarkSuspender(BenchmarkSuspender &&rhs) noexcept
Definition: Benchmark.h:97
static Duration timeSpent
Definition: Benchmark.h:149
auto dismissing(F f) -> invoke_result_t< F >
Definition: Benchmark.h:130
const char * string
Definition: Conv.cpp:212
bool runBenchmarksOnFlag()
Definition: Benchmark.h:48
uint64_t value(const typename LockFreeRingBuffer< T, Atom >::Cursor &rbcursor)
std::enable_if< boost::function_types::function_arity< decltype(&Lambda::operator())>::value==2 >::type addBenchmark(const char *file, const char *name, Lambda &&lambda)
Definition: Benchmark.h:172
Future< Unit > times(const int n, F &&thunk)
Definition: Future-inl.h:2348
void printResultComparison(const vector< detail::BenchmarkResult > &base, const vector< detail::BenchmarkResult > &test)
Definition: Benchmark.cpp:371
void benchmark(size_t n)
auto makeUnpredictable(T &datum) -> typename std::enable_if< !detail::DoNotOptimizeAwayNeedsIndirect< T >::value >::type
Definition: Benchmark.h:285
auto doNotOptimizeAway(const T &datum) -> typename std::enable_if< !detail::DoNotOptimizeAwayNeedsIndirect< T >::value >::type
Definition: Benchmark.h:258