blob: cd8096888f4519380bbe338545005a279ab5fff1 [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
Serban Constantinescu282e2322013-10-22 11:30:12 +010026#include <inttypes.h>
27
Elliott Hughes1728b232014-05-14 10:02:03 -070028static int64_t g_bytes_processed;
29static int64_t g_benchmark_total_time_ns;
30static int64_t g_benchmark_start_time_ns;
Elliott Hughes7be369d2012-11-08 15:37:43 -080031
32typedef std::map<std::string, ::testing::Benchmark*> BenchmarkMap;
33typedef BenchmarkMap::iterator BenchmarkMapIt;
Elliott Hughes1728b232014-05-14 10:02:03 -070034static BenchmarkMap g_benchmarks;
Elliott Hughesc0eed722014-06-11 16:48:29 -070035static size_t g_name_column_width = 20;
Elliott Hughes7be369d2012-11-08 15:37:43 -080036
37static 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
51static int64_t NanoTime() {
52 struct timespec t;
53 t.tv_sec = t.tv_nsec = 0;
54 clock_gettime(CLOCK_MONOTONIC, &t);
55 return static_cast<int64_t>(t.tv_sec) * 1000000000LL + t.tv_nsec;
56}
57
58namespace testing {
59
60Benchmark* Benchmark::Arg(int arg) {
61 args_.push_back(arg);
62 return this;
63}
64
Elliott Hughes9edb3e02013-02-06 15:47:09 -080065const char* Benchmark::Name() {
66 return name_;
67}
68
Elliott Hughes7be369d2012-11-08 15:37:43 -080069bool Benchmark::ShouldRun(int argc, char* argv[]) {
70 if (argc == 1) {
71 return true; // With no arguments, we run all benchmarks.
72 }
73 // Otherwise, we interpret each argument as a regular expression and
74 // see if any of our benchmarks match.
75 for (int i = 1; i < argc; i++) {
76 regex_t re;
77 if (regcomp(&re, argv[i], 0) != 0) {
78 fprintf(stderr, "couldn't compile \"%s\" as a regular expression!\n", argv[i]);
79 exit(EXIT_FAILURE);
80 }
81 int match = regexec(&re, name_, 0, NULL, 0);
82 regfree(&re);
83 if (match != REG_NOMATCH) {
84 return true;
85 }
86 }
87 return false;
88}
89
90void Benchmark::Register(const char* name, void (*fn)(int), void (*fn_range)(int, int)) {
91 name_ = name;
92 fn_ = fn;
93 fn_range_ = fn_range;
94
95 if (fn_ == NULL && fn_range_ == NULL) {
96 fprintf(stderr, "%s: missing function\n", name_);
97 exit(EXIT_FAILURE);
98 }
99
Elliott Hughes1728b232014-05-14 10:02:03 -0700100 g_benchmarks.insert(std::make_pair(name, this));
Elliott Hughes7be369d2012-11-08 15:37:43 -0800101}
102
103void Benchmark::Run() {
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800104 if (fn_ != NULL) {
105 RunWithArg(0);
106 } else {
107 if (args_.empty()) {
108 fprintf(stderr, "%s: no args!\n", name_);
109 exit(EXIT_FAILURE);
110 }
111 for (size_t i = 0; i < args_.size(); ++i) {
112 RunWithArg(args_[i]);
113 }
Elliott Hughes7be369d2012-11-08 15:37:43 -0800114 }
115}
116
117void Benchmark::RunRepeatedlyWithArg(int iterations, int arg) {
Elliott Hughes1728b232014-05-14 10:02:03 -0700118 g_bytes_processed = 0;
119 g_benchmark_total_time_ns = 0;
120 g_benchmark_start_time_ns = NanoTime();
Elliott Hughes7be369d2012-11-08 15:37:43 -0800121 if (fn_ != NULL) {
122 fn_(iterations);
123 } else {
124 fn_range_(iterations, arg);
125 }
Elliott Hughes1728b232014-05-14 10:02:03 -0700126 if (g_benchmark_start_time_ns != 0) {
127 g_benchmark_total_time_ns += NanoTime() - g_benchmark_start_time_ns;
Elliott Hughes7be369d2012-11-08 15:37:43 -0800128 }
129}
130
131void Benchmark::RunWithArg(int arg) {
132 // run once in case it's expensive
133 int iterations = 1;
134 RunRepeatedlyWithArg(iterations, arg);
Elliott Hughes1728b232014-05-14 10:02:03 -0700135 while (g_benchmark_total_time_ns < 1e9 && iterations < 1e9) {
Elliott Hughes7be369d2012-11-08 15:37:43 -0800136 int last = iterations;
Elliott Hughes1728b232014-05-14 10:02:03 -0700137 if (g_benchmark_total_time_ns/iterations == 0) {
Elliott Hughes7be369d2012-11-08 15:37:43 -0800138 iterations = 1e9;
139 } else {
Elliott Hughes1728b232014-05-14 10:02:03 -0700140 iterations = 1e9 / (g_benchmark_total_time_ns/iterations);
Elliott Hughes7be369d2012-11-08 15:37:43 -0800141 }
142 iterations = std::max(last + 1, std::min(iterations + iterations/2, 100*last));
143 iterations = Round(iterations);
144 RunRepeatedlyWithArg(iterations, arg);
145 }
146
147 char throughput[100];
148 throughput[0] = '\0';
Elliott Hughes1728b232014-05-14 10:02:03 -0700149 if (g_benchmark_total_time_ns > 0 && g_bytes_processed > 0) {
150 double mib_processed = static_cast<double>(g_bytes_processed)/1e6;
151 double seconds = static_cast<double>(g_benchmark_total_time_ns)/1e9;
Elliott Hughes7be369d2012-11-08 15:37:43 -0800152 snprintf(throughput, sizeof(throughput), " %8.2f MiB/s", mib_processed/seconds);
153 }
154
155 char full_name[100];
156 if (fn_range_ != NULL) {
157 if (arg >= (1<<20)) {
158 snprintf(full_name, sizeof(full_name), "%s/%dM", name_, arg/(1<<20));
159 } else if (arg >= (1<<10)) {
160 snprintf(full_name, sizeof(full_name), "%s/%dK", name_, arg/(1<<10));
161 } else {
162 snprintf(full_name, sizeof(full_name), "%s/%d", name_, arg);
163 }
164 } else {
165 snprintf(full_name, sizeof(full_name), "%s", name_);
166 }
167
Elliott Hughesc0eed722014-06-11 16:48:29 -0700168 printf("%-*s %10d %10" PRId64 "%s\n", g_name_column_width, full_name,
Elliott Hughes1728b232014-05-14 10:02:03 -0700169 iterations, g_benchmark_total_time_ns/iterations, throughput);
Elliott Hughes7be369d2012-11-08 15:37:43 -0800170 fflush(stdout);
171}
172
173} // namespace testing
174
175void SetBenchmarkBytesProcessed(int64_t x) {
Elliott Hughes1728b232014-05-14 10:02:03 -0700176 g_bytes_processed = x;
Elliott Hughes7be369d2012-11-08 15:37:43 -0800177}
178
179void StopBenchmarkTiming() {
Elliott Hughes1728b232014-05-14 10:02:03 -0700180 if (g_benchmark_start_time_ns != 0) {
181 g_benchmark_total_time_ns += NanoTime() - g_benchmark_start_time_ns;
Elliott Hughes7be369d2012-11-08 15:37:43 -0800182 }
Elliott Hughes1728b232014-05-14 10:02:03 -0700183 g_benchmark_start_time_ns = 0;
Elliott Hughes7be369d2012-11-08 15:37:43 -0800184}
185
186void StartBenchmarkTiming() {
Elliott Hughes1728b232014-05-14 10:02:03 -0700187 if (g_benchmark_start_time_ns == 0) {
188 g_benchmark_start_time_ns = NanoTime();
Elliott Hughes7be369d2012-11-08 15:37:43 -0800189 }
190}
191
192int main(int argc, char* argv[]) {
Elliott Hughes1728b232014-05-14 10:02:03 -0700193 if (g_benchmarks.empty()) {
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800194 fprintf(stderr, "No benchmarks registered!\n");
Elliott Hughes7be369d2012-11-08 15:37:43 -0800195 exit(EXIT_FAILURE);
196 }
197
Elliott Hughesc0eed722014-06-11 16:48:29 -0700198 for (BenchmarkMapIt it = g_benchmarks.begin(); it != g_benchmarks.end(); ++it) {
199 g_name_column_width = std::max(g_name_column_width, strlen(it->second->Name()));
200 }
201
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800202 bool need_header = true;
Elliott Hughes1728b232014-05-14 10:02:03 -0700203 for (BenchmarkMapIt it = g_benchmarks.begin(); it != g_benchmarks.end(); ++it) {
Elliott Hughes7be369d2012-11-08 15:37:43 -0800204 ::testing::Benchmark* b = it->second;
205 if (b->ShouldRun(argc, argv)) {
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800206 if (need_header) {
Elliott Hughesc0eed722014-06-11 16:48:29 -0700207 printf("%-*s %10s %10s\n", g_name_column_width, "", "iterations", "ns/op");
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800208 fflush(stdout);
209 need_header = false;
210 }
Elliott Hughes7be369d2012-11-08 15:37:43 -0800211 b->Run();
212 }
213 }
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800214
215 if (need_header) {
216 fprintf(stderr, "No matching benchmarks!\n");
217 fprintf(stderr, "Available benchmarks:\n");
Elliott Hughes1728b232014-05-14 10:02:03 -0700218 for (BenchmarkMapIt it = g_benchmarks.begin(); it != g_benchmarks.end(); ++it) {
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800219 fprintf(stderr, " %s\n", it->second->Name());
220 }
221 exit(EXIT_FAILURE);
222 }
223
Elliott Hughes7be369d2012-11-08 15:37:43 -0800224 return 0;
225}