blob: fae09bec45db20f03b7dc4e51cb8c3985d7250cf [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>
Elliott Hughes8bb020e2015-01-16 13:11:25 -080025#include <vector>
Elliott Hughes7be369d2012-11-08 15:37:43 -080026
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
Elliott Hughes8bb020e2015-01-16 13:11:25 -080041// Similar to the code in art, but supporting both binary and decimal prefixes.
42static std::string PrettyInt(uint64_t count, size_t base) {
43 if (base != 2 && base != 10) abort();
44
45 // The byte thresholds at which we display amounts. A count is displayed
46 // in unit U when kUnitThresholds[U] <= bytes < kUnitThresholds[U+1].
47 static const uint64_t kUnitThresholds2[] = {
48 1024*1024*1024 /* Gi */, 2*1024*1024 /* Mi */, 3*1024 /* Ki */, 0,
49 };
50 static const uint64_t kUnitThresholds10[] = {
51 1000*1000*1000 /* G */, 2*1000*1000 /* M */, 3*1000 /* k */, 0,
52 };
53 static const uint64_t kAmountPerUnit2[] = { 1024*1024*1024, 1024*1024, 1024, 1 };
54 static const uint64_t kAmountPerUnit10[] = { 1000*1000*1000, 1000*1000, 1000, 1 };
55 static const char* const kUnitStrings2[] = { "Gi", "Mi", "Ki", "" };
56 static const char* const kUnitStrings10[] = { "G", "M", "k", "" };
57
58 // Which set are we using?
59 const uint64_t* kUnitThresholds = ((base == 2) ? kUnitThresholds2 : kUnitThresholds10);
60 const uint64_t* kAmountPerUnit = ((base == 2) ? kAmountPerUnit2 : kAmountPerUnit10);
61 const char* const* kUnitStrings = ((base == 2) ? kUnitStrings2 : kUnitStrings10);
62
63 size_t i = 0;
64 for (; kUnitThresholds[i] != 0; ++i) {
65 if (count >= kUnitThresholds[i]) {
66 break;
67 }
68 }
69 char* s = NULL;
70 asprintf(&s, "%" PRId64 "%s", count / kAmountPerUnit[i], kUnitStrings[i]);
71 std::string result(s);
72 free(s);
73 return result;
74}
75
Elliott Hughes7be369d2012-11-08 15:37:43 -080076static int Round(int n) {
77 int base = 1;
78 while (base*10 < n) {
79 base *= 10;
80 }
81 if (n < 2*base) {
82 return 2*base;
83 }
84 if (n < 5*base) {
85 return 5*base;
86 }
87 return 10*base;
88}
89
90static int64_t NanoTime() {
91 struct timespec t;
92 t.tv_sec = t.tv_nsec = 0;
93 clock_gettime(CLOCK_MONOTONIC, &t);
94 return static_cast<int64_t>(t.tv_sec) * 1000000000LL + t.tv_nsec;
95}
96
97namespace testing {
98
99Benchmark* Benchmark::Arg(int arg) {
100 args_.push_back(arg);
101 return this;
102}
103
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800104const char* Benchmark::Name() {
105 return name_;
106}
107
Elliott Hughes7be369d2012-11-08 15:37:43 -0800108bool Benchmark::ShouldRun(int argc, char* argv[]) {
109 if (argc == 1) {
110 return true; // With no arguments, we run all benchmarks.
111 }
112 // Otherwise, we interpret each argument as a regular expression and
113 // see if any of our benchmarks match.
114 for (int i = 1; i < argc; i++) {
115 regex_t re;
116 if (regcomp(&re, argv[i], 0) != 0) {
117 fprintf(stderr, "couldn't compile \"%s\" as a regular expression!\n", argv[i]);
118 exit(EXIT_FAILURE);
119 }
120 int match = regexec(&re, name_, 0, NULL, 0);
121 regfree(&re);
122 if (match != REG_NOMATCH) {
123 return true;
124 }
125 }
126 return false;
127}
128
129void Benchmark::Register(const char* name, void (*fn)(int), void (*fn_range)(int, int)) {
130 name_ = name;
131 fn_ = fn;
132 fn_range_ = fn_range;
133
134 if (fn_ == NULL && fn_range_ == NULL) {
135 fprintf(stderr, "%s: missing function\n", name_);
136 exit(EXIT_FAILURE);
137 }
138
Elliott Hughese48f5332015-01-15 17:10:42 -0800139 Benchmarks().push_back(this);
Elliott Hughes7be369d2012-11-08 15:37:43 -0800140}
141
142void Benchmark::Run() {
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800143 if (fn_ != NULL) {
144 RunWithArg(0);
145 } else {
146 if (args_.empty()) {
147 fprintf(stderr, "%s: no args!\n", name_);
148 exit(EXIT_FAILURE);
149 }
150 for (size_t i = 0; i < args_.size(); ++i) {
151 RunWithArg(args_[i]);
152 }
Elliott Hughes7be369d2012-11-08 15:37:43 -0800153 }
154}
155
156void Benchmark::RunRepeatedlyWithArg(int iterations, int arg) {
Elliott Hughes1728b232014-05-14 10:02:03 -0700157 g_bytes_processed = 0;
158 g_benchmark_total_time_ns = 0;
159 g_benchmark_start_time_ns = NanoTime();
Elliott Hughes7be369d2012-11-08 15:37:43 -0800160 if (fn_ != NULL) {
161 fn_(iterations);
162 } else {
163 fn_range_(iterations, arg);
164 }
Elliott Hughes1728b232014-05-14 10:02:03 -0700165 if (g_benchmark_start_time_ns != 0) {
166 g_benchmark_total_time_ns += NanoTime() - g_benchmark_start_time_ns;
Elliott Hughes7be369d2012-11-08 15:37:43 -0800167 }
168}
169
170void Benchmark::RunWithArg(int arg) {
Colin Crossa7635042013-11-27 17:37:54 -0800171 // Run once in case it's expensive.
Elliott Hughes7be369d2012-11-08 15:37:43 -0800172 int iterations = 1;
Colin Crossa7635042013-11-27 17:37:54 -0800173 int64_t realStartTime = NanoTime();
Elliott Hughes7be369d2012-11-08 15:37:43 -0800174 RunRepeatedlyWithArg(iterations, arg);
Colin Crossa7635042013-11-27 17:37:54 -0800175 int64_t realTotalTime = NanoTime() - realStartTime;
176 while (realTotalTime < 1e9 && iterations < 1e8) {
Elliott Hughes7be369d2012-11-08 15:37:43 -0800177 int last = iterations;
Colin Crossa7635042013-11-27 17:37:54 -0800178 if (realTotalTime/iterations == 0) {
Elliott Hughes7be369d2012-11-08 15:37:43 -0800179 iterations = 1e9;
180 } else {
Colin Crossa7635042013-11-27 17:37:54 -0800181 iterations = 1e9 / (realTotalTime/iterations);
Elliott Hughes7be369d2012-11-08 15:37:43 -0800182 }
183 iterations = std::max(last + 1, std::min(iterations + iterations/2, 100*last));
184 iterations = Round(iterations);
Colin Crossa7635042013-11-27 17:37:54 -0800185 realStartTime = NanoTime();
Elliott Hughes7be369d2012-11-08 15:37:43 -0800186 RunRepeatedlyWithArg(iterations, arg);
Colin Crossa7635042013-11-27 17:37:54 -0800187 realTotalTime = NanoTime() - realStartTime;
Elliott Hughes7be369d2012-11-08 15:37:43 -0800188 }
189
190 char throughput[100];
191 throughput[0] = '\0';
Elliott Hughes8bb020e2015-01-16 13:11:25 -0800192
Elliott Hughes1728b232014-05-14 10:02:03 -0700193 if (g_benchmark_total_time_ns > 0 && g_bytes_processed > 0) {
Elliott Hughes8bb020e2015-01-16 13:11:25 -0800194 double gib_processed = static_cast<double>(g_bytes_processed)/1e9;
Elliott Hughes1728b232014-05-14 10:02:03 -0700195 double seconds = static_cast<double>(g_benchmark_total_time_ns)/1e9;
Elliott Hughes8bb020e2015-01-16 13:11:25 -0800196 snprintf(throughput, sizeof(throughput), " %8.3f GiB/s", gib_processed/seconds);
Elliott Hughes7be369d2012-11-08 15:37:43 -0800197 }
198
199 char full_name[100];
200 if (fn_range_ != NULL) {
Elliott Hughes8bb020e2015-01-16 13:11:25 -0800201 snprintf(full_name, sizeof(full_name), "%s/%s", name_, PrettyInt(arg, 2).c_str());
Elliott Hughes7be369d2012-11-08 15:37:43 -0800202 } else {
203 snprintf(full_name, sizeof(full_name), "%s", name_);
204 }
205
Elliott Hughes8bb020e2015-01-16 13:11:25 -0800206 printf("%-*s %10s %10" PRId64 "%s\n",
207 g_name_column_width, full_name,
208 PrettyInt(iterations, 10).c_str(),
209 g_benchmark_total_time_ns/iterations,
210 throughput);
Elliott Hughes7be369d2012-11-08 15:37:43 -0800211 fflush(stdout);
212}
213
214} // namespace testing
215
216void SetBenchmarkBytesProcessed(int64_t x) {
Elliott Hughes1728b232014-05-14 10:02:03 -0700217 g_bytes_processed = x;
Elliott Hughes7be369d2012-11-08 15:37:43 -0800218}
219
220void StopBenchmarkTiming() {
Elliott Hughes1728b232014-05-14 10:02:03 -0700221 if (g_benchmark_start_time_ns != 0) {
222 g_benchmark_total_time_ns += NanoTime() - g_benchmark_start_time_ns;
Elliott Hughes7be369d2012-11-08 15:37:43 -0800223 }
Elliott Hughes1728b232014-05-14 10:02:03 -0700224 g_benchmark_start_time_ns = 0;
Elliott Hughes7be369d2012-11-08 15:37:43 -0800225}
226
227void StartBenchmarkTiming() {
Elliott Hughes1728b232014-05-14 10:02:03 -0700228 if (g_benchmark_start_time_ns == 0) {
229 g_benchmark_start_time_ns = NanoTime();
Elliott Hughes7be369d2012-11-08 15:37:43 -0800230 }
231}
232
233int main(int argc, char* argv[]) {
Colin Cross7b9df192013-11-15 14:34:22 -0800234 if (Benchmarks().empty()) {
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800235 fprintf(stderr, "No benchmarks registered!\n");
Elliott Hughes7be369d2012-11-08 15:37:43 -0800236 exit(EXIT_FAILURE);
237 }
238
Elliott Hughese48f5332015-01-15 17:10:42 -0800239 for (auto& b : Benchmarks()) {
240 int name_width = static_cast<int>(strlen(b->Name()));
Elliott Hughes5ab51d02014-06-12 12:52:58 -0700241 g_name_column_width = std::max(g_name_column_width, name_width);
Elliott Hughesc0eed722014-06-11 16:48:29 -0700242 }
243
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800244 bool need_header = true;
Elliott Hughese48f5332015-01-15 17:10:42 -0800245 for (auto& b : Benchmarks()) {
Elliott Hughes7be369d2012-11-08 15:37:43 -0800246 if (b->ShouldRun(argc, argv)) {
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800247 if (need_header) {
Elliott Hughesc0eed722014-06-11 16:48:29 -0700248 printf("%-*s %10s %10s\n", g_name_column_width, "", "iterations", "ns/op");
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800249 fflush(stdout);
250 need_header = false;
251 }
Elliott Hughes7be369d2012-11-08 15:37:43 -0800252 b->Run();
253 }
254 }
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800255
256 if (need_header) {
257 fprintf(stderr, "No matching benchmarks!\n");
258 fprintf(stderr, "Available benchmarks:\n");
Elliott Hughese48f5332015-01-15 17:10:42 -0800259 for (auto& b : Benchmarks()) {
260 fprintf(stderr, " %s\n", b->Name());
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800261 }
262 exit(EXIT_FAILURE);
263 }
264
Elliott Hughes7be369d2012-11-08 15:37:43 -0800265 return 0;
266}