1// Copyright 2009 The RE2 Authors. All Rights Reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5#include <stdint.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <algorithm>
9#include <chrono>
10
11#include "util/benchmark.h"
12#include "util/flags.h"
13#include "re2/re2.h"
14
15#ifdef _WIN32
16#define snprintf _snprintf
17#endif
18
19using ::testing::Benchmark;
20
21static Benchmark* benchmarks[10000];
22static int nbenchmarks;
23
24void Benchmark::Register() {
25 lo_ = std::max(1, lo_);
26 hi_ = std::max(lo_, hi_);
27 benchmarks[nbenchmarks++] = this;
28}
29
30static int64_t nsec() {
31 return std::chrono::duration_cast<std::chrono::nanoseconds>(
32 std::chrono::steady_clock::now().time_since_epoch())
33 .count();
34}
35
36static int64_t t0;
37static int64_t ns;
38static int64_t bytes;
39static int64_t items;
40
41void StartBenchmarkTiming() {
42 if (t0 == 0) {
43 t0 = nsec();
44 }
45}
46
47void StopBenchmarkTiming() {
48 if (t0 != 0) {
49 ns += nsec() - t0;
50 t0 = 0;
51 }
52}
53
54void SetBenchmarkBytesProcessed(int64_t b) { bytes = b; }
55
56void SetBenchmarkItemsProcessed(int64_t i) { items = i; }
57
58static void RunFunc(Benchmark* b, int iters, int arg) {
59 t0 = nsec();
60 ns = 0;
61 bytes = 0;
62 items = 0;
63 b->func()(iters, arg);
64 StopBenchmarkTiming();
65}
66
67static int round(int n) {
68 int base = 1;
69 while (base * 10 < n) base *= 10;
70 if (n < 2 * base) return 2 * base;
71 if (n < 5 * base) return 5 * base;
72 return 10 * base;
73}
74
75static void RunBench(Benchmark* b, int arg) {
76 int iters, last;
77
78 // Run once just in case it's expensive.
79 iters = 1;
80 RunFunc(b, iters, arg);
81 while (ns < (int)1e9 && iters < (int)1e9) {
82 last = iters;
83 if (ns / iters == 0) {
84 iters = (int)1e9;
85 } else {
86 iters = (int)1e9 / static_cast<int>(ns / iters);
87 }
88 iters = std::max(last + 1, std::min(iters + iters / 2, 100 * last));
89 iters = round(iters);
90 RunFunc(b, iters, arg);
91 }
92
93 char mb[100];
94 char suf[100];
95 mb[0] = '\0';
96 suf[0] = '\0';
97 if (ns > 0 && bytes > 0)
98 snprintf(mb, sizeof mb, "\t%7.2f MB/s",
99 ((double)bytes / 1e6) / ((double)ns / 1e9));
100 if (b->has_arg()) {
101 if (arg >= (1 << 20)) {
102 snprintf(suf, sizeof suf, "/%dM", arg / (1 << 20));
103 } else if (arg >= (1 << 10)) {
104 snprintf(suf, sizeof suf, "/%dK", arg / (1 << 10));
105 } else {
106 snprintf(suf, sizeof suf, "/%d", arg);
107 }
108 }
109 printf("%s%s\t%8d\t%10lld ns/op%s\n", b->name(), suf, iters,
110 (long long)ns / iters, mb);
111 fflush(stdout);
112}
113
114static bool WantBench(const char* name, int argc, const char** argv) {
115 if (argc == 1) return true;
116 for (int i = 1; i < argc; i++) {
117 if (RE2::PartialMatch(name, argv[i]))
118 return true;
119 }
120 return false;
121}
122
123int main(int argc, const char** argv) {
124 for (int i = 0; i < nbenchmarks; i++) {
125 Benchmark* b = benchmarks[i];
126 if (!WantBench(b->name(), argc, argv))
127 continue;
128 for (int arg = b->lo(); arg <= b->hi(); arg <<= 1)
129 RunBench(b, arg);
130 }
131}
132