Home | History | Annotate | Download | only in tests
      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