Tidy up the ersatz benchmark library.

Change-Id: I3302c44127ab681421c14dd79afd9b85958e7467
Reviewed-on: https://code-review.googlesource.com/c/re2/+/46650
Reviewed-by: Paul Wankadia <junyer@google.com>
diff --git a/util/benchmark.cc b/util/benchmark.cc
index 144f550..d3e1b40 100644
--- a/util/benchmark.cc
+++ b/util/benchmark.cc
@@ -7,11 +7,10 @@
 #include <stdlib.h>
 #include <algorithm>
 #include <chrono>
-#include <thread>
 
-#include "util/util.h"
-#include "util/flags.h"
 #include "util/benchmark.h"
+#include "util/flags.h"
+#include "util/util.h"
 #include "re2/re2.h"
 
 DEFINE_string(test_tmpdir, "/var/tmp", "temp directory");
@@ -20,142 +19,125 @@
 #define snprintf _snprintf
 #endif
 
-using testing::Benchmark;
+using ::testing::Benchmark;
 
 static Benchmark* benchmarks[10000];
 static int nbenchmarks;
 
 void Benchmark::Register() {
-	benchmarks[nbenchmarks] = this;
-	if(lo < 1)
-		lo = 1;
-	if(hi < lo)
-		hi = lo;
-	nbenchmarks++;
+  lo_ = std::max(1, lo_);
+  hi_ = std::max(lo_, hi_);
+  benchmarks[nbenchmarks++] = this;
 }
 
 static int64_t nsec() {
-	return std::chrono::duration_cast<std::chrono::nanoseconds>(
-		std::chrono::steady_clock::now().time_since_epoch()).count();
+  return std::chrono::duration_cast<std::chrono::nanoseconds>(
+             std::chrono::steady_clock::now().time_since_epoch())
+      .count();
 }
 
-static int64_t bytes;
-static int64_t ns;
 static int64_t t0;
+static int64_t ns;
+static int64_t bytes;
 static int64_t items;
 
-void SetBenchmarkBytesProcessed(int64_t x) {
-	bytes = x;
-}
-
-void StopBenchmarkTiming() {
-	if(t0 != 0)
-		ns += nsec() - t0;
-	t0 = 0;
+void BenchmarkMemoryUsage() {
+  // Pretend to support benchmarking memory usage.
 }
 
 void StartBenchmarkTiming() {
-	if(t0 == 0)
-		t0 = nsec();
+  if (t0 == 0) {
+    t0 = nsec();
+  }
 }
 
-void SetBenchmarkItemsProcessed(int n) {
-	items = n;
+void StopBenchmarkTiming() {
+  if (t0 != 0) {
+    ns += nsec() - t0;
+    t0 = 0;
+  }
 }
 
-void BenchmarkMemoryUsage() {
-	// TODO(rsc): Implement.
-}
+void SetBenchmarkBytesProcessed(int64_t b) { bytes = b; }
+
+void SetBenchmarkItemsProcessed(int i) { items = i; }
 
 int NumCPUs() {
-	return static_cast<int>(std::thread::hardware_concurrency());
+  // Pretend to support multi-threaded benchmarking.
+  return static_cast<int>(std::thread::hardware_concurrency());
 }
 
-static void runN(Benchmark *b, int n, int siz) {
-	bytes = 0;
-	items = 0;
-	ns = 0;
-	t0 = nsec();
-	if(b->fn)
-		b->fn(n);
-	else if(b->fnr)
-		b->fnr(n, siz);
-	else {
-		fprintf(stderr, "%s: missing function\n", b->name);
-		abort();
-	}
-	if(t0 != 0)
-		ns += nsec() - t0;
+static void RunFunc(Benchmark* b, int iters, int arg) {
+  t0 = nsec();
+  ns = 0;
+  bytes = 0;
+  items = 0;
+  b->func()(iters, arg);
+  StopBenchmarkTiming();
 }
 
 static int round(int n) {
-	int base = 1;
-	
-	while(base*10 < n)
-		base *= 10;
-	if(n < 2*base)
-		return 2*base;
-	if(n < 5*base)
-		return 5*base;
-	return 10*base;
+  int base = 1;
+  while (base * 10 < n) base *= 10;
+  if (n < 2 * base) return 2 * base;
+  if (n < 5 * base) return 5 * base;
+  return 10 * base;
 }
 
-void RunBench(Benchmark* b, int nthread, int siz) {
-	int n, last;
+static void RunBench(Benchmark* b, int arg) {
+  int iters, last;
 
-	// TODO(rsc): Threaded benchmarks.
-	if(nthread != 1)
-		return;
-	
-	// run once in case it's expensive
-	n = 1;
-	runN(b, n, siz);
-	while(ns < (int)1e9 && n < (int)1e9) {
-		last = n;
-		if(ns/n == 0)
-			n = (int)1e9;
-		else
-			n = (int)1e9 / static_cast<int>(ns/n);
-		
-		n = std::max(last+1, std::min(n+n/2, 100*last));
-		n = round(n);
-		runN(b, n, siz);
-	}
-	
-	char mb[100];
-	char suf[100];
-	mb[0] = '\0';
-	suf[0] = '\0';
-	if(ns > 0 && bytes > 0)
-		snprintf(mb, sizeof mb, "\t%7.2f MB/s", ((double)bytes/1e6)/((double)ns/1e9));
-	if(b->fnr || b->lo != b->hi) {
-		if(siz >= (1<<20))
-			snprintf(suf, sizeof suf, "/%dM", siz/(1<<20));
-		else if(siz >= (1<<10))
-			snprintf(suf, sizeof suf, "/%dK", siz/(1<<10));
-		else
-			snprintf(suf, sizeof suf, "/%d", siz);
-	}
-	printf("%s%s\t%8lld\t%10lld ns/op%s\n", b->name, suf, (long long)n, (long long)ns/n, mb);
-	fflush(stdout);
+  // Run once just in case it's expensive.
+  iters = 1;
+  RunFunc(b, iters, arg);
+  while (ns < (int)1e9 && iters < (int)1e9) {
+    last = iters;
+    if (ns / iters == 0) {
+      iters = (int)1e9;
+    } else {
+      iters = (int)1e9 / static_cast<int>(ns / iters);
+    }
+    iters = std::max(last + 1, std::min(iters + iters / 2, 100 * last));
+    iters = round(iters);
+    RunFunc(b, iters, arg);
+  }
+
+  char mb[100];
+  char suf[100];
+  mb[0] = '\0';
+  suf[0] = '\0';
+  if (ns > 0 && bytes > 0)
+    snprintf(mb, sizeof mb, "\t%7.2f MB/s",
+             ((double)bytes / 1e6) / ((double)ns / 1e9));
+  if (b->arg()) {
+    if (arg >= (1 << 20)) {
+      snprintf(suf, sizeof suf, "/%dM", arg / (1 << 20));
+    } else if (arg >= (1 << 10)) {
+      snprintf(suf, sizeof suf, "/%dK", arg / (1 << 10));
+    } else {
+      snprintf(suf, sizeof suf, "/%d", arg);
+    }
+  }
+  printf("%s%s\t%8d\t%10lld ns/op%s\n", b->name(), suf, iters,
+         (long long)ns / iters, mb);
+  fflush(stdout);
 }
 
-static int match(const char* name, int argc, const char** argv) {
-	if(argc == 1)
-		return 1;
-	for(int i = 1; i < argc; i++)
-		if(RE2::PartialMatch(name, argv[i]))
-			return 1;
-	return 0;
+static bool WantBench(const char* name, int argc, const char** argv) {
+  if (argc == 1) return true;
+  for (int i = 1; i < argc; i++) {
+    if (RE2::PartialMatch(name, argv[i]))
+      return true;
+  }
+  return false;
 }
 
 int main(int argc, const char** argv) {
-	for(int i = 0; i < nbenchmarks; i++) {
-		Benchmark* b = benchmarks[i];
-		if(match(b->name, argc, argv))
-			for(int j = b->threadlo; j <= b->threadhi; j++)
-				for(int k = std::max(b->lo, 1); k <= std::max(b->hi, 1); k<<=1)
-					RunBench(b, j, k);
-	}
+  for (int i = 0; i < nbenchmarks; i++) {
+    Benchmark* b = benchmarks[i];
+    if (!WantBench(b->name(), argc, argv))
+      continue;
+    for (int arg = b->lo(); arg <= b->hi(); arg <<= 1)
+      RunBench(b, arg);
+  }
 }
-
diff --git a/util/benchmark.h b/util/benchmark.h
index fba30b9..8dd36e5 100644
--- a/util/benchmark.h
+++ b/util/benchmark.h
@@ -6,38 +6,71 @@
 #define UTIL_BENCHMARK_H_
 
 #include <stdint.h>
+#include <functional>
+#include <thread>
 
 namespace testing {
-struct Benchmark {
-  const char* name;
-  void (*fn)(int);
-  void (*fnr)(int, int);
-  int lo;
-  int hi;
-  int threadlo;
-  int threadhi;
 
+class Benchmark {
+ public:
+  Benchmark(const char* name, void (*func)(int))
+      : name_(name),
+        func_([func](int iters, int arg) { func(iters); }),
+        lo_(0),
+        hi_(0),
+        arg_(false) {
+    Register();
+  }
+
+  Benchmark(const char* name, void (*func)(int, int), int lo, int hi)
+      : name_(name),
+        func_([func](int iters, int arg) { func(iters, arg); }),
+        lo_(lo),
+        hi_(hi),
+        arg_(true) {
+    Register();
+  }
+
+  Benchmark* ThreadRange(int lo, int hi) {
+    // Pretend to support multi-threaded benchmarking.
+    return this;
+  }
+
+  const char* name() const { return name_; }
+  const std::function<void(int, int)>& func() const { return func_; }
+  int lo() const { return lo_; }
+  int hi() const { return hi_; }
+  bool arg() const { return arg_; }
+
+ private:
   void Register();
-  Benchmark(const char* name, void (*f)(int)) { Clear(name); fn = f; Register(); }
-  Benchmark(const char* name, void (*f)(int, int), int l, int h) { Clear(name); fnr = f; lo = l; hi = h; Register(); }
-  void Clear(const char* n) { name = n; fn = 0; fnr = 0; lo = 0; hi = 0; threadlo = 0; threadhi = 0; }
-  Benchmark* ThreadRange(int lo, int hi) { threadlo = lo; threadhi = hi; return this; }
+
+  const char* name_;
+  std::function<void(int, int)> func_;
+  int lo_;
+  int hi_;
+  bool arg_;
+
+  Benchmark(const Benchmark&) = delete;
+  Benchmark& operator=(const Benchmark&) = delete;
 };
+
 }  // namespace testing
 
-void SetBenchmarkBytesProcessed(int64_t);
-void StopBenchmarkTiming();
-void StartBenchmarkTiming();
 void BenchmarkMemoryUsage();
-void SetBenchmarkItemsProcessed(int);
+void StartBenchmarkTiming();
+void StopBenchmarkTiming();
+void SetBenchmarkBytesProcessed(int64_t b);
+void SetBenchmarkItemsProcessed(int i);
 
 int NumCPUs();
 
-#define BENCHMARK(f) \
-	::testing::Benchmark* _benchmark_##f = (new ::testing::Benchmark(#f, f))
+#define BENCHMARK(f)                     \
+  ::testing::Benchmark* _benchmark_##f = \
+      (new ::testing::Benchmark(#f, f))
 
-#define BENCHMARK_RANGE(f, lo, hi) \
-	::testing::Benchmark* _benchmark_##f = \
-	(new ::testing::Benchmark(#f, f, lo, hi))
+#define BENCHMARK_RANGE(f, lo, hi)       \
+  ::testing::Benchmark* _benchmark_##f = \
+      (new ::testing::Benchmark(#f, f, lo, hi))
 
 #endif  // UTIL_BENCHMARK_H_