Allow building with -Wmissing-noreturn
[folly.git] / folly / Benchmark.h
index e73dc7c94c4cdf75c28f811f36052008493017dc..4c1e2806de166b6de65629bed1bf8de277814ca5 100644 (file)
  * limitations under the License.
  */
 
-#ifndef FOLLY_BENCHMARK_H_
-#define FOLLY_BENCHMARK_H_
+#pragma once
 
-#include <folly/ScopeGuard.h>
 #include <folly/Portability.h>
 #include <folly/Preprocessor.h> // for FB_ANONYMOUS_VARIABLE
+#include <folly/ScopeGuard.h>
+#include <folly/Traits.h>
+#include <folly/portability/GFlags.h>
+#include <folly/portability/Time.h>
+
 #include <cassert>
 #include <ctime>
 #include <boost/function_types/function_arity.hpp>
 #include <functional>
 #include <glog/logging.h>
-#include <gflags/gflags.h>
 #include <limits>
 #include <type_traits>
 
@@ -51,13 +53,6 @@ inline bool runBenchmarksOnFlag() {
 
 namespace detail {
 
-/**
- * This is the clock ID used for measuring time. On older kernels, the
- * resolution of this clock will be very coarse, which will cause the
- * benchmarks to fail.
- */
-enum Clock { DEFAULT_CLOCK_ID = CLOCK_REALTIME };
-
 typedef std::pair<uint64_t, unsigned int> TimeIterPair;
 
 /**
@@ -115,7 +110,7 @@ inline uint64_t timespecDiff(timespec end, timespec start,
  */
 struct BenchmarkSuspender {
   BenchmarkSuspender() {
-    CHECK_EQ(0, clock_gettime(detail::DEFAULT_CLOCK_ID, &start));
+    CHECK_EQ(0, clock_gettime(CLOCK_REALTIME, &start));
   }
 
   BenchmarkSuspender(const BenchmarkSuspender &) = delete;
@@ -148,7 +143,7 @@ struct BenchmarkSuspender {
 
   void rehire() {
     assert(start.tv_nsec == 0 || start.tv_sec == 0);
-    CHECK_EQ(0, clock_gettime(detail::DEFAULT_CLOCK_ID, &start));
+    CHECK_EQ(0, clock_gettime(CLOCK_REALTIME, &start));
   }
 
   template <class F>
@@ -175,7 +170,7 @@ struct BenchmarkSuspender {
 private:
   void tally() {
     timespec end;
-    CHECK_EQ(0, clock_gettime(detail::DEFAULT_CLOCK_ID, &end));
+    CHECK_EQ(0, clock_gettime(CLOCK_REALTIME, &end));
     nsSpent += detail::timespecDiff(end, start);
     start = end;
   }
@@ -202,9 +197,9 @@ addBenchmark(const char* file, const char* name, Lambda&& lambda) {
     unsigned int niter;
 
     // CORE MEASUREMENT STARTS
-    auto const r1 = clock_gettime(detail::DEFAULT_CLOCK_ID, &start);
+    auto const r1 = clock_gettime(CLOCK_REALTIME, &start);
     niter = lambda(times);
-    auto const r2 = clock_gettime(detail::DEFAULT_CLOCK_ID, &end);
+    auto const r2 = clock_gettime(CLOCK_REALTIME, &end);
     // CORE MEASUREMENT ENDS
 
     CHECK_EQ(0, r1);
@@ -241,32 +236,88 @@ addBenchmark(const char* file, const char* name, Lambda&& lambda) {
 }
 
 /**
- * Call doNotOptimizeAway(var) against variables that you use for
+ * Call doNotOptimizeAway(var) to ensure that var will be computed even
+ * post-optimization.  Use it for variables that are computed during
  * benchmarking but otherwise are useless. The compiler tends to do a
- * good job at eliminating unused variables, and this function fools
- * it into thinking var is in fact needed.
+ * good job at eliminating unused variables, and this function fools it
+ * into thinking var is in fact needed.
+ *
+ * Call makeUnpredictable(var) when you don't want the optimizer to use
+ * its knowledge of var to shape the following code.  This is useful
+ * when constant propagation or power reduction is possible during your
+ * benchmark but not in real use cases.
  */
+
 #ifdef _MSC_VER
 
 #pragma optimize("", off)
 
-template <class T>
-void doNotOptimizeAway(T&& datum) {
-  datum = datum;
-}
+inline void doNotOptimizeDependencySink(const void*) {}
 
 #pragma optimize("", on)
 
-#elif defined(__clang__)
-
 template <class T>
-__attribute__((__optnone__)) void doNotOptimizeAway(T&& /* datum */) {}
+void doNotOptimizeAway(const T& datum) {
+  doNotOptimizeDependencySink(&datum);
+}
+
+template <typename T>
+void makeUnpredictable(T& datum) {
+  doNotOptimizeDependencySink(&datum);
+}
 
 #else
 
-template <class T>
-void doNotOptimizeAway(T&& datum) {
-  asm volatile("" : "+r" (datum));
+namespace detail {
+template <typename T>
+struct DoNotOptimizeAwayNeedsIndirect {
+  using Decayed = typename std::decay<T>::type;
+
+  // First two constraints ensure it can be an "r" operand.
+  // std::is_pointer check is because callers seem to expect that
+  // doNotOptimizeAway(&x) is equivalent to doNotOptimizeAway(x).
+  constexpr static bool value = !folly::IsTriviallyCopyable<Decayed>::value ||
+      sizeof(Decayed) > sizeof(long) || std::is_pointer<Decayed>::value;
+};
+} // detail namespace
+
+template <typename T>
+auto doNotOptimizeAway(const T& datum) -> typename std::enable_if<
+    !detail::DoNotOptimizeAwayNeedsIndirect<T>::value>::type {
+  // The "r" constraint forces the compiler to make datum available
+  // in a register to the asm block, which means that it must have
+  // computed/loaded it.  We use this path for things that are <=
+  // sizeof(long) (they have to fit), trivial (otherwise the compiler
+  // doesn't want to put them in a register), and not a pointer (because
+  // doNotOptimizeAway(&foo) would otherwise be a foot gun that didn't
+  // necessarily compute foo).
+  //
+  // An earlier version of this method had a more permissive input operand
+  // constraint, but that caused unnecessary variation between clang and
+  // gcc benchmarks.
+  asm volatile("" ::"r"(datum));
+}
+
+template <typename T>
+auto doNotOptimizeAway(const T& datum) -> typename std::enable_if<
+    detail::DoNotOptimizeAwayNeedsIndirect<T>::value>::type {
+  // This version of doNotOptimizeAway tells the compiler that the asm
+  // block will read datum from memory, and that in addition it might read
+  // or write from any memory location.  If the memory clobber could be
+  // separated into input and output that would be preferrable.
+  asm volatile("" ::"m"(datum) : "memory");
+}
+
+template <typename T>
+auto makeUnpredictable(T& datum) -> typename std::enable_if<
+    !detail::DoNotOptimizeAwayNeedsIndirect<T>::value>::type {
+  asm volatile("" : "+r"(datum));
+}
+
+template <typename T>
+auto makeUnpredictable(T& datum) -> typename std::enable_if<
+    detail::DoNotOptimizeAwayNeedsIndirect<T>::value>::type {
+  asm volatile("" ::"m"(datum) : "memory");
 }
 
 #endif
@@ -374,7 +425,7 @@ void doNotOptimizeAway(T&& datum) {
   BENCHMARK_NAMED_PARAM(name, param, param)
 
 /**
- * Same as BENCHMARK_PARAM, but allows to return the actual number of
+ * Same as BENCHMARK_PARAM, but allows one to return the actual number of
  * iterations that have been run.
  */
 #define BENCHMARK_PARAM_MULTI(name, param)                              \
@@ -414,7 +465,7 @@ void doNotOptimizeAway(T&& datum) {
   }
 
 /**
- * Same as BENCHMARK_NAMED_PARAM, but allows to return the actual number
+ * Same as BENCHMARK_NAMED_PARAM, but allows one to return the actual number
  * of iterations that have been run.
  */
 #define BENCHMARK_NAMED_PARAM_MULTI(name, param_name, ...)              \
@@ -429,7 +480,7 @@ void doNotOptimizeAway(T&& datum) {
 /**
  * Just like BENCHMARK, but prints the time relative to a
  * baseline. The baseline is the most recent BENCHMARK() seen in
- * lexical order. Example:
+ * the current scope. Example:
  *
  * // This is the baseline
  * BENCHMARK(insertVectorBegin, n) {
@@ -459,7 +510,7 @@ void doNotOptimizeAway(T&& datum) {
     __VA_ARGS__)
 
 /**
- * Same as BENCHMARK_RELATIVE, but allows to return the actual number
+ * Same as BENCHMARK_RELATIVE, but allows one to return the actual number
  * of iterations that have been run.
  */
 #define BENCHMARK_RELATIVE_MULTI(name, ...)                     \
@@ -476,7 +527,7 @@ void doNotOptimizeAway(T&& datum) {
   BENCHMARK_RELATIVE_NAMED_PARAM(name, param, param)
 
 /**
- * Same as BENCHMARK_RELATIVE_PARAM, but allows to return the actual
+ * Same as BENCHMARK_RELATIVE_PARAM, but allows one to return the actual
  * number of iterations that have been run.
  */
 #define BENCHMARK_RELATIVE_PARAM_MULTI(name, param)                     \
@@ -496,7 +547,7 @@ void doNotOptimizeAway(T&& datum) {
   }
 
 /**
- * Same as BENCHMARK_RELATIVE_NAMED_PARAM, but allows to return the
+ * Same as BENCHMARK_RELATIVE_NAMED_PARAM, but allows one to return the
  * actual number of iterations that have been run.
  */
 #define BENCHMARK_RELATIVE_NAMED_PARAM_MULTI(name, param_name, ...)     \
@@ -534,5 +585,3 @@ void doNotOptimizeAway(T&& datum) {
   if (auto FB_ANONYMOUS_VARIABLE(BENCHMARK_SUSPEND) =   \
       ::folly::BenchmarkSuspender()) {}                 \
   else
-
-#endif // FOLLY_BENCHMARK_H_