1 /*
2 * Copyright (C) 2012-2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <benchmark.h>
18
19 #include <inttypes.h>
20 #include <regex.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23
24 #include <string>
25 #include <map>
26 #include <vector>
27
28 static uint64_t gBytesProcessed;
29 static uint64_t gBenchmarkTotalTimeNs;
30 static uint64_t gBenchmarkTotalTimeNsSquared;
31 static uint64_t gBenchmarkNum;
32 static uint64_t gBenchmarkStartTimeNs;
33
34 typedef std::vector< ::testing::Benchmark* > BenchmarkList;
35 static BenchmarkList* gBenchmarks;
36
Round(int n)37 static int Round(int n) {
38 int base = 1;
39 while (base*10 < n) {
40 base *= 10;
41 }
42 if (n < 2*base) {
43 return 2*base;
44 }
45 if (n < 5*base) {
46 return 5*base;
47 }
48 return 10*base;
49 }
50
NanoTime()51 static uint64_t NanoTime() {
52 struct timespec t;
53 t.tv_sec = t.tv_nsec = 0;
54 clock_gettime(CLOCK_MONOTONIC, &t);
55 return static_cast<uint64_t>(t.tv_sec) * 1000000000ULL + t.tv_nsec;
56 }
57
58 namespace testing {
59
PrettyPrintInt(char * str,int len,unsigned int arg)60 int PrettyPrintInt(char* str, int len, unsigned int arg)
61 {
62 if (arg >= (1<<30) && arg % (1<<30) == 0) {
63 return snprintf(str, len, "%uGi", arg/(1<<30));
64 } else if (arg >= (1<<20) && arg % (1<<20) == 0) {
65 return snprintf(str, len, "%uMi", arg/(1<<20));
66 } else if (arg >= (1<<10) && arg % (1<<10) == 0) {
67 return snprintf(str, len, "%uKi", arg/(1<<10));
68 } else if (arg >= 1000000000 && arg % 1000000000 == 0) {
69 return snprintf(str, len, "%uG", arg/1000000000);
70 } else if (arg >= 1000000 && arg % 1000000 == 0) {
71 return snprintf(str, len, "%uM", arg/1000000);
72 } else if (arg >= 1000 && arg % 1000 == 0) {
73 return snprintf(str, len, "%uK", arg/1000);
74 } else {
75 return snprintf(str, len, "%u", arg);
76 }
77 }
78
ShouldRun(Benchmark * b,int argc,char * argv[])79 bool ShouldRun(Benchmark* b, int argc, char* argv[]) {
80 if (argc == 1) {
81 return true; // With no arguments, we run all benchmarks.
82 }
83 // Otherwise, we interpret each argument as a regular expression and
84 // see if any of our benchmarks match.
85 for (int i = 1; i < argc; i++) {
86 regex_t re;
87 if (regcomp(&re, argv[i], 0) != 0) {
88 fprintf(stderr, "couldn't compile \"%s\" as a regular expression!\n", argv[i]);
89 exit(EXIT_FAILURE);
90 }
91 int match = regexec(&re, b->Name(), 0, NULL, 0);
92 regfree(&re);
93 if (match != REG_NOMATCH) {
94 return true;
95 }
96 }
97 return false;
98 }
99
BenchmarkRegister(Benchmark * b)100 void BenchmarkRegister(Benchmark* b) {
101 if (gBenchmarks == NULL) {
102 gBenchmarks = new BenchmarkList;
103 }
104 gBenchmarks->push_back(b);
105 }
106
RunRepeatedly(Benchmark * b,int iterations)107 void RunRepeatedly(Benchmark* b, int iterations) {
108 gBytesProcessed = 0;
109 ResetBenchmarkTiming();
110 uint64_t StartTimeNs = NanoTime();
111 b->RunFn(iterations);
112 // Catch us if we fail to log anything.
113 if ((gBenchmarkTotalTimeNs == 0)
114 && (StartTimeNs != 0)
115 && (gBenchmarkStartTimeNs == 0)) {
116 gBenchmarkTotalTimeNs = NanoTime() - StartTimeNs;
117 }
118 }
119
Run(Benchmark * b)120 void Run(Benchmark* b) {
121 // run once in case it's expensive
122 unsigned iterations = 1;
123 uint64_t s = NanoTime();
124 RunRepeatedly(b, iterations);
125 s = NanoTime() - s;
126 while (s < 2e9 && gBenchmarkTotalTimeNs < 1e9 && iterations < 1e9) {
127 unsigned last = iterations;
128 if (gBenchmarkTotalTimeNs/iterations == 0) {
129 iterations = 1e9;
130 } else {
131 iterations = 1e9 / (gBenchmarkTotalTimeNs/iterations);
132 }
133 iterations = std::max(last + 1, std::min(iterations + iterations/2, 100*last));
134 iterations = Round(iterations);
135 s = NanoTime();
136 RunRepeatedly(b, iterations);
137 s = NanoTime() - s;
138 }
139
140 char throughput[100];
141 throughput[0] = '\0';
142 if (gBenchmarkTotalTimeNs > 0 && gBytesProcessed > 0) {
143 double mib_processed = static_cast<double>(gBytesProcessed)/1e6;
144 double seconds = static_cast<double>(gBenchmarkTotalTimeNs)/1e9;
145 snprintf(throughput, sizeof(throughput), " %8.2f MiB/s", mib_processed/seconds);
146 }
147
148 char full_name[100];
149 snprintf(full_name, sizeof(full_name), "%s%s%s", b->Name(),
150 b->ArgName() ? "/" : "",
151 b->ArgName() ? b->ArgName() : "");
152
153 uint64_t mean = gBenchmarkTotalTimeNs / iterations;
154 uint64_t sdev = 0;
155 if (gBenchmarkNum == iterations) {
156 mean = gBenchmarkTotalTimeNs / gBenchmarkNum;
157 uint64_t nXvariance = gBenchmarkTotalTimeNsSquared * gBenchmarkNum
158 - (gBenchmarkTotalTimeNs * gBenchmarkTotalTimeNs);
159 sdev = (sqrt((double)nXvariance) / gBenchmarkNum / gBenchmarkNum) + 0.5;
160 }
161 if (mean > (10000 * sdev)) {
162 printf("%-25s %10" PRIu64 " %10" PRIu64 "%s\n", full_name,
163 static_cast<uint64_t>(iterations), mean, throughput);
164 } else {
165 printf("%-25s %10" PRIu64 " %10" PRIu64 "(\317\203%" PRIu64 ")%s\n", full_name,
166 static_cast<uint64_t>(iterations), mean, sdev, throughput);
167 }
168 fflush(stdout);
169 }
170
171 } // namespace testing
172
SetBenchmarkBytesProcessed(uint64_t x)173 void SetBenchmarkBytesProcessed(uint64_t x) {
174 gBytesProcessed = x;
175 }
176
ResetBenchmarkTiming()177 void ResetBenchmarkTiming() {
178 gBenchmarkStartTimeNs = 0;
179 gBenchmarkTotalTimeNs = 0;
180 gBenchmarkTotalTimeNsSquared = 0;
181 gBenchmarkNum = 0;
182 }
183
StopBenchmarkTiming(void)184 void StopBenchmarkTiming(void) {
185 if (gBenchmarkStartTimeNs != 0) {
186 int64_t diff = NanoTime() - gBenchmarkStartTimeNs;
187 gBenchmarkTotalTimeNs += diff;
188 gBenchmarkTotalTimeNsSquared += diff * diff;
189 ++gBenchmarkNum;
190 }
191 gBenchmarkStartTimeNs = 0;
192 }
193
StartBenchmarkTiming(void)194 void StartBenchmarkTiming(void) {
195 if (gBenchmarkStartTimeNs == 0) {
196 gBenchmarkStartTimeNs = NanoTime();
197 }
198 }
199
StopBenchmarkTiming(uint64_t NanoTime)200 void StopBenchmarkTiming(uint64_t NanoTime) {
201 if (gBenchmarkStartTimeNs != 0) {
202 int64_t diff = NanoTime - gBenchmarkStartTimeNs;
203 gBenchmarkTotalTimeNs += diff;
204 gBenchmarkTotalTimeNsSquared += diff * diff;
205 if (NanoTime != 0) {
206 ++gBenchmarkNum;
207 }
208 }
209 gBenchmarkStartTimeNs = 0;
210 }
211
StartBenchmarkTiming(uint64_t NanoTime)212 void StartBenchmarkTiming(uint64_t NanoTime) {
213 if (gBenchmarkStartTimeNs == 0) {
214 gBenchmarkStartTimeNs = NanoTime;
215 }
216 }
217
main(int argc,char * argv[])218 int main(int argc, char* argv[]) {
219 if (gBenchmarks->empty()) {
220 fprintf(stderr, "No benchmarks registered!\n");
221 exit(EXIT_FAILURE);
222 }
223
224 bool need_header = true;
225 for (auto b : *gBenchmarks) {
226 if (ShouldRun(b, argc, argv)) {
227 if (need_header) {
228 printf("%-25s %10s %10s\n", "", "iterations", "ns/op");
229 fflush(stdout);
230 need_header = false;
231 }
232 Run(b);
233 }
234 }
235
236 if (need_header) {
237 fprintf(stderr, "No matching benchmarks!\n");
238 fprintf(stderr, "Available benchmarks:\n");
239 for (auto b : *gBenchmarks) {
240 fprintf(stderr, " %s\n", b->Name());
241 }
242 exit(EXIT_FAILURE);
243 }
244
245 return 0;
246 }
247