blob: 6d83f8a83b9e9a3e9a9d3cd4e8a04151106db4a9 [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
Colin Cross7b9df192013-11-15 14:34:22 -080017#include <benchmark.h>
Elliott Hughes7be369d2012-11-08 15:37:43 -080018
19#include <regex.h>
20#include <stdio.h>
21#include <stdlib.h>
Elliott Hughes212e0e32014-12-01 16:43:51 -080022#include <time.h>
Elliott Hughes7be369d2012-11-08 15:37:43 -080023
24#include <string>
25#include <map>
26
Serban Constantinescu282e2322013-10-22 11:30:12 +010027#include <inttypes.h>
28
Elliott Hughes1728b232014-05-14 10:02:03 -070029static int64_t g_bytes_processed;
30static int64_t g_benchmark_total_time_ns;
31static int64_t g_benchmark_start_time_ns;
Colin Cross7b9df192013-11-15 14:34:22 -080032static int g_name_column_width = 20;
Elliott Hughes7be369d2012-11-08 15:37:43 -080033
Elliott Hughese48f5332015-01-15 17:10:42 -080034typedef std::vector<::testing::Benchmark*> BenchmarkList;
Colin Cross7b9df192013-11-15 14:34:22 -080035
Elliott Hughese48f5332015-01-15 17:10:42 -080036static BenchmarkList& Benchmarks() {
37 static BenchmarkList benchmarks;
Colin Cross7b9df192013-11-15 14:34:22 -080038 return benchmarks;
39}
Elliott Hughes7be369d2012-11-08 15:37:43 -080040
41static int Round(int n) {
42 int base = 1;
43 while (base*10 < n) {
44 base *= 10;
45 }
46 if (n < 2*base) {
47 return 2*base;
48 }
49 if (n < 5*base) {
50 return 5*base;
51 }
52 return 10*base;
53}
54
55static int64_t NanoTime() {
56 struct timespec t;
57 t.tv_sec = t.tv_nsec = 0;
58 clock_gettime(CLOCK_MONOTONIC, &t);
59 return static_cast<int64_t>(t.tv_sec) * 1000000000LL + t.tv_nsec;
60}
61
62namespace testing {
63
64Benchmark* Benchmark::Arg(int arg) {
65 args_.push_back(arg);
66 return this;
67}
68
Elliott Hughes9edb3e02013-02-06 15:47:09 -080069const char* Benchmark::Name() {
70 return name_;
71}
72
Elliott Hughes7be369d2012-11-08 15:37:43 -080073bool Benchmark::ShouldRun(int argc, char* argv[]) {
74 if (argc == 1) {
75 return true; // With no arguments, we run all benchmarks.
76 }
77 // Otherwise, we interpret each argument as a regular expression and
78 // see if any of our benchmarks match.
79 for (int i = 1; i < argc; i++) {
80 regex_t re;
81 if (regcomp(&re, argv[i], 0) != 0) {
82 fprintf(stderr, "couldn't compile \"%s\" as a regular expression!\n", argv[i]);
83 exit(EXIT_FAILURE);
84 }
85 int match = regexec(&re, name_, 0, NULL, 0);
86 regfree(&re);
87 if (match != REG_NOMATCH) {
88 return true;
89 }
90 }
91 return false;
92}
93
94void Benchmark::Register(const char* name, void (*fn)(int), void (*fn_range)(int, int)) {
95 name_ = name;
96 fn_ = fn;
97 fn_range_ = fn_range;
98
99 if (fn_ == NULL && fn_range_ == NULL) {
100 fprintf(stderr, "%s: missing function\n", name_);
101 exit(EXIT_FAILURE);
102 }
103
Elliott Hughese48f5332015-01-15 17:10:42 -0800104 Benchmarks().push_back(this);
Elliott Hughes7be369d2012-11-08 15:37:43 -0800105}
106
107void Benchmark::Run() {
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800108 if (fn_ != NULL) {
109 RunWithArg(0);
110 } else {
111 if (args_.empty()) {
112 fprintf(stderr, "%s: no args!\n", name_);
113 exit(EXIT_FAILURE);
114 }
115 for (size_t i = 0; i < args_.size(); ++i) {
116 RunWithArg(args_[i]);
117 }
Elliott Hughes7be369d2012-11-08 15:37:43 -0800118 }
119}
120
121void Benchmark::RunRepeatedlyWithArg(int iterations, int arg) {
Elliott Hughes1728b232014-05-14 10:02:03 -0700122 g_bytes_processed = 0;
123 g_benchmark_total_time_ns = 0;
124 g_benchmark_start_time_ns = NanoTime();
Elliott Hughes7be369d2012-11-08 15:37:43 -0800125 if (fn_ != NULL) {
126 fn_(iterations);
127 } else {
128 fn_range_(iterations, arg);
129 }
Elliott Hughes1728b232014-05-14 10:02:03 -0700130 if (g_benchmark_start_time_ns != 0) {
131 g_benchmark_total_time_ns += NanoTime() - g_benchmark_start_time_ns;
Elliott Hughes7be369d2012-11-08 15:37:43 -0800132 }
133}
134
135void Benchmark::RunWithArg(int arg) {
Colin Crossa7635042013-11-27 17:37:54 -0800136 // Run once in case it's expensive.
Elliott Hughes7be369d2012-11-08 15:37:43 -0800137 int iterations = 1;
Colin Crossa7635042013-11-27 17:37:54 -0800138 int64_t realStartTime = NanoTime();
Elliott Hughes7be369d2012-11-08 15:37:43 -0800139 RunRepeatedlyWithArg(iterations, arg);
Colin Crossa7635042013-11-27 17:37:54 -0800140 int64_t realTotalTime = NanoTime() - realStartTime;
141 while (realTotalTime < 1e9 && iterations < 1e8) {
Elliott Hughes7be369d2012-11-08 15:37:43 -0800142 int last = iterations;
Colin Crossa7635042013-11-27 17:37:54 -0800143 if (realTotalTime/iterations == 0) {
Elliott Hughes7be369d2012-11-08 15:37:43 -0800144 iterations = 1e9;
145 } else {
Colin Crossa7635042013-11-27 17:37:54 -0800146 iterations = 1e9 / (realTotalTime/iterations);
Elliott Hughes7be369d2012-11-08 15:37:43 -0800147 }
148 iterations = std::max(last + 1, std::min(iterations + iterations/2, 100*last));
149 iterations = Round(iterations);
Colin Crossa7635042013-11-27 17:37:54 -0800150 realStartTime = NanoTime();
Elliott Hughes7be369d2012-11-08 15:37:43 -0800151 RunRepeatedlyWithArg(iterations, arg);
Colin Crossa7635042013-11-27 17:37:54 -0800152 realTotalTime = NanoTime() - realStartTime;
Elliott Hughes7be369d2012-11-08 15:37:43 -0800153 }
154
155 char throughput[100];
156 throughput[0] = '\0';
Elliott Hughes1728b232014-05-14 10:02:03 -0700157 if (g_benchmark_total_time_ns > 0 && g_bytes_processed > 0) {
158 double mib_processed = static_cast<double>(g_bytes_processed)/1e6;
159 double seconds = static_cast<double>(g_benchmark_total_time_ns)/1e9;
Elliott Hughes7be369d2012-11-08 15:37:43 -0800160 snprintf(throughput, sizeof(throughput), " %8.2f MiB/s", mib_processed/seconds);
161 }
162
163 char full_name[100];
164 if (fn_range_ != NULL) {
165 if (arg >= (1<<20)) {
166 snprintf(full_name, sizeof(full_name), "%s/%dM", name_, arg/(1<<20));
167 } else if (arg >= (1<<10)) {
168 snprintf(full_name, sizeof(full_name), "%s/%dK", name_, arg/(1<<10));
169 } else {
170 snprintf(full_name, sizeof(full_name), "%s/%d", name_, arg);
171 }
172 } else {
173 snprintf(full_name, sizeof(full_name), "%s", name_);
174 }
175
Elliott Hughesc0eed722014-06-11 16:48:29 -0700176 printf("%-*s %10d %10" PRId64 "%s\n", g_name_column_width, full_name,
Elliott Hughes1728b232014-05-14 10:02:03 -0700177 iterations, g_benchmark_total_time_ns/iterations, throughput);
Elliott Hughes7be369d2012-11-08 15:37:43 -0800178 fflush(stdout);
179}
180
181} // namespace testing
182
183void SetBenchmarkBytesProcessed(int64_t x) {
Elliott Hughes1728b232014-05-14 10:02:03 -0700184 g_bytes_processed = x;
Elliott Hughes7be369d2012-11-08 15:37:43 -0800185}
186
187void StopBenchmarkTiming() {
Elliott Hughes1728b232014-05-14 10:02:03 -0700188 if (g_benchmark_start_time_ns != 0) {
189 g_benchmark_total_time_ns += NanoTime() - g_benchmark_start_time_ns;
Elliott Hughes7be369d2012-11-08 15:37:43 -0800190 }
Elliott Hughes1728b232014-05-14 10:02:03 -0700191 g_benchmark_start_time_ns = 0;
Elliott Hughes7be369d2012-11-08 15:37:43 -0800192}
193
194void StartBenchmarkTiming() {
Elliott Hughes1728b232014-05-14 10:02:03 -0700195 if (g_benchmark_start_time_ns == 0) {
196 g_benchmark_start_time_ns = NanoTime();
Elliott Hughes7be369d2012-11-08 15:37:43 -0800197 }
198}
199
200int main(int argc, char* argv[]) {
Colin Cross7b9df192013-11-15 14:34:22 -0800201 if (Benchmarks().empty()) {
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800202 fprintf(stderr, "No benchmarks registered!\n");
Elliott Hughes7be369d2012-11-08 15:37:43 -0800203 exit(EXIT_FAILURE);
204 }
205
Elliott Hughese48f5332015-01-15 17:10:42 -0800206 for (auto& b : Benchmarks()) {
207 int name_width = static_cast<int>(strlen(b->Name()));
Elliott Hughes5ab51d02014-06-12 12:52:58 -0700208 g_name_column_width = std::max(g_name_column_width, name_width);
Elliott Hughesc0eed722014-06-11 16:48:29 -0700209 }
210
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800211 bool need_header = true;
Elliott Hughese48f5332015-01-15 17:10:42 -0800212 for (auto& b : Benchmarks()) {
Elliott Hughes7be369d2012-11-08 15:37:43 -0800213 if (b->ShouldRun(argc, argv)) {
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800214 if (need_header) {
Elliott Hughesc0eed722014-06-11 16:48:29 -0700215 printf("%-*s %10s %10s\n", g_name_column_width, "", "iterations", "ns/op");
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800216 fflush(stdout);
217 need_header = false;
218 }
Elliott Hughes7be369d2012-11-08 15:37:43 -0800219 b->Run();
220 }
221 }
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800222
223 if (need_header) {
224 fprintf(stderr, "No matching benchmarks!\n");
225 fprintf(stderr, "Available benchmarks:\n");
Elliott Hughese48f5332015-01-15 17:10:42 -0800226 for (auto& b : Benchmarks()) {
227 fprintf(stderr, " %s\n", b->Name());
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800228 }
229 exit(EXIT_FAILURE);
230 }
231
Elliott Hughes7be369d2012-11-08 15:37:43 -0800232 return 0;
233}