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 <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