1 /* 2 * Copyright (C) 2012-2014 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 <inttypes.h> 20 #include <math.h> 21 #include <regex.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 25 #include <string> 26 #include <map> 27 #include <vector> 28 29 static uint64_t gBytesProcessed; 30 static uint64_t gBenchmarkTotalTimeNs; 31 static uint64_t gBenchmarkTotalTimeNsSquared; 32 static uint64_t gBenchmarkNum; 33 static uint64_t gBenchmarkStartTimeNs; 34 35 typedef std::vector< ::testing::Benchmark* > BenchmarkList; 36 static BenchmarkList* gBenchmarks; 37 38 static 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 52 static uint64_t NanoTime() { 53 struct timespec t; 54 t.tv_sec = t.tv_nsec = 0; 55 clock_gettime(CLOCK_MONOTONIC, &t); 56 return static_cast<uint64_t>(t.tv_sec) * 1000000000ULL + t.tv_nsec; 57 } 58 59 namespace testing { 60 61 int PrettyPrintInt(char* str, int len, unsigned int arg) 62 { 63 if (arg >= (1<<30) && arg % (1<<30) == 0) { 64 return snprintf(str, len, "%uGi", arg/(1<<30)); 65 } else if (arg >= (1<<20) && arg % (1<<20) == 0) { 66 return snprintf(str, len, "%uMi", arg/(1<<20)); 67 } else if (arg >= (1<<10) && arg % (1<<10) == 0) { 68 return snprintf(str, len, "%uKi", arg/(1<<10)); 69 } else if (arg >= 1000000000 && arg % 1000000000 == 0) { 70 return snprintf(str, len, "%uG", arg/1000000000); 71 } else if (arg >= 1000000 && arg % 1000000 == 0) { 72 return snprintf(str, len, "%uM", arg/1000000); 73 } else if (arg >= 1000 && arg % 1000 == 0) { 74 return snprintf(str, len, "%uK", arg/1000); 75 } else { 76 return snprintf(str, len, "%u", arg); 77 } 78 } 79 80 bool ShouldRun(Benchmark* b, int argc, char* argv[]) { 81 if (argc == 1) { 82 return true; // With no arguments, we run all benchmarks. 83 } 84 // Otherwise, we interpret each argument as a regular expression and 85 // see if any of our benchmarks match. 86 for (int i = 1; i < argc; i++) { 87 regex_t re; 88 if (regcomp(&re, argv[i], 0) != 0) { 89 fprintf(stderr, "couldn't compile \"%s\" as a regular expression!\n", argv[i]); 90 exit(EXIT_FAILURE); 91 } 92 int match = regexec(&re, b->Name(), 0, NULL, 0); 93 regfree(&re); 94 if (match != REG_NOMATCH) { 95 return true; 96 } 97 } 98 return false; 99 } 100 101 void BenchmarkRegister(Benchmark* b) { 102 if (gBenchmarks == NULL) { 103 gBenchmarks = new BenchmarkList; 104 } 105 gBenchmarks->push_back(b); 106 } 107 108 void RunRepeatedly(Benchmark* b, int iterations) { 109 gBytesProcessed = 0; 110 ResetBenchmarkTiming(); 111 uint64_t StartTimeNs = NanoTime(); 112 b->RunFn(iterations); 113 // Catch us if we fail to log anything. 114 if ((gBenchmarkTotalTimeNs == 0) 115 && (StartTimeNs != 0) 116 && (gBenchmarkStartTimeNs == 0)) { 117 gBenchmarkTotalTimeNs = NanoTime() - StartTimeNs; 118 } 119 } 120 121 void Run(Benchmark* b) { 122 // run once in case it's expensive 123 unsigned iterations = 1; 124 uint64_t s = NanoTime(); 125 RunRepeatedly(b, iterations); 126 s = NanoTime() - s; 127 while (s < 2e9 && gBenchmarkTotalTimeNs < 1e9 && iterations < 1e9) { 128 unsigned last = iterations; 129 if (gBenchmarkTotalTimeNs/iterations == 0) { 130 iterations = 1e9; 131 } else { 132 iterations = 1e9 / (gBenchmarkTotalTimeNs/iterations); 133 } 134 iterations = std::max(last + 1, std::min(iterations + iterations/2, 100*last)); 135 iterations = Round(iterations); 136 s = NanoTime(); 137 RunRepeatedly(b, iterations); 138 s = NanoTime() - s; 139 } 140 141 char throughput[100]; 142 throughput[0] = '\0'; 143 if (gBenchmarkTotalTimeNs > 0 && gBytesProcessed > 0) { 144 double mib_processed = static_cast<double>(gBytesProcessed)/1e6; 145 double seconds = static_cast<double>(gBenchmarkTotalTimeNs)/1e9; 146 snprintf(throughput, sizeof(throughput), " %8.2f MiB/s", mib_processed/seconds); 147 } 148 149 char full_name[100]; 150 snprintf(full_name, sizeof(full_name), "%s%s%s", b->Name(), 151 b->ArgName() ? "/" : "", 152 b->ArgName() ? b->ArgName() : ""); 153 154 uint64_t mean = gBenchmarkTotalTimeNs / iterations; 155 uint64_t sdev = 0; 156 if (gBenchmarkNum == iterations) { 157 mean = gBenchmarkTotalTimeNs / gBenchmarkNum; 158 uint64_t nXvariance = gBenchmarkTotalTimeNsSquared * gBenchmarkNum 159 - (gBenchmarkTotalTimeNs * gBenchmarkTotalTimeNs); 160 sdev = (sqrt((double)nXvariance) / gBenchmarkNum / gBenchmarkNum) + 0.5; 161 } 162 if (mean > (10000 * sdev)) { 163 printf("%-25s %10" PRIu64 " %10" PRIu64 "%s\n", full_name, 164 static_cast<uint64_t>(iterations), mean, throughput); 165 } else { 166 printf("%-25s %10" PRIu64 " %10" PRIu64 "(\317\203%" PRIu64 ")%s\n", full_name, 167 static_cast<uint64_t>(iterations), mean, sdev, throughput); 168 } 169 fflush(stdout); 170 } 171 172 } // namespace testing 173 174 void SetBenchmarkBytesProcessed(uint64_t x) { 175 gBytesProcessed = x; 176 } 177 178 void ResetBenchmarkTiming() { 179 gBenchmarkStartTimeNs = 0; 180 gBenchmarkTotalTimeNs = 0; 181 gBenchmarkTotalTimeNsSquared = 0; 182 gBenchmarkNum = 0; 183 } 184 185 void StopBenchmarkTiming(void) { 186 if (gBenchmarkStartTimeNs != 0) { 187 int64_t diff = NanoTime() - gBenchmarkStartTimeNs; 188 gBenchmarkTotalTimeNs += diff; 189 gBenchmarkTotalTimeNsSquared += diff * diff; 190 ++gBenchmarkNum; 191 } 192 gBenchmarkStartTimeNs = 0; 193 } 194 195 void StartBenchmarkTiming(void) { 196 if (gBenchmarkStartTimeNs == 0) { 197 gBenchmarkStartTimeNs = NanoTime(); 198 } 199 } 200 201 void StopBenchmarkTiming(uint64_t NanoTime) { 202 if (gBenchmarkStartTimeNs != 0) { 203 int64_t diff = NanoTime - gBenchmarkStartTimeNs; 204 gBenchmarkTotalTimeNs += diff; 205 gBenchmarkTotalTimeNsSquared += diff * diff; 206 if (NanoTime != 0) { 207 ++gBenchmarkNum; 208 } 209 } 210 gBenchmarkStartTimeNs = 0; 211 } 212 213 void StartBenchmarkTiming(uint64_t NanoTime) { 214 if (gBenchmarkStartTimeNs == 0) { 215 gBenchmarkStartTimeNs = NanoTime; 216 } 217 } 218 219 int main(int argc, char* argv[]) { 220 if (gBenchmarks->empty()) { 221 fprintf(stderr, "No benchmarks registered!\n"); 222 exit(EXIT_FAILURE); 223 } 224 225 bool need_header = true; 226 for (auto b : *gBenchmarks) { 227 if (ShouldRun(b, argc, argv)) { 228 if (need_header) { 229 printf("%-25s %10s %10s\n", "", "iterations", "ns/op"); 230 fflush(stdout); 231 need_header = false; 232 } 233 Run(b); 234 } 235 } 236 237 if (need_header) { 238 fprintf(stderr, "No matching benchmarks!\n"); 239 fprintf(stderr, "Available benchmarks:\n"); 240 for (auto b : *gBenchmarks) { 241 fprintf(stderr, " %s\n", b->Name()); 242 } 243 exit(EXIT_FAILURE); 244 } 245 246 return 0; 247 } 248