Use std::vector in the CacheLocalityBenchmark rather than a VLA
[folly.git] / folly / Benchmark.cpp
index 66bcf7c1e76c4b0a9f7bb1277d9febddc2ad897e..1b32e9ef3c479f7b81b6a20f923c801ddd589be5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Facebook, Inc.
+ * Copyright 2016 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -28,6 +28,7 @@
 #include <limits>
 #include <utility>
 #include <vector>
+#include <cstring>
 
 using namespace std;
 
@@ -52,10 +53,18 @@ namespace folly {
 BenchmarkSuspender::NanosecondsSpent BenchmarkSuspender::nsSpent;
 
 typedef function<detail::TimeIterPair(unsigned int)> BenchmarkFun;
-static vector<tuple<const char*, const char*, BenchmarkFun>> benchmarks;
+
+
+vector<tuple<string, string, BenchmarkFun>>& benchmarks() {
+  static vector<tuple<string, string, BenchmarkFun>> _benchmarks;
+  return _benchmarks;
+}
+
+#define FB_FOLLY_GLOBAL_BENCHMARK_BASELINE fbFollyGlobalBenchmarkBaseline
+#define FB_STRINGIZE_X2(x) FB_STRINGIZE(x)
 
 // Add the global baseline
-BENCHMARK(globalBenchmarkBaseline) {
+BENCHMARK(FB_FOLLY_GLOBAL_BENCHMARK_BASELINE) {
 #ifdef _MSC_VER
   _ReadWriteBarrier();
 #else
@@ -63,9 +72,25 @@ BENCHMARK(globalBenchmarkBaseline) {
 #endif
 }
 
+int getGlobalBenchmarkBaselineIndex() {
+  const char *global = FB_STRINGIZE_X2(FB_FOLLY_GLOBAL_BENCHMARK_BASELINE);
+  auto it = std::find_if(
+    benchmarks().begin(),
+    benchmarks().end(),
+    [global](const tuple<string, string, BenchmarkFun> &v) {
+      return get<1>(v) == global;
+    }
+  );
+  CHECK(it != benchmarks().end());
+  return it - benchmarks().begin();
+}
+
+#undef FB_STRINGIZE_X2
+#undef FB_FOLLY_GLOBAL_BENCHMARK_BASELINE
+
 void detail::addBenchmarkImpl(const char* file, const char* name,
                               BenchmarkFun fun) {
-  benchmarks.emplace_back(file, name, std::move(fun));
+  benchmarks().emplace_back(file, name, std::move(fun));
 }
 
 /**
@@ -201,7 +226,7 @@ static double runBenchmarkGetNSPerIteration(const BenchmarkFun& fun,
   // They key here is accuracy; too low numbers means the accuracy was
   // coarse. We up the ante until we get to at least minNanoseconds
   // timings.
-  static uint64_t resolutionInNs = 0, coarseResolutionInNs = 0;
+  static uint64_t resolutionInNs = 0;
   if (!resolutionInNs) {
     timespec ts;
     CHECK_EQ(0, clock_getres(detail::DEFAULT_CLOCK_ID, &ts));
@@ -322,14 +347,14 @@ static string metricReadable(double n, unsigned int decimals) {
 }
 
 static void printBenchmarkResultsAsTable(
-  const vector<tuple<const char*, const char*, double> >& data) {
+  const vector<tuple<string, string, double> >& data) {
   // Width available
   static const unsigned int columns = 76;
 
   // Compute the longest benchmark name
   size_t longestName = 0;
-  FOR_EACH_RANGE (i, 1, benchmarks.size()) {
-    longestName = max(longestName, strlen(get<1>(benchmarks[i])));
+  FOR_EACH_RANGE (i, 1, benchmarks().size()) {
+    longestName = max(longestName, get<1>(benchmarks()[i]).size());
   }
 
   // Print a horizontal rule
@@ -338,19 +363,19 @@ static void printBenchmarkResultsAsTable(
   };
 
   // Print header for a file
-  auto header = [&](const char* file) {
+  auto header = [&](const string& file) {
     separator('=');
     printf("%-*srelative  time/iter  iters/s\n",
-           columns - 28, file);
+           columns - 28, file.c_str());
     separator('=');
   };
 
   double baselineNsPerIter = numeric_limits<double>::max();
-  const char* lastFile = "";
+  string lastFile;
 
   for (auto& datum : data) {
     auto file = get<0>(datum);
-    if (strcmp(file, lastFile)) {
+    if (file != lastFile) {
       // New file starting
       header(file);
       lastFile = file;
@@ -372,7 +397,9 @@ static void printBenchmarkResultsAsTable(
     s.resize(columns - 29, ' ');
     auto nsPerIter = get<2>(datum);
     auto secPerIter = nsPerIter / 1E9;
-    auto itersPerSec = 1 / secPerIter;
+    auto itersPerSec = (secPerIter == 0)
+                           ? std::numeric_limits<double>::infinity()
+                           : (1 / secPerIter);
     if (!useBaseline) {
       // Print without baseline
       printf("%*s           %9s  %7s\n",
@@ -393,7 +420,7 @@ static void printBenchmarkResultsAsTable(
 }
 
 static void printBenchmarkResultsAsJson(
-  const vector<tuple<const char*, const char*, double> >& data) {
+  const vector<tuple<string, string, double> >& data) {
   dynamic d = dynamic::object;
   for (auto& datum: data) {
     d[std::get<1>(datum)] = std::get<2>(datum) * 1000.;
@@ -403,7 +430,7 @@ static void printBenchmarkResultsAsJson(
 }
 
 static void printBenchmarkResults(
-  const vector<tuple<const char*, const char*, double> >& data) {
+  const vector<tuple<string, string, double> >& data) {
 
   if (FLAGS_json) {
     printBenchmarkResultsAsJson(data);
@@ -413,10 +440,10 @@ static void printBenchmarkResults(
 }
 
 void runBenchmarks() {
-  CHECK(!benchmarks.empty());
+  CHECK(!benchmarks().empty());
 
-  vector<tuple<const char*, const char*, double>> results;
-  results.reserve(benchmarks.size() - 1);
+  vector<tuple<string, string, double>> results;
+  results.reserve(benchmarks().size() - 1);
 
   std::unique_ptr<boost::regex> bmRegex;
   if (!FLAGS_bm_regex.empty()) {
@@ -425,19 +452,24 @@ void runBenchmarks() {
 
   // PLEASE KEEP QUIET. MEASUREMENTS IN PROGRESS.
 
-  auto const globalBaseline = runBenchmarkGetNSPerIteration(
-    get<2>(benchmarks.front()), 0);
-  FOR_EACH_RANGE (i, 1, benchmarks.size()) {
+  unsigned int baselineIndex = getGlobalBenchmarkBaselineIndex();
+
+  auto const globalBaseline =
+      runBenchmarkGetNSPerIteration(get<2>(benchmarks()[baselineIndex]), 0);
+  FOR_EACH_RANGE (i, 0, benchmarks().size()) {
+    if (i == baselineIndex) {
+      continue;
+    }
     double elapsed = 0.0;
-    if (strcmp(get<1>(benchmarks[i]), "-") != 0) { // skip separators
-      if (bmRegex && !boost::regex_search(get<1>(benchmarks[i]), *bmRegex)) {
+    if (get<1>(benchmarks()[i]) != "-") { // skip separators
+      if (bmRegex && !boost::regex_search(get<1>(benchmarks()[i]), *bmRegex)) {
         continue;
       }
-      elapsed = runBenchmarkGetNSPerIteration(get<2>(benchmarks[i]),
+      elapsed = runBenchmarkGetNSPerIteration(get<2>(benchmarks()[i]),
                                               globalBaseline);
     }
-    results.emplace_back(get<0>(benchmarks[i]),
-                         get<1>(benchmarks[i]), elapsed);
+    results.emplace_back(get<0>(benchmarks()[i]),
+                         get<1>(benchmarks()[i]), elapsed);
   }
 
   // PLEASE MAKE NOISE. MEASUREMENTS DONE.