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_