blob: 815d56b68f242c9dcaa25bf9855dec53d4d56343 [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>
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;
Elliott Hughes7be369d2012-11-08 15:37:43 -080032
33typedef std::map<std::string, ::testing::Benchmark*> BenchmarkMap;
34typedef BenchmarkMap::iterator BenchmarkMapIt;
Elliott Hughes1728b232014-05-14 10:02:03 -070035static BenchmarkMap g_benchmarks;
Elliott Hughes5ab51d02014-06-12 12:52:58 -070036static int g_name_column_width = 20;
Elliott Hughes7be369d2012-11-08 15:37:43 -080037
38static int Round(int n) {
39 int base = 1;
40 while (base*10 < n) {
41 base *= 10;
42 }
43 if (n < 2*base) {
44 return 2*base;
45 }
46 if (n < 5*base) {
47 return 5*base;
48 }
49 return 10*base;
50}
51
52static int64_t NanoTime() {
53 struct timespec t;
54 t.tv_sec = t.tv_nsec = 0;
55 clock_gettime(CLOCK_MONOTONIC, &t);
56 return static_cast<int64_t>(t.tv_sec) * 1000000000LL + t.tv_nsec;
57}
58
59namespace testing {
60
61Benchmark* Benchmark::Arg(int arg) {
62 args_.push_back(arg);
63 return this;
64}
65
Elliott Hughes9edb3e02013-02-06 15:47:09 -080066const char* Benchmark::Name() {
67 return name_;
68}
69
Elliott Hughes7be369d2012-11-08 15:37:43 -080070bool Benchmark::ShouldRun(int argc, char* argv[]) {
71 if (argc == 1) {
72 return true; // With no arguments, we run all benchmarks.
73 }
74 // Otherwise, we interpret each argument as a regular expression and
75 // see if any of our benchmarks match.
76 for (int i = 1; i < argc; i++) {
77 regex_t re;
78 if (regcomp(&re, argv[i], 0) != 0) {
79 fprintf(stderr, "couldn't compile \"%s\" as a regular expression!\n", argv[i]);
80 exit(EXIT_FAILURE);
81 }
82 int match = regexec(&re, name_, 0, NULL, 0);
83 regfree(&re);
84 if (match != REG_NOMATCH) {
85 return true;
86 }
87 }
88 return false;
89}
90
91void Benchmark::Register(const char* name, void (*fn)(int), void (*fn_range)(int, int)) {
92 name_ = name;
93 fn_ = fn;
94 fn_range_ = fn_range;
95
96 if (fn_ == NULL && fn_range_ == NULL) {
97 fprintf(stderr, "%s: missing function\n", name_);
98 exit(EXIT_FAILURE);
99 }
100
Elliott Hughes1728b232014-05-14 10:02:03 -0700101 g_benchmarks.insert(std::make_pair(name, this));
Elliott Hughes7be369d2012-11-08 15:37:43 -0800102}
103
104void Benchmark::Run() {
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800105 if (fn_ != NULL) {
106 RunWithArg(0);
107 } else {
108 if (args_.empty()) {
109 fprintf(stderr, "%s: no args!\n", name_);
110 exit(EXIT_FAILURE);
111 }
112 for (size_t i = 0; i < args_.size(); ++i) {
113 RunWithArg(args_[i]);
114 }
Elliott Hughes7be369d2012-11-08 15:37:43 -0800115 }
116}
117
118void Benchmark::RunRepeatedlyWithArg(int iterations, int arg) {
Elliott Hughes1728b232014-05-14 10:02:03 -0700119 g_bytes_processed = 0;
120 g_benchmark_total_time_ns = 0;
121 g_benchmark_start_time_ns = NanoTime();
Elliott Hughes7be369d2012-11-08 15:37:43 -0800122 if (fn_ != NULL) {
123 fn_(iterations);
124 } else {
125 fn_range_(iterations, arg);
126 }
Elliott Hughes1728b232014-05-14 10:02:03 -0700127 if (g_benchmark_start_time_ns != 0) {
128 g_benchmark_total_time_ns += NanoTime() - g_benchmark_start_time_ns;
Elliott Hughes7be369d2012-11-08 15:37:43 -0800129 }
130}
131
132void Benchmark::RunWithArg(int arg) {
133 // run once in case it's expensive
134 int iterations = 1;
135 RunRepeatedlyWithArg(iterations, arg);
Elliott Hughes1728b232014-05-14 10:02:03 -0700136 while (g_benchmark_total_time_ns < 1e9 && iterations < 1e9) {
Elliott Hughes7be369d2012-11-08 15:37:43 -0800137 int last = iterations;
Elliott Hughes1728b232014-05-14 10:02:03 -0700138 if (g_benchmark_total_time_ns/iterations == 0) {
Elliott Hughes7be369d2012-11-08 15:37:43 -0800139 iterations = 1e9;
140 } else {
Elliott Hughes1728b232014-05-14 10:02:03 -0700141 iterations = 1e9 / (g_benchmark_total_time_ns/iterations);
Elliott Hughes7be369d2012-11-08 15:37:43 -0800142 }
143 iterations = std::max(last + 1, std::min(iterations + iterations/2, 100*last));
144 iterations = Round(iterations);
145 RunRepeatedlyWithArg(iterations, arg);
146 }
147
148 char throughput[100];
149 throughput[0] = '\0';
Elliott Hughes1728b232014-05-14 10:02:03 -0700150 if (g_benchmark_total_time_ns > 0 && g_bytes_processed > 0) {
151 double mib_processed = static_cast<double>(g_bytes_processed)/1e6;
152 double seconds = static_cast<double>(g_benchmark_total_time_ns)/1e9;
Elliott Hughes7be369d2012-11-08 15:37:43 -0800153 snprintf(throughput, sizeof(throughput), " %8.2f MiB/s", mib_processed/seconds);
154 }
155
156 char full_name[100];
157 if (fn_range_ != NULL) {
158 if (arg >= (1<<20)) {
159 snprintf(full_name, sizeof(full_name), "%s/%dM", name_, arg/(1<<20));
160 } else if (arg >= (1<<10)) {
161 snprintf(full_name, sizeof(full_name), "%s/%dK", name_, arg/(1<<10));
162 } else {
163 snprintf(full_name, sizeof(full_name), "%s/%d", name_, arg);
164 }
165 } else {
166 snprintf(full_name, sizeof(full_name), "%s", name_);
167 }
168
Elliott Hughesc0eed722014-06-11 16:48:29 -0700169 printf("%-*s %10d %10" PRId64 "%s\n", g_name_column_width, full_name,
Elliott Hughes1728b232014-05-14 10:02:03 -0700170 iterations, g_benchmark_total_time_ns/iterations, throughput);
Elliott Hughes7be369d2012-11-08 15:37:43 -0800171 fflush(stdout);
172}
173
174} // namespace testing
175
176void SetBenchmarkBytesProcessed(int64_t x) {
Elliott Hughes1728b232014-05-14 10:02:03 -0700177 g_bytes_processed = x;
Elliott Hughes7be369d2012-11-08 15:37:43 -0800178}
179
180void StopBenchmarkTiming() {
Elliott Hughes1728b232014-05-14 10:02:03 -0700181 if (g_benchmark_start_time_ns != 0) {
182 g_benchmark_total_time_ns += NanoTime() - g_benchmark_start_time_ns;
Elliott Hughes7be369d2012-11-08 15:37:43 -0800183 }
Elliott Hughes1728b232014-05-14 10:02:03 -0700184 g_benchmark_start_time_ns = 0;
Elliott Hughes7be369d2012-11-08 15:37:43 -0800185}
186
187void StartBenchmarkTiming() {
Elliott Hughes1728b232014-05-14 10:02:03 -0700188 if (g_benchmark_start_time_ns == 0) {
189 g_benchmark_start_time_ns = NanoTime();
Elliott Hughes7be369d2012-11-08 15:37:43 -0800190 }
191}
192
193int main(int argc, char* argv[]) {
Elliott Hughes1728b232014-05-14 10:02:03 -0700194 if (g_benchmarks.empty()) {
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800195 fprintf(stderr, "No benchmarks registered!\n");
Elliott Hughes7be369d2012-11-08 15:37:43 -0800196 exit(EXIT_FAILURE);
197 }
198
Elliott Hughesc0eed722014-06-11 16:48:29 -0700199 for (BenchmarkMapIt it = g_benchmarks.begin(); it != g_benchmarks.end(); ++it) {
Elliott Hughes5ab51d02014-06-12 12:52:58 -0700200 int name_width = static_cast<int>(strlen(it->second->Name()));
201 g_name_column_width = std::max(g_name_column_width, name_width);
Elliott Hughesc0eed722014-06-11 16:48:29 -0700202 }
203
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800204 bool need_header = true;
Elliott Hughes1728b232014-05-14 10:02:03 -0700205 for (BenchmarkMapIt it = g_benchmarks.begin(); it != g_benchmarks.end(); ++it) {
Elliott Hughes7be369d2012-11-08 15:37:43 -0800206 ::testing::Benchmark* b = it->second;
207 if (b->ShouldRun(argc, argv)) {
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800208 if (need_header) {
Elliott Hughesc0eed722014-06-11 16:48:29 -0700209 printf("%-*s %10s %10s\n", g_name_column_width, "", "iterations", "ns/op");
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800210 fflush(stdout);
211 need_header = false;
212 }
Elliott Hughes7be369d2012-11-08 15:37:43 -0800213 b->Run();
214 }
215 }
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800216
217 if (need_header) {
218 fprintf(stderr, "No matching benchmarks!\n");
219 fprintf(stderr, "Available benchmarks:\n");
Elliott Hughes1728b232014-05-14 10:02:03 -0700220 for (BenchmarkMapIt it = g_benchmarks.begin(); it != g_benchmarks.end(); ++it) {
Elliott Hughes9edb3e02013-02-06 15:47:09 -0800221 fprintf(stderr, " %s\n", it->second->Name());
222 }
223 exit(EXIT_FAILURE);
224 }
225
Elliott Hughes7be369d2012-11-08 15:37:43 -0800226 return 0;
227}