blob: e15a688315b3a2753663ab1cc98cfcea74976e82 [file] [log] [blame]
Elliott Hughes7be369d2012-11-08 15:37:43 -08001/*
2 * Copyright (C) 2012 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 <regex.h>
20#include <stdio.h>
21#include <stdlib.h>
22
23#include <string>
24#include <map>
25
26static int64_t gBytesProcessed;
27static int64_t gBenchmarkTotalTimeNs;
28static int64_t gBenchmarkStartTimeNs;
29
30typedef std::map<std::string, ::testing::Benchmark*> BenchmarkMap;
31typedef BenchmarkMap::iterator BenchmarkMapIt;
32static BenchmarkMap gBenchmarks;
33
34static int Round(int n) {
35 int base = 1;
36 while (base*10 < n) {
37 base *= 10;
38 }
39 if (n < 2*base) {
40 return 2*base;
41 }
42 if (n < 5*base) {
43 return 5*base;
44 }
45 return 10*base;
46}
47
48static int64_t NanoTime() {
49 struct timespec t;
50 t.tv_sec = t.tv_nsec = 0;
51 clock_gettime(CLOCK_MONOTONIC, &t);
52 return static_cast<int64_t>(t.tv_sec) * 1000000000LL + t.tv_nsec;
53}
54
55namespace testing {
56
57Benchmark* Benchmark::Arg(int arg) {
58 args_.push_back(arg);
59 return this;
60}
61
Elliott Hughes9edb3e02013-02-06 15:47:09 -080062const char* Benchmark::Name() {
63 return name_;
64}
65
Elliott Hughes7be369d2012-11-08 15:37:43 -080066bool Benchmark::ShouldRun(int argc, char* argv[]) {
67 if (argc == 1) {
68 return true; // With no arguments, we run all benchmarks.
69 }
70 // Otherwise, we interpret each argument as a regular expression and
71 // see if any of our benchmarks match.
72 for (int i = 1; i < argc; i++) {
73 regex_t re;
74 if (regcomp(&re, argv[i], 0) != 0) {
75 fprintf(stderr, "couldn't compile \"%s\" as a regular expression!\n", argv[i]);
76 exit(EXIT_FAILURE);
77 }
78 int match = regexec(&re, name_, 0, NULL, 0);
79 regfree(&re);
80 if (match != REG_NOMATCH) {
81 return true;
82 }
83 }
84 return false;
85}
86
87void Benchmark::Register(const char* name, void (*fn)(int), void (*fn_range)(int, int)) {
88 name_ = name;
89 fn_ = fn;
90 fn_range_ = fn_range;
91
92 if (fn_ == NULL && fn_range_ == NULL) {
93 fprintf(stderr, "%s: missing function\n", name_);
94 exit(EXIT_FAILURE);
95 }
96
97 gBenchmarks.insert(std::make_pair(name, this));
98}
99
100void Benchmark::Run() {
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800101 if (fn_ != NULL) {
102 RunWithArg(0);
103 } else {
104 if (args_.empty()) {
105 fprintf(stderr, "%s: no args!\n", name_);
106 exit(EXIT_FAILURE);
107 }
108 for (size_t i = 0; i < args_.size(); ++i) {
109 RunWithArg(args_[i]);
110 }
Elliott Hughes7be369d2012-11-08 15:37:43 -0800111 }
112}
113
114void Benchmark::RunRepeatedlyWithArg(int iterations, int arg) {
115 gBytesProcessed = 0;
116 gBenchmarkTotalTimeNs = 0;
117 gBenchmarkStartTimeNs = NanoTime();
118 if (fn_ != NULL) {
119 fn_(iterations);
120 } else {
121 fn_range_(iterations, arg);
122 }
123 if (gBenchmarkStartTimeNs != 0) {
124 gBenchmarkTotalTimeNs += NanoTime() - gBenchmarkStartTimeNs;
125 }
126}
127
128void Benchmark::RunWithArg(int arg) {
129 // run once in case it's expensive
130 int iterations = 1;
131 RunRepeatedlyWithArg(iterations, arg);
132 while (gBenchmarkTotalTimeNs < 1e9 && iterations < 1e9) {
133 int last = iterations;
134 if (gBenchmarkTotalTimeNs/iterations == 0) {
135 iterations = 1e9;
136 } else {
137 iterations = 1e9 / (gBenchmarkTotalTimeNs/iterations);
138 }
139 iterations = std::max(last + 1, std::min(iterations + iterations/2, 100*last));
140 iterations = Round(iterations);
141 RunRepeatedlyWithArg(iterations, arg);
142 }
143
144 char throughput[100];
145 throughput[0] = '\0';
146 if (gBenchmarkTotalTimeNs > 0 && gBytesProcessed > 0) {
147 double mib_processed = static_cast<double>(gBytesProcessed)/1e6;
148 double seconds = static_cast<double>(gBenchmarkTotalTimeNs)/1e9;
149 snprintf(throughput, sizeof(throughput), " %8.2f MiB/s", mib_processed/seconds);
150 }
151
152 char full_name[100];
153 if (fn_range_ != NULL) {
154 if (arg >= (1<<20)) {
155 snprintf(full_name, sizeof(full_name), "%s/%dM", name_, arg/(1<<20));
156 } else if (arg >= (1<<10)) {
157 snprintf(full_name, sizeof(full_name), "%s/%dK", name_, arg/(1<<10));
158 } else {
159 snprintf(full_name, sizeof(full_name), "%s/%d", name_, arg);
160 }
161 } else {
162 snprintf(full_name, sizeof(full_name), "%s", name_);
163 }
164
165 printf("%-20s %10lld %10lld%s\n", full_name,
166 static_cast<int64_t>(iterations), gBenchmarkTotalTimeNs/iterations, throughput);
167 fflush(stdout);
168}
169
170} // namespace testing
171
172void SetBenchmarkBytesProcessed(int64_t x) {
173 gBytesProcessed = x;
174}
175
176void StopBenchmarkTiming() {
177 if (gBenchmarkStartTimeNs != 0) {
178 gBenchmarkTotalTimeNs += NanoTime() - gBenchmarkStartTimeNs;
179 }
180 gBenchmarkStartTimeNs = 0;
181}
182
183void StartBenchmarkTiming() {
184 if (gBenchmarkStartTimeNs == 0) {
185 gBenchmarkStartTimeNs = NanoTime();
186 }
187}
188
189int main(int argc, char* argv[]) {
190 if (gBenchmarks.empty()) {
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800191 fprintf(stderr, "No benchmarks registered!\n");
Elliott Hughes7be369d2012-11-08 15:37:43 -0800192 exit(EXIT_FAILURE);
193 }
194
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800195 bool need_header = true;
Elliott Hughes7be369d2012-11-08 15:37:43 -0800196 for (BenchmarkMapIt it = gBenchmarks.begin(); it != gBenchmarks.end(); ++it) {
197 ::testing::Benchmark* b = it->second;
198 if (b->ShouldRun(argc, argv)) {
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800199 if (need_header) {
200 printf("%-20s %10s %10s\n", "", "iterations", "ns/op");
201 fflush(stdout);
202 need_header = false;
203 }
Elliott Hughes7be369d2012-11-08 15:37:43 -0800204 b->Run();
205 }
206 }
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800207
208 if (need_header) {
209 fprintf(stderr, "No matching benchmarks!\n");
210 fprintf(stderr, "Available benchmarks:\n");
211 for (BenchmarkMapIt it = gBenchmarks.begin(); it != gBenchmarks.end(); ++it) {
212 fprintf(stderr, " %s\n", it->second->Name());
213 }
214 exit(EXIT_FAILURE);
215 }
216
Elliott Hughes7be369d2012-11-08 15:37:43 -0800217 return 0;
218}