improvements to folly::doNotOptimizeAway
[folly.git] / folly / Benchmark.h
1 /*
2  * Copyright 2016 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>
23 #include <folly/portability/GFlags.h>
24 #include <folly/portability/Time.h>
25
26 #include <cassert>
27 #include <ctime>
28 #include <boost/function_types/function_arity.hpp>
29 #include <functional>
30 #include <glog/logging.h>
31 #include <limits>
32 #include <type_traits>
33
34 DECLARE_bool(benchmark);
35
36 namespace folly {
37
38 /**
39  * Runs all benchmarks defined. Usually put in main().
40  */
41 void runBenchmarks();
42
43 /**
44  * Runs all benchmarks defined if and only if the --benchmark flag has
45  * been passed to the program. Usually put in main().
46  */
47 inline bool runBenchmarksOnFlag() {
48   if (FLAGS_benchmark) {
49     runBenchmarks();
50   }
51   return FLAGS_benchmark;
52 }
53
54 namespace detail {
55
56 typedef std::pair<uint64_t, unsigned int> TimeIterPair;
57
58 /**
59  * Adds a benchmark wrapped in a std::function. Only used
60  * internally. Pass by value is intentional.
61  */
62 void addBenchmarkImpl(const char* file,
63                       const char* name,
64                       std::function<TimeIterPair(unsigned int)>);
65
66 /**
67  * Takes the difference between two timespec values. end is assumed to
68  * occur after start.
69  */
70 inline uint64_t timespecDiff(timespec end, timespec start) {
71   if (end.tv_sec == start.tv_sec) {
72     assert(end.tv_nsec >= start.tv_nsec);
73     return end.tv_nsec - start.tv_nsec;
74   }
75   assert(end.tv_sec > start.tv_sec);
76   auto diff = uint64_t(end.tv_sec - start.tv_sec);
77   assert(diff <
78          std::numeric_limits<uint64_t>::max() / 1000000000UL);
79   return diff * 1000000000UL
80     + end.tv_nsec - start.tv_nsec;
81 }
82
83 /**
84  * Takes the difference between two sets of timespec values. The first
85  * two come from a high-resolution clock whereas the other two come
86  * from a low-resolution clock. The crux of the matter is that
87  * high-res values may be bogus as documented in
88  * http://linux.die.net/man/3/clock_gettime. The trouble is when the
89  * running process migrates from one CPU to another, which is more
90  * likely for long-running processes. Therefore we watch for high
91  * differences between the two timings.
92  *
93  * This function is subject to further improvements.
94  */
95 inline uint64_t timespecDiff(timespec end, timespec start,
96                              timespec endCoarse, timespec startCoarse) {
97   auto fine = timespecDiff(end, start);
98   auto coarse = timespecDiff(endCoarse, startCoarse);
99   if (coarse - fine >= 1000000) {
100     // The fine time is in all likelihood bogus
101     return coarse;
102   }
103   return fine;
104 }
105
106 } // namespace detail
107
108 /**
109  * Supporting type for BENCHMARK_SUSPEND defined below.
110  */
111 struct BenchmarkSuspender {
112   BenchmarkSuspender() {
113     CHECK_EQ(0, clock_gettime(CLOCK_REALTIME, &start));
114   }
115
116   BenchmarkSuspender(const BenchmarkSuspender &) = delete;
117   BenchmarkSuspender(BenchmarkSuspender && rhs) noexcept {
118     start = rhs.start;
119     rhs.start.tv_nsec = rhs.start.tv_sec = 0;
120   }
121
122   BenchmarkSuspender& operator=(const BenchmarkSuspender &) = delete;
123   BenchmarkSuspender& operator=(BenchmarkSuspender && rhs) {
124     if (start.tv_nsec > 0 || start.tv_sec > 0) {
125       tally();
126     }
127     start = rhs.start;
128     rhs.start.tv_nsec = rhs.start.tv_sec = 0;
129     return *this;
130   }
131
132   ~BenchmarkSuspender() {
133     if (start.tv_nsec > 0 || start.tv_sec > 0) {
134       tally();
135     }
136   }
137
138   void dismiss() {
139     assert(start.tv_nsec > 0 || start.tv_sec > 0);
140     tally();
141     start.tv_nsec = start.tv_sec = 0;
142   }
143
144   void rehire() {
145     assert(start.tv_nsec == 0 || start.tv_sec == 0);
146     CHECK_EQ(0, clock_gettime(CLOCK_REALTIME, &start));
147   }
148
149   template <class F>
150   auto dismissing(F f) -> typename std::result_of<F()>::type {
151     SCOPE_EXIT { rehire(); };
152     dismiss();
153     return f();
154   }
155
156   /**
157    * This is for use inside of if-conditions, used in BENCHMARK macros.
158    * If-conditions bypass the explicit on operator bool.
159    */
160   explicit operator bool() const {
161     return false;
162   }
163
164   /**
165    * Accumulates nanoseconds spent outside benchmark.
166    */
167   typedef uint64_t NanosecondsSpent;
168   static NanosecondsSpent nsSpent;
169
170 private:
171   void tally() {
172     timespec end;
173     CHECK_EQ(0, clock_gettime(CLOCK_REALTIME, &end));
174     nsSpent += detail::timespecDiff(end, start);
175     start = end;
176   }
177
178   timespec start;
179 };
180
181 /**
182  * Adds a benchmark. Usually not called directly but instead through
183  * the macro BENCHMARK defined below. The lambda function involved
184  * must take exactly one parameter of type unsigned, and the benchmark
185  * uses it with counter semantics (iteration occurs inside the
186  * function).
187  */
188 template <typename Lambda>
189 typename std::enable_if<
190   boost::function_types::function_arity<decltype(&Lambda::operator())>::value
191   == 2
192 >::type
193 addBenchmark(const char* file, const char* name, Lambda&& lambda) {
194   auto execute = [=](unsigned int times) {
195     BenchmarkSuspender::nsSpent = 0;
196     timespec start, end;
197     unsigned int niter;
198
199     // CORE MEASUREMENT STARTS
200     auto const r1 = clock_gettime(CLOCK_REALTIME, &start);
201     niter = lambda(times);
202     auto const r2 = clock_gettime(CLOCK_REALTIME, &end);
203     // CORE MEASUREMENT ENDS
204
205     CHECK_EQ(0, r1);
206     CHECK_EQ(0, r2);
207
208     return detail::TimeIterPair(
209       detail::timespecDiff(end, start) - BenchmarkSuspender::nsSpent,
210       niter);
211   };
212
213   detail::addBenchmarkImpl(file, name,
214     std::function<detail::TimeIterPair(unsigned int)>(execute));
215 }
216
217 /**
218  * Adds a benchmark. Usually not called directly but instead through
219  * the macro BENCHMARK defined below. The lambda function involved
220  * must take zero parameters, and the benchmark calls it repeatedly
221  * (iteration occurs outside the function).
222  */
223 template <typename Lambda>
224 typename std::enable_if<
225   boost::function_types::function_arity<decltype(&Lambda::operator())>::value
226   == 1
227 >::type
228 addBenchmark(const char* file, const char* name, Lambda&& lambda) {
229   addBenchmark(file, name, [=](unsigned int times) {
230       unsigned int niter = 0;
231       while (times-- > 0) {
232         niter += lambda();
233       }
234       return niter;
235     });
236 }
237
238 /**
239  * Call doNotOptimizeAway(var) to ensure that var will be computed even
240  * post-optimization.  Use it for variables that are computed during
241  * benchmarking but otherwise are useless. The compiler tends to do a
242  * good job at eliminating unused variables, and this function fools it
243  * into thinking var is in fact needed.
244  *
245  * Call makeUnpredictable(var) when you don't want the optimizer to use
246  * its knowledge of var to shape the following code.  This is useful
247  * when constant propagation or power reduction is possible during your
248  * benchmark but not in real use cases.
249  */
250
251 #ifdef _MSC_VER
252
253 #pragma optimize("", off)
254
255 inline void doNotOptimizeDependencySink(const void*) {}
256
257 #pragma optimize("", on)
258
259 template <class T>
260 void doNotOptimizeAway(const T& datum) {
261   doNotOptimizeDependencySink(&datum);
262 }
263
264 template <typename T>
265 void makeUnpredictable(T& datum) {
266   doNotOptimizeDependencySink(&datum);
267 }
268
269 #else
270
271 namespace detail {
272 template <typename T>
273 struct DoNotOptimizeAwayNeedsIndirect {
274   using Decayed = typename std::decay<T>::type;
275
276   // First two constraints ensure it can be an "r" operand.
277   // std::is_pointer check is because callers seem to expect that
278   // doNotOptimizeAway(&x) is equivalent to doNotOptimizeAway(x).
279   constexpr static bool value = !folly::IsTriviallyCopyable<Decayed>::value ||
280       sizeof(Decayed) > sizeof(long) || std::is_pointer<Decayed>::value;
281 };
282 } // detail namespace
283
284 template <typename T>
285 auto doNotOptimizeAway(const T& datum) -> typename std::enable_if<
286     !detail::DoNotOptimizeAwayNeedsIndirect<T>::value>::type {
287   asm volatile("" ::"X"(datum));
288 }
289
290 template <typename T>
291 auto doNotOptimizeAway(const T& datum) -> typename std::enable_if<
292     detail::DoNotOptimizeAwayNeedsIndirect<T>::value>::type {
293   asm volatile("" ::"m"(datum) : "memory");
294 }
295
296 template <typename T>
297 auto makeUnpredictable(T& datum) -> typename std::enable_if<
298     !detail::DoNotOptimizeAwayNeedsIndirect<T>::value>::type {
299   asm volatile("" : "+r"(datum));
300 }
301
302 template <typename T>
303 auto makeUnpredictable(T& datum) -> typename std::enable_if<
304     detail::DoNotOptimizeAwayNeedsIndirect<T>::value>::type {
305   asm volatile("" ::"m"(datum) : "memory");
306 }
307
308 #endif
309
310 } // namespace folly
311
312 /**
313  * Introduces a benchmark function. Used internally, see BENCHMARK and
314  * friends below.
315  */
316 #define BENCHMARK_IMPL(funName, stringName, rv, paramType, paramName)   \
317   static void funName(paramType);                                       \
318   static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) = (           \
319     ::folly::addBenchmark(__FILE__, stringName,                         \
320       [](paramType paramName) -> unsigned { funName(paramName);         \
321                                             return rv; }),              \
322     true);                                                              \
323   static void funName(paramType paramName)
324
325 /**
326  * Introduces a benchmark function with support for returning the actual
327  * number of iterations. Used internally, see BENCHMARK_MULTI and friends
328  * below.
329  */
330 #define BENCHMARK_MULTI_IMPL(funName, stringName, paramType, paramName) \
331   static unsigned funName(paramType);                                   \
332   static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) = (           \
333     ::folly::addBenchmark(__FILE__, stringName,                         \
334       [](paramType paramName) { return funName(paramName); }),          \
335     true);                                                              \
336   static unsigned funName(paramType paramName)
337
338 /**
339  * Introduces a benchmark function. Use with either one or two arguments.
340  * The first is the name of the benchmark. Use something descriptive, such
341  * as insertVectorBegin. The second argument may be missing, or could be a
342  * symbolic counter. The counter dictates how many internal iteration the
343  * benchmark does. Example:
344  *
345  * BENCHMARK(vectorPushBack) {
346  *   vector<int> v;
347  *   v.push_back(42);
348  * }
349  *
350  * BENCHMARK(insertVectorBegin, n) {
351  *   vector<int> v;
352  *   FOR_EACH_RANGE (i, 0, n) {
353  *     v.insert(v.begin(), 42);
354  *   }
355  * }
356  */
357 #define BENCHMARK(name, ...)                                    \
358   BENCHMARK_IMPL(                                               \
359     name,                                                       \
360     FB_STRINGIZE(name),                                         \
361     FB_ARG_2_OR_1(1, ## __VA_ARGS__),                           \
362     FB_ONE_OR_NONE(unsigned, ## __VA_ARGS__),                   \
363     __VA_ARGS__)
364
365 /**
366  * Like BENCHMARK above, but allows the user to return the actual
367  * number of iterations executed in the function body. This can be
368  * useful if the benchmark function doesn't know upfront how many
369  * iterations it's going to run or if it runs through a certain
370  * number of test cases, e.g.:
371  *
372  * BENCHMARK_MULTI(benchmarkSomething) {
373  *   std::vector<int> testCases { 0, 1, 1, 2, 3, 5 };
374  *   for (int c : testCases) {
375  *     doSomething(c);
376  *   }
377  *   return testCases.size();
378  * }
379  */
380 #define BENCHMARK_MULTI(name, ...)                              \
381   BENCHMARK_MULTI_IMPL(                                         \
382     name,                                                       \
383     FB_STRINGIZE(name),                                         \
384     FB_ONE_OR_NONE(unsigned, ## __VA_ARGS__),                   \
385     __VA_ARGS__)
386
387 /**
388  * Defines a benchmark that passes a parameter to another one. This is
389  * common for benchmarks that need a "problem size" in addition to
390  * "number of iterations". Consider:
391  *
392  * void pushBack(uint n, size_t initialSize) {
393  *   vector<int> v;
394  *   BENCHMARK_SUSPEND {
395  *     v.resize(initialSize);
396  *   }
397  *   FOR_EACH_RANGE (i, 0, n) {
398  *    v.push_back(i);
399  *   }
400  * }
401  * BENCHMARK_PARAM(pushBack, 0)
402  * BENCHMARK_PARAM(pushBack, 1000)
403  * BENCHMARK_PARAM(pushBack, 1000000)
404  *
405  * The benchmark above estimates the speed of push_back at different
406  * initial sizes of the vector. The framework will pass 0, 1000, and
407  * 1000000 for initialSize, and the iteration count for n.
408  */
409 #define BENCHMARK_PARAM(name, param)                                    \
410   BENCHMARK_NAMED_PARAM(name, param, param)
411
412 /**
413  * Same as BENCHMARK_PARAM, but allows one to return the actual number of
414  * iterations that have been run.
415  */
416 #define BENCHMARK_PARAM_MULTI(name, param)                              \
417   BENCHMARK_NAMED_PARAM_MULTI(name, param, param)
418
419 /*
420  * Like BENCHMARK_PARAM(), but allows a custom name to be specified for each
421  * parameter, rather than using the parameter value.
422  *
423  * Useful when the parameter value is not a valid token for string pasting,
424  * of when you want to specify multiple parameter arguments.
425  *
426  * For example:
427  *
428  * void addValue(uint n, int64_t bucketSize, int64_t min, int64_t max) {
429  *   Histogram<int64_t> hist(bucketSize, min, max);
430  *   int64_t num = min;
431  *   FOR_EACH_RANGE (i, 0, n) {
432  *     hist.addValue(num);
433  *     ++num;
434  *     if (num > max) { num = min; }
435  *   }
436  * }
437  *
438  * BENCHMARK_NAMED_PARAM(addValue, 0_to_100, 1, 0, 100)
439  * BENCHMARK_NAMED_PARAM(addValue, 0_to_1000, 10, 0, 1000)
440  * BENCHMARK_NAMED_PARAM(addValue, 5k_to_20k, 250, 5000, 20000)
441  */
442 #define BENCHMARK_NAMED_PARAM(name, param_name, ...)                    \
443   BENCHMARK_IMPL(                                                       \
444       FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)),              \
445       FB_STRINGIZE(name) "(" FB_STRINGIZE(param_name) ")",              \
446       iters,                                                            \
447       unsigned,                                                         \
448       iters) {                                                          \
449     name(iters, ## __VA_ARGS__);                                        \
450   }
451
452 /**
453  * Same as BENCHMARK_NAMED_PARAM, but allows one to return the actual number
454  * of iterations that have been run.
455  */
456 #define BENCHMARK_NAMED_PARAM_MULTI(name, param_name, ...)              \
457   BENCHMARK_MULTI_IMPL(                                                 \
458       FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)),              \
459       FB_STRINGIZE(name) "(" FB_STRINGIZE(param_name) ")",              \
460       unsigned,                                                         \
461       iters) {                                                          \
462     return name(iters, ## __VA_ARGS__);                                 \
463   }
464
465 /**
466  * Just like BENCHMARK, but prints the time relative to a
467  * baseline. The baseline is the most recent BENCHMARK() seen in
468  * the current scope. Example:
469  *
470  * // This is the baseline
471  * BENCHMARK(insertVectorBegin, n) {
472  *   vector<int> v;
473  *   FOR_EACH_RANGE (i, 0, n) {
474  *     v.insert(v.begin(), 42);
475  *   }
476  * }
477  *
478  * BENCHMARK_RELATIVE(insertListBegin, n) {
479  *   list<int> s;
480  *   FOR_EACH_RANGE (i, 0, n) {
481  *     s.insert(s.begin(), 42);
482  *   }
483  * }
484  *
485  * Any number of relative benchmark can be associated with a
486  * baseline. Another BENCHMARK() occurrence effectively establishes a
487  * new baseline.
488  */
489 #define BENCHMARK_RELATIVE(name, ...)                           \
490   BENCHMARK_IMPL(                                               \
491     name,                                                       \
492     "%" FB_STRINGIZE(name),                                     \
493     FB_ARG_2_OR_1(1, ## __VA_ARGS__),                           \
494     FB_ONE_OR_NONE(unsigned, ## __VA_ARGS__),                   \
495     __VA_ARGS__)
496
497 /**
498  * Same as BENCHMARK_RELATIVE, but allows one to return the actual number
499  * of iterations that have been run.
500  */
501 #define BENCHMARK_RELATIVE_MULTI(name, ...)                     \
502   BENCHMARK_MULTI_IMPL(                                         \
503     name,                                                       \
504     "%" FB_STRINGIZE(name),                                     \
505     FB_ONE_OR_NONE(unsigned, ## __VA_ARGS__),                   \
506     __VA_ARGS__)
507
508 /**
509  * A combination of BENCHMARK_RELATIVE and BENCHMARK_PARAM.
510  */
511 #define BENCHMARK_RELATIVE_PARAM(name, param)                           \
512   BENCHMARK_RELATIVE_NAMED_PARAM(name, param, param)
513
514 /**
515  * Same as BENCHMARK_RELATIVE_PARAM, but allows one to return the actual
516  * number of iterations that have been run.
517  */
518 #define BENCHMARK_RELATIVE_PARAM_MULTI(name, param)                     \
519   BENCHMARK_RELATIVE_NAMED_PARAM_MULTI(name, param, param)
520
521 /**
522  * A combination of BENCHMARK_RELATIVE and BENCHMARK_NAMED_PARAM.
523  */
524 #define BENCHMARK_RELATIVE_NAMED_PARAM(name, param_name, ...)           \
525   BENCHMARK_IMPL(                                                       \
526       FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)),              \
527       "%" FB_STRINGIZE(name) "(" FB_STRINGIZE(param_name) ")",          \
528       iters,                                                            \
529       unsigned,                                                         \
530       iters) {                                                          \
531     name(iters, ## __VA_ARGS__);                                        \
532   }
533
534 /**
535  * Same as BENCHMARK_RELATIVE_NAMED_PARAM, but allows one to return the
536  * actual number of iterations that have been run.
537  */
538 #define BENCHMARK_RELATIVE_NAMED_PARAM_MULTI(name, param_name, ...)     \
539   BENCHMARK_MULTI_IMPL(                                                 \
540       FB_CONCATENATE(name, FB_CONCATENATE(_, param_name)),              \
541       "%" FB_STRINGIZE(name) "(" FB_STRINGIZE(param_name) ")",          \
542       unsigned,                                                         \
543       iters) {                                                          \
544     return name(iters, ## __VA_ARGS__);                                 \
545   }
546
547 /**
548  * Draws a line of dashes.
549  */
550 #define BENCHMARK_DRAW_LINE()                                             \
551   static bool FB_ANONYMOUS_VARIABLE(follyBenchmarkUnused) = (             \
552     ::folly::addBenchmark(__FILE__, "-", []() -> unsigned { return 0; }), \
553     true);
554
555 /**
556  * Allows execution of code that doesn't count torward the benchmark's
557  * time budget. Example:
558  *
559  * BENCHMARK_START_GROUP(insertVectorBegin, n) {
560  *   vector<int> v;
561  *   BENCHMARK_SUSPEND {
562  *     v.reserve(n);
563  *   }
564  *   FOR_EACH_RANGE (i, 0, n) {
565  *     v.insert(v.begin(), 42);
566  *   }
567  * }
568  */
569 #define BENCHMARK_SUSPEND                               \
570   if (auto FB_ANONYMOUS_VARIABLE(BENCHMARK_SUSPEND) =   \
571       ::folly::BenchmarkSuspender()) {}                 \
572   else