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