diff options
Diffstat (limited to 'utils/google-benchmark/src')
| -rw-r--r-- | utils/google-benchmark/src/CMakeLists.txt | 45 | ||||
| -rw-r--r-- | utils/google-benchmark/src/benchmark.cc | 37 | ||||
| -rw-r--r-- | utils/google-benchmark/src/benchmark_api_internal.h | 2 | ||||
| -rw-r--r-- | utils/google-benchmark/src/benchmark_register.cc | 35 | ||||
| -rw-r--r-- | utils/google-benchmark/src/complexity.cc | 40 | ||||
| -rw-r--r-- | utils/google-benchmark/src/console_reporter.cc | 31 | ||||
| -rw-r--r-- | utils/google-benchmark/src/counter.cc | 68 | ||||
| -rw-r--r-- | utils/google-benchmark/src/counter.h | 26 | ||||
| -rw-r--r-- | utils/google-benchmark/src/csv_reporter.cc | 56 | ||||
| -rw-r--r-- | utils/google-benchmark/src/cycleclock.h | 31 | ||||
| -rw-r--r-- | utils/google-benchmark/src/internal_macros.h | 19 | ||||
| -rw-r--r-- | utils/google-benchmark/src/json_reporter.cc | 7 | ||||
| -rw-r--r-- | utils/google-benchmark/src/re.h | 16 | ||||
| -rw-r--r-- | utils/google-benchmark/src/sleep.cc | 3 | ||||
| -rw-r--r-- | utils/google-benchmark/src/sleep.h | 12 | ||||
| -rw-r--r-- | utils/google-benchmark/src/string_util.cc | 20 | ||||
| -rw-r--r-- | utils/google-benchmark/src/sysinfo.cc | 9 | ||||
| -rw-r--r-- | utils/google-benchmark/src/timers.cc | 45 | 
18 files changed, 424 insertions, 78 deletions
diff --git a/utils/google-benchmark/src/CMakeLists.txt b/utils/google-benchmark/src/CMakeLists.txt index 403887517797..7707773930c5 100644 --- a/utils/google-benchmark/src/CMakeLists.txt +++ b/utils/google-benchmark/src/CMakeLists.txt @@ -21,24 +21,55 @@ set_target_properties(benchmark PROPERTIES  # Link threads.  target_link_libraries(benchmark  ${BENCHMARK_CXX_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) +find_library(LIBRT rt) +if(LIBRT) +  target_link_libraries(benchmark ${LIBRT}) +endif()  # We need extra libraries on Windows  if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")    target_link_libraries(benchmark Shlwapi)  endif() -# Expose public API -target_include_directories(benchmark PUBLIC ${PROJECT_SOURCE_DIR}/include) +set(include_install_dir "include") +set(lib_install_dir "lib/") +set(bin_install_dir "bin/") +set(config_install_dir "lib/cmake/${PROJECT_NAME}") + +set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated") + +set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake") +set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake") +set(targets_export_name "${PROJECT_NAME}Targets") + +set(namespace "${PROJECT_NAME}::") + +include(CMakePackageConfigHelpers) +write_basic_package_version_file( +    "${version_config}" VERSION ${GIT_VERSION} COMPATIBILITY SameMajorVersion +) + +configure_file("${PROJECT_SOURCE_DIR}/cmake/Config.cmake.in" "${project_config}" @ONLY)  # Install target (will install the library to specified CMAKE_INSTALL_PREFIX variable)  install(    TARGETS benchmark -  ARCHIVE DESTINATION lib -  LIBRARY DESTINATION lib -  RUNTIME DESTINATION bin -  COMPONENT library) +  EXPORT ${targets_export_name} +  ARCHIVE DESTINATION ${lib_install_dir} +  LIBRARY DESTINATION ${lib_install_dir} +  RUNTIME DESTINATION ${bin_install_dir} +  INCLUDES DESTINATION ${include_install_dir})  install(    DIRECTORY "${PROJECT_SOURCE_DIR}/include/benchmark" -  DESTINATION include +  DESTINATION ${include_install_dir}    FILES_MATCHING PATTERN "*.*h") + +install( +    FILES "${project_config}" "${version_config}" +    DESTINATION "${config_install_dir}") + +install( +    EXPORT "${targets_export_name}" +    NAMESPACE "${namespace}" +    DESTINATION "${config_install_dir}") diff --git a/utils/google-benchmark/src/benchmark.cc b/utils/google-benchmark/src/benchmark.cc index 95f6a25b7563..00ffa07ff56e 100644 --- a/utils/google-benchmark/src/benchmark.cc +++ b/utils/google-benchmark/src/benchmark.cc @@ -37,6 +37,7 @@  #include "colorprint.h"  #include "commandlineflags.h"  #include "complexity.h" +#include "counter.h"  #include "log.h"  #include "mutex.h"  #include "re.h" @@ -145,6 +146,7 @@ class ThreadManager {      std::string report_label_;      std::string error_message_;      bool has_error_ = false; +    UserCounters counters;    };    GUARDED_BY(GetBenchmarkMutex()) Result results; @@ -249,6 +251,7 @@ BenchmarkReporter::Run CreateRunReport(      report.complexity_n = results.complexity_n;      report.complexity = b.complexity;      report.complexity_lambda = b.complexity_lambda; +    report.counters = results.counters;    }    return report;  } @@ -272,6 +275,7 @@ void RunInThread(const benchmark::internal::Benchmark::Instance* b,      results.bytes_processed += st.bytes_processed();      results.items_processed += st.items_processed();      results.complexity_n += st.complexity_length_n(); +    internal::Increment(&results.counters, st.counters);    }    manager->NotifyThreadComplete();  } @@ -281,7 +285,8 @@ std::vector<BenchmarkReporter::Run> RunBenchmark(      std::vector<BenchmarkReporter::Run>* complexity_reports) {    std::vector<BenchmarkReporter::Run> reports;  // return value -  size_t iters = 1; +  const bool has_explicit_iteration_count = b.iterations != 0; +  size_t iters = has_explicit_iteration_count ? b.iterations : 1;    std::unique_ptr<internal::ThreadManager> manager;    std::vector<std::thread> pool(b.threads - 1);    const int repeats = @@ -291,7 +296,7 @@ std::vector<BenchmarkReporter::Run> RunBenchmark(        (b.report_mode == internal::RM_Unspecified             ? FLAGS_benchmark_report_aggregates_only             : b.report_mode == internal::RM_ReportAggregatesOnly); -  for (int i = 0; i < repeats; i++) { +  for (int repetition_num = 0; repetition_num < repeats; repetition_num++) {      for (;;) {        // Try benchmark        VLOG(2) << "Running " << b.name << " for " << iters << "\n"; @@ -327,10 +332,20 @@ std::vector<BenchmarkReporter::Run> RunBenchmark(        const double min_time =            !IsZero(b.min_time) ? b.min_time : FLAGS_benchmark_min_time; -      // If this was the first run, was elapsed time or cpu time large enough? -      // If this is not the first run, go with the current value of iter. -      if ((i > 0) || results.has_error_ || (iters >= kMaxIterations) || -          (seconds >= min_time) || (results.real_time_used >= 5 * min_time)) { + +      // Determine if this run should be reported; Either it has +      // run for a sufficient amount of time or because an error was reported. +      const bool should_report =  repetition_num > 0 +        || has_explicit_iteration_count // An exact iteration count was requested +        || results.has_error_ +        || iters >= kMaxIterations +        || seconds >= min_time // the elapsed time is large enough +        // CPU time is specified but the elapsed real time greatly exceeds the +        // minimum time. Note that user provided timers are except from this +        // sanity check. +        || ((results.real_time_used >= 5 * min_time) && !b.use_manual_time); + +      if (should_report) {          BenchmarkReporter::Run report =              CreateRunReport(b, results, iters, seconds);          if (!report.error_occurred && b.complexity != oNone) @@ -386,6 +401,7 @@ State::State(size_t max_iters, const std::vector<int>& ranges, int thread_i,        items_processed_(0),        complexity_n_(0),        error_occurred_(false), +      counters(),        thread_index(thread_i),        threads(n_threads),        max_iterations(max_iters), @@ -634,7 +650,7 @@ void ParseCommandLineFlags(int* argc, char** argv) {          // TODO: Remove this.          ParseStringFlag(argv[i], "color_print", &FLAGS_benchmark_color) ||          ParseInt32Flag(argv[i], "v", &FLAGS_v)) { -      for (int j = i; j != *argc; ++j) argv[j] = argv[j + 1]; +      for (int j = i; j != *argc - 1; ++j) argv[j] = argv[j + 1];        --(*argc);        --i; @@ -664,4 +680,11 @@ void Initialize(int* argc, char** argv) {    internal::LogLevel() = FLAGS_v;  } +bool ReportUnrecognizedArguments(int argc, char** argv) { +  for (int i = 1; i < argc; ++i) { +    fprintf(stderr, "%s: error: unrecognized command-line flag: %s\n", argv[0], argv[i]); +  } +  return argc > 1; +} +  }  // end namespace benchmark diff --git a/utils/google-benchmark/src/benchmark_api_internal.h b/utils/google-benchmark/src/benchmark_api_internal.h index 8b97ce600873..828ed12165ff 100644 --- a/utils/google-benchmark/src/benchmark_api_internal.h +++ b/utils/google-benchmark/src/benchmark_api_internal.h @@ -24,9 +24,11 @@ struct Benchmark::Instance {    bool use_manual_time;    BigO complexity;    BigOFunc* complexity_lambda; +  UserCounters counters;    bool last_benchmark_instance;    int repetitions;    double min_time; +  size_t iterations;    int threads;  // Number of concurrent threads to us  }; diff --git a/utils/google-benchmark/src/benchmark_register.cc b/utils/google-benchmark/src/benchmark_register.cc index 4e580d8ea383..fe373204189f 100644 --- a/utils/google-benchmark/src/benchmark_register.cc +++ b/utils/google-benchmark/src/benchmark_register.cc @@ -143,6 +143,7 @@ bool BenchmarkFamilies::FindBenchmarks(          instance.time_unit = family->time_unit_;          instance.range_multiplier = family->range_multiplier_;          instance.min_time = family->min_time_; +        instance.iterations = family->iterations_;          instance.repetitions = family->repetitions_;          instance.use_real_time = family->use_real_time_;          instance.use_manual_time = family->use_manual_time_; @@ -163,16 +164,17 @@ bool BenchmarkFamilies::FindBenchmarks(              }            } -          AppendHumanReadable(arg, &instance.name); +          instance.name += std::to_string(arg);            ++arg_i;          } -        if (!IsZero(family->min_time_)) { +        if (!IsZero(family->min_time_))            instance.name += StringPrintF("/min_time:%0.3f", family->min_time_); -        } -        if (family->repetitions_ != 0) { +        if (family->iterations_ != 0) +          instance.name += StringPrintF("/iterations:%d", family->iterations_); +        if (family->repetitions_ != 0)            instance.name += StringPrintF("/repeats:%d", family->repetitions_); -        } +          if (family->use_manual_time_) {            instance.name += "/manual_time";          } else if (family->use_real_time_) { @@ -219,6 +221,7 @@ Benchmark::Benchmark(const char* name)        time_unit_(kNanosecond),        range_multiplier_(kRangeMultiplier),        min_time_(0), +      iterations_(0),        repetitions_(0),        use_real_time_(false),        use_manual_time_(false), @@ -344,6 +347,22 @@ Benchmark* Benchmark::RangeMultiplier(int multiplier) {    return this;  } + +Benchmark* Benchmark::MinTime(double t) { +  CHECK(t > 0.0); +  CHECK(iterations_ == 0); +  min_time_ = t; +  return this; +} + + +Benchmark* Benchmark::Iterations(size_t n) { +  CHECK(n > 0); +  CHECK(IsZero(min_time_)); +  iterations_ = n; +  return this; +} +  Benchmark* Benchmark::Repetitions(int n) {    CHECK(n > 0);    repetitions_ = n; @@ -355,12 +374,6 @@ Benchmark* Benchmark::ReportAggregatesOnly(bool value) {    return this;  } -Benchmark* Benchmark::MinTime(double t) { -  CHECK(t > 0.0); -  min_time_ = t; -  return this; -} -  Benchmark* Benchmark::UseRealTime() {    CHECK(!use_manual_time_)        << "Cannot set UseRealTime and UseManualTime simultaneously."; diff --git a/utils/google-benchmark/src/complexity.cc b/utils/google-benchmark/src/complexity.cc index dfab791a3293..02adbef62926 100644 --- a/utils/google-benchmark/src/complexity.cc +++ b/utils/google-benchmark/src/complexity.cc @@ -171,6 +171,22 @@ std::vector<BenchmarkReporter::Run> ComputeStats(    // All repetitions should be run with the same number of iterations so we    // can take this information from the first benchmark.    int64_t const run_iterations = reports.front().iterations; +  // create stats for user counters +  struct CounterStat { +    Counter c; +    Stat1_d s; +  }; +  std::map< std::string, CounterStat > counter_stats; +  for(Run const& r : reports) { +    for(auto const& cnt : r.counters) { +      auto it = counter_stats.find(cnt.first); +      if(it == counter_stats.end()) { +        counter_stats.insert({cnt.first, {cnt.second, Stat1_d{}}}); +      } else { +        CHECK_EQ(counter_stats[cnt.first].c.flags, cnt.second.flags); +      } +    } +  }    // Populate the accumulators.    for (Run const& run : reports) { @@ -183,6 +199,12 @@ std::vector<BenchmarkReporter::Run> ComputeStats(          Stat1_d(run.cpu_accumulated_time / run.iterations, run.iterations);      items_per_second_stat += Stat1_d(run.items_per_second, run.iterations);      bytes_per_second_stat += Stat1_d(run.bytes_per_second, run.iterations); +    // user counters +    for(auto const& cnt : run.counters) { +      auto it = counter_stats.find(cnt.first); +      CHECK_NE(it, counter_stats.end()); +      it->second.s += Stat1_d(cnt.second, run.iterations); +    }    }    // Get the data from the accumulator to BenchmarkReporter::Run's. @@ -196,6 +218,11 @@ std::vector<BenchmarkReporter::Run> ComputeStats(    mean_data.bytes_per_second = bytes_per_second_stat.Mean();    mean_data.items_per_second = items_per_second_stat.Mean();    mean_data.time_unit = reports[0].time_unit; +  // user counters +  for(auto const& kv : counter_stats) { +    auto c = Counter(kv.second.s.Mean(), counter_stats[kv.first].c.flags); +    mean_data.counters[kv.first] = c; +  }    // Only add label to mean/stddev if it is same for all runs    mean_data.report_label = reports[0].report_label; @@ -215,6 +242,11 @@ std::vector<BenchmarkReporter::Run> ComputeStats(    stddev_data.bytes_per_second = bytes_per_second_stat.StdDev();    stddev_data.items_per_second = items_per_second_stat.StdDev();    stddev_data.time_unit = reports[0].time_unit; +  // user counters +  for(auto const& kv : counter_stats) { +    auto c = Counter(kv.second.s.StdDev(), counter_stats[kv.first].c.flags); +    stddev_data.counters[kv.first] = c; +  }    results.push_back(mean_data);    results.push_back(stddev_data); @@ -263,6 +295,11 @@ std::vector<BenchmarkReporter::Run> ComputeBigO(    big_o.report_big_o = true;    big_o.complexity = result_cpu.complexity; +  // All the time results are reported after being multiplied by the +  // time unit multiplier. But since RMS is a relative quantity it +  // should not be multiplied at all. So, here, we _divide_ it by the +  // multiplier so that when it is multiplied later the result is the +  // correct one.    double multiplier = GetTimeUnitMultiplier(reports[0].time_unit);    // Only add label to mean/stddev if it is same for all runs @@ -275,6 +312,9 @@ std::vector<BenchmarkReporter::Run> ComputeBigO(    rms.cpu_accumulated_time = result_cpu.rms / multiplier;    rms.report_rms = true;    rms.complexity = result_cpu.complexity; +  // don't forget to keep the time unit, or we won't be able to +  // recover the correct value. +  rms.time_unit = reports[0].time_unit;    results.push_back(big_o);    results.push_back(rms); diff --git a/utils/google-benchmark/src/console_reporter.cc b/utils/google-benchmark/src/console_reporter.cc index 7e0cca3e3a0b..3f3de02945a3 100644 --- a/utils/google-benchmark/src/console_reporter.cc +++ b/utils/google-benchmark/src/console_reporter.cc @@ -14,6 +14,7 @@  #include "benchmark/reporter.h"  #include "complexity.h" +#include "counter.h"  #include <algorithm>  #include <cstdint> @@ -34,6 +35,7 @@ namespace benchmark {  bool ConsoleReporter::ReportContext(const Context& context) {    name_field_width_ = context.name_field_width; +  printed_header_ = false;    PrintBasicContext(&GetErrorStream(), context); @@ -45,16 +47,32 @@ bool ConsoleReporter::ReportContext(const Context& context) {      color_output_ = false;    }  #endif + +  return true; +} + +void ConsoleReporter::PrintHeader(const Run& run) {    std::string str =        FormatString("%-*s %13s %13s %10s\n", static_cast<int>(name_field_width_),                     "Benchmark", "Time", "CPU", "Iterations"); -  GetOutputStream() << str << std::string(str.length() - 1, '-') << "\n"; - -  return true; +  if(!run.counters.empty()) { +    str += " UserCounters..."; +  } +  std::string line = std::string(str.length(), '-'); +  GetOutputStream() << line << "\n" << str << line << "\n";  }  void ConsoleReporter::ReportRuns(const std::vector<Run>& reports) { -  for (const auto& run : reports) PrintRunData(run); +  for (const auto& run : reports) { +    // print the header if none was printed yet +    if (!printed_header_) { +      printed_header_ = true; +      PrintHeader(run); +    } +    // As an alternative to printing the headers like this, we could sort +    // the benchmarks by header and then print like that. +    PrintRunData(run); +  }  }  static void IgnoreColorPrint(std::ostream& out, LogColor, const char* fmt, @@ -114,6 +132,11 @@ void ConsoleReporter::PrintRunData(const Run& result) {      printer(Out, COLOR_CYAN, "%10lld", result.iterations);    } +  for (auto& c : result.counters) { +    auto const& s = HumanReadableNumber(c.second.value); +    printer(Out, COLOR_DEFAULT, " %s=%s", c.first.c_str(), s.c_str()); +  } +    if (!rate.empty()) {      printer(Out, COLOR_DEFAULT, " %*s", 13, rate.c_str());    } diff --git a/utils/google-benchmark/src/counter.cc b/utils/google-benchmark/src/counter.cc new file mode 100644 index 000000000000..307863d3c4d6 --- /dev/null +++ b/utils/google-benchmark/src/counter.cc @@ -0,0 +1,68 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "counter.h" + +namespace benchmark { +namespace internal { + +double Finish(Counter const& c, double cpu_time, double num_threads) { +  double v = c.value; +  if (c.flags & Counter::kIsRate) { +    v /= cpu_time; +  } +  if (c.flags & Counter::kAvgThreads) { +    v /= num_threads; +  } +  return v; +} + +void Finish(UserCounters *l, double cpu_time, double num_threads) { +  for (auto &c : *l) { +    c.second = Finish(c.second, cpu_time, num_threads); +  } +} + +void Increment(UserCounters *l, UserCounters const& r) { +  // add counters present in both or just in *l +  for (auto &c : *l) { +    auto it = r.find(c.first); +    if (it != r.end()) { +      c.second = c.second + it->second; +    } +  } +  // add counters present in r, but not in *l +  for (auto const &tc : r) { +    auto it = l->find(tc.first); +    if (it == l->end()) { +      (*l)[tc.first] = tc.second; +    } +  } +} + +bool SameNames(UserCounters const& l, UserCounters const& r) { +  if (&l == &r) return true; +  if (l.size() != r.size()) { +    return false; +  } +  for (auto const& c : l) { +    if ( r.find(c.first) == r.end()) { +      return false; +    } +  } +  return true; +} + +} // end namespace internal +} // end namespace benchmark diff --git a/utils/google-benchmark/src/counter.h b/utils/google-benchmark/src/counter.h new file mode 100644 index 000000000000..bbb92d9a2983 --- /dev/null +++ b/utils/google-benchmark/src/counter.h @@ -0,0 +1,26 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +//     http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "benchmark/benchmark_api.h" + +namespace benchmark { + +// these counter-related functions are hidden to reduce API surface. +namespace internal { +void Finish(UserCounters *l, double time, double num_threads); +void Increment(UserCounters *l, UserCounters const& r); +bool SameNames(UserCounters const& l, UserCounters const& r); +} // end namespace internal + +} //end namespace benchmark diff --git a/utils/google-benchmark/src/csv_reporter.cc b/utils/google-benchmark/src/csv_reporter.cc index 18ab3b668951..6779815b3c37 100644 --- a/utils/google-benchmark/src/csv_reporter.cc +++ b/utils/google-benchmark/src/csv_reporter.cc @@ -24,6 +24,7 @@  #include "string_util.h"  #include "timers.h" +#include "check.h"  // File format reference: http://edoceo.com/utilitas/csv-file-format. @@ -38,21 +39,51 @@ std::vector<std::string> elements = {  bool CSVReporter::ReportContext(const Context& context) {    PrintBasicContext(&GetErrorStream(), context); +  return true; +} +void CSVReporter::ReportRuns(const std::vector<Run> & reports) {    std::ostream& Out = GetOutputStream(); -  for (auto B = elements.begin(); B != elements.end();) { -    Out << *B++; -    if (B != elements.end()) Out << ","; + +  if (!printed_header_) { +    // save the names of all the user counters +    for (const auto& run : reports) { +      for (const auto& cnt : run.counters) { +        user_counter_names_.insert(cnt.first); +      } +    } + +    // print the header +    for (auto B = elements.begin(); B != elements.end();) { +      Out << *B++; +      if (B != elements.end()) Out << ","; +    } +    for (auto B = user_counter_names_.begin(); B != user_counter_names_.end();) { +      Out << ",\"" << *B++ << "\""; +    } +    Out << "\n"; + +    printed_header_ = true; +  } else { +    // check that all the current counters are saved in the name set +    for (const auto& run : reports) { +      for (const auto& cnt : run.counters) { +        CHECK(user_counter_names_.find(cnt.first) != user_counter_names_.end()) +              << "All counters must be present in each run. " +              << "Counter named \"" << cnt.first +              << "\" was not in a run after being added to the header"; +      } +    } +  } + +  // print results for each run +  for (const auto& run : reports) { +    PrintRunData(run);    } -  Out << "\n"; -  return true; -} -void CSVReporter::ReportRuns(const std::vector<Run>& reports) { -  for (const auto& run : reports) PrintRunData(run);  } -void CSVReporter::PrintRunData(const Run& run) { +void CSVReporter::PrintRunData(const Run & run) {    std::ostream& Out = GetOutputStream();    // Field with embedded double-quote characters must be doubled and the field @@ -102,6 +133,13 @@ void CSVReporter::PrintRunData(const Run& run) {      Out << "\"" << label << "\"";    }    Out << ",,";  // for error_occurred and error_message + +  // Print user counters +  for (const auto &ucn : user_counter_names_) { +    auto it = run.counters.find(ucn); +    CHECK(it != run.counters.end()); +    Out << "," << it->second; +  }    Out << '\n';  } diff --git a/utils/google-benchmark/src/cycleclock.h b/utils/google-benchmark/src/cycleclock.h index e4825d4ba244..e0f9b01f9d39 100644 --- a/utils/google-benchmark/src/cycleclock.h +++ b/utils/google-benchmark/src/cycleclock.h @@ -43,6 +43,11 @@ extern "C" uint64_t __rdtsc();  #ifndef BENCHMARK_OS_WINDOWS  #include <sys/time.h> +#include <time.h> +#endif + +#ifdef BENCHMARK_OS_EMSCRIPTEN +#include <emscripten.h>  #endif  namespace benchmark { @@ -65,6 +70,10 @@ inline BENCHMARK_ALWAYS_INLINE int64_t Now() {    // counter pauses; it does not continue counting, nor does it    // reset to zero.    return mach_absolute_time(); +#elif defined(BENCHMARK_OS_EMSCRIPTEN) +  // this goes above x86-specific code because old versions of Emscripten +  // define __x86_64__, although they have nothing to do with it. +  return static_cast<int64_t>(emscripten_get_now() * 1e+6);  #elif defined(__i386__)    int64_t ret;    __asm__ volatile("rdtsc" : "=A"(ret)); @@ -79,7 +88,7 @@ inline BENCHMARK_ALWAYS_INLINE int64_t Now() {    asm("mftbu %0" : "=r"(tbu0));    asm("mftb  %0" : "=r"(tbl));    asm("mftbu %0" : "=r"(tbu1)); -  tbl &= -static_cast<int64>(tbu0 == tbu1); +  tbl &= -static_cast<int64_t>(tbu0 == tbu1);    // high 32 bits in tbu1; low 32 bits in tbl  (tbu0 is garbage)    return (tbu1 << 32) | tbl;  #elif defined(__sparc__) @@ -99,6 +108,22 @@ inline BENCHMARK_ALWAYS_INLINE int64_t Now() {    _asm rdtsc  #elif defined(COMPILER_MSVC)    return __rdtsc(); +#elif defined(BENCHMARK_OS_NACL) +  // Native Client validator on x86/x86-64 allows RDTSC instructions, +  // and this case is handled above. Native Client validator on ARM +  // rejects MRC instructions (used in the ARM-specific sequence below), +  // so we handle it here. Portable Native Client compiles to +  // architecture-agnostic bytecode, which doesn't provide any +  // cycle counter access mnemonics. + +  // Native Client does not provide any API to access cycle counter. +  // Use clock_gettime(CLOCK_MONOTONIC, ...) instead of gettimeofday +  // because is provides nanosecond resolution (which is noticable at +  // least for PNaCl modules running on x86 Mac & Linux). +  // Initialize to always return 0 if clock_gettime fails. +  struct timespec ts = { 0, 0 }; +  clock_gettime(CLOCK_MONOTONIC, &ts); +  return static_cast<int64_t>(ts.tv_sec) * 1000000000 + ts.tv_nsec;  #elif defined(__aarch64__)    // System timer of ARMv8 runs at a different frequency than the CPU's.    // The frequency is fixed, typically in the range 1-50MHz.  It can be @@ -108,7 +133,9 @@ inline BENCHMARK_ALWAYS_INLINE int64_t Now() {    asm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value));    return virtual_timer_value;  #elif defined(__ARM_ARCH) -#if (__ARM_ARCH >= 6)  // V6 is the earliest arch that has a standard cyclecount +  // V6 is the earliest arch that has a standard cyclecount +  // Native Client validator doesn't allow MRC instructions. +#if (__ARM_ARCH >= 6)    uint32_t pmccntr;    uint32_t pmuseren;    uint32_t pmcntenset; diff --git a/utils/google-benchmark/src/internal_macros.h b/utils/google-benchmark/src/internal_macros.h index e8efcbb39c18..ab9dd85c1023 100644 --- a/utils/google-benchmark/src/internal_macros.h +++ b/utils/google-benchmark/src/internal_macros.h @@ -30,13 +30,26 @@  #elif defined(_WIN32)  #define BENCHMARK_OS_WINDOWS 1  #elif defined(__APPLE__) -// TODO(ericwf) This doesn't actually check that it is a Mac OSX system. Just -// that it is an apple system. -#define BENCHMARK_OS_MACOSX 1 +#include "TargetConditionals.h" +  #if defined(TARGET_OS_MAC) +    #define BENCHMARK_OS_MACOSX 1 +    #if defined(TARGET_OS_IPHONE) +      #define BENCHMARK_OS_IOS 1 +    #endif +  #endif  #elif defined(__FreeBSD__)  #define BENCHMARK_OS_FREEBSD 1  #elif defined(__linux__)  #define BENCHMARK_OS_LINUX 1 +#elif defined(__native_client__) +#define BENCHMARK_OS_NACL 1 +#elif defined(EMSCRIPTEN) +#define BENCHMARK_OS_EMSCRIPTEN 1 +#endif + +#if !__has_feature(cxx_exceptions) && !defined(__cpp_exceptions) \ +     && !defined(__EXCEPTIONS) +#define BENCHMARK_HAS_NO_EXCEPTIONS  #endif  #endif  // BENCHMARK_INTERNAL_MACROS_H_ diff --git a/utils/google-benchmark/src/json_reporter.cc b/utils/google-benchmark/src/json_reporter.cc index cea5f9bfa1c9..5a653088e5bf 100644 --- a/utils/google-benchmark/src/json_reporter.cc +++ b/utils/google-benchmark/src/json_reporter.cc @@ -154,10 +154,15 @@ void JSONReporter::PrintRunData(Run const& run) {          << indent          << FormatKV("items_per_second", RoundDouble(run.items_per_second));    } +  for(auto &c : run.counters) { +    out << ",\n" +        << indent +        << FormatKV(c.first, RoundDouble(c.second)); +  }    if (!run.report_label.empty()) {      out << ",\n" << indent << FormatKV("label", run.report_label);    }    out << '\n';  } -}  // end namespace benchmark +} // end namespace benchmark diff --git a/utils/google-benchmark/src/re.h b/utils/google-benchmark/src/re.h index af4a498c9f7a..01e9736505e5 100644 --- a/utils/google-benchmark/src/re.h +++ b/utils/google-benchmark/src/re.h @@ -15,6 +15,15 @@  #ifndef BENCHMARK_RE_H_  #define BENCHMARK_RE_H_ +#include "internal_macros.h" + +// Prefer C regex libraries when compiling w/o exceptions so that we can +// correctly report errors. +#if defined(BENCHMARK_HAS_NO_EXCEPTIONS) && defined(HAVE_STD_REGEX) && \ +    (defined(HAVE_GNU_POSIX_REGEX) || defined(HAVE_POSIX_REGEX)) +#undef HAVE_STD_REGEX +#endif +  #if defined(HAVE_STD_REGEX)  #include <regex>  #elif defined(HAVE_GNU_POSIX_REGEX) @@ -62,15 +71,20 @@ class Regex {  #if defined(HAVE_STD_REGEX)  inline bool Regex::Init(const std::string& spec, std::string* error) { +#ifdef BENCHMARK_HAS_NO_EXCEPTIONS +  ((void)error); // suppress unused warning +#else    try { +#endif      re_ = std::regex(spec, std::regex_constants::extended); -      init_ = true; +#ifndef BENCHMARK_HAS_NO_EXCEPTIONS    } catch (const std::regex_error& e) {      if (error) {        *error = e.what();      }    } +#endif    return init_;  } diff --git a/utils/google-benchmark/src/sleep.cc b/utils/google-benchmark/src/sleep.cc index 918abc485dc3..54aa04a42247 100644 --- a/utils/google-benchmark/src/sleep.cc +++ b/utils/google-benchmark/src/sleep.cc @@ -15,6 +15,7 @@  #include "sleep.h"  #include <cerrno> +#include <cstdlib>  #include <ctime>  #include "internal_macros.h" @@ -40,7 +41,7 @@ void SleepForMicroseconds(int microseconds) {  }  void SleepForMilliseconds(int milliseconds) { -  SleepForMicroseconds(static_cast<int>(milliseconds) * kNumMicrosPerMilli); +  SleepForMicroseconds(milliseconds * kNumMicrosPerMilli);  }  void SleepForSeconds(double seconds) { diff --git a/utils/google-benchmark/src/sleep.h b/utils/google-benchmark/src/sleep.h index f1e515ca4f3a..f98551afe284 100644 --- a/utils/google-benchmark/src/sleep.h +++ b/utils/google-benchmark/src/sleep.h @@ -1,14 +1,12 @@  #ifndef BENCHMARK_SLEEP_H_  #define BENCHMARK_SLEEP_H_ -#include <cstdint> -  namespace benchmark { -const int64_t kNumMillisPerSecond = 1000LL; -const int64_t kNumMicrosPerMilli = 1000LL; -const int64_t kNumMicrosPerSecond = kNumMillisPerSecond * 1000LL; -const int64_t kNumNanosPerMicro = 1000LL; -const int64_t kNumNanosPerSecond = kNumNanosPerMicro * kNumMicrosPerSecond; +const int kNumMillisPerSecond = 1000; +const int kNumMicrosPerMilli = 1000; +const int kNumMicrosPerSecond = kNumMillisPerSecond * 1000; +const int kNumNanosPerMicro = 1000; +const int kNumNanosPerSecond = kNumNanosPerMicro * kNumMicrosPerSecond;  void SleepForMilliseconds(int milliseconds);  void SleepForSeconds(double seconds); diff --git a/utils/google-benchmark/src/string_util.cc b/utils/google-benchmark/src/string_util.cc index 4cefbfba6c27..cd4e7cfde57c 100644 --- a/utils/google-benchmark/src/string_util.cc +++ b/utils/google-benchmark/src/string_util.cc @@ -45,6 +45,8 @@ void ToExponentAndMantissa(double val, double thresh, int precision,        std::max(thresh, 1.0 / std::pow(10.0, precision));    const double big_threshold = adjusted_threshold * one_k;    const double small_threshold = adjusted_threshold; +  // Values in ]simple_threshold,small_threshold[ will be printed as-is +  const double simple_threshold = 0.01;    if (val > big_threshold) {      // Positive powers @@ -62,14 +64,16 @@ void ToExponentAndMantissa(double val, double thresh, int precision,      *exponent = 0;    } else if (val < small_threshold) {      // Negative powers -    double scaled = val; -    for (size_t i = 0; i < arraysize(kSmallSIUnits); ++i) { -      scaled *= one_k; -      if (scaled >= small_threshold) { -        mantissa_stream << scaled; -        *exponent = -static_cast<int64_t>(i + 1); -        *mantissa = mantissa_stream.str(); -        return; +    if (val < simple_threshold) { +      double scaled = val; +      for (size_t i = 0; i < arraysize(kSmallSIUnits); ++i) { +        scaled *= one_k; +        if (scaled >= small_threshold) { +          mantissa_stream << scaled; +          *exponent = -static_cast<int64_t>(i + 1); +          *mantissa = mantissa_stream.str(); +          return; +        }        }      }      mantissa_stream << val; diff --git a/utils/google-benchmark/src/sysinfo.cc b/utils/google-benchmark/src/sysinfo.cc index dd1e66306e18..7feb79e65f20 100644 --- a/utils/google-benchmark/src/sysinfo.cc +++ b/utils/google-benchmark/src/sysinfo.cc @@ -75,7 +75,9 @@ bool ReadIntFromFile(const char* file, long* value) {      char line[1024];      char* err;      memset(line, '\0', sizeof(line)); -    CHECK(read(fd, line, sizeof(line) - 1)); +    ssize_t read_err = read(fd, line, sizeof(line) - 1); +    ((void)read_err); // prevent unused warning +    CHECK(read_err >= 0);      const long temp_value = strtol(line, &err, 10);      if (line[0] != '\0' && (*err == '\n' || *err == '\0')) {        *value = temp_value; @@ -295,8 +297,13 @@ void InitializeSystemInfo() {        (size == sizeof(cpu_freq))) {      cpuinfo_cycles_per_second = cpu_freq;    } else { +    #if defined BENCHMARK_OS_IOS +    fprintf(stderr, "CPU frequency cannot be detected. \n"); +    cpuinfo_cycles_per_second = 0; +    #else      fprintf(stderr, "%s\n", strerror(errno));      std::exit(EXIT_FAILURE); +    #endif    }  #else    // Generic cycles per second counter diff --git a/utils/google-benchmark/src/timers.cc b/utils/google-benchmark/src/timers.cc index fadc08f791e7..8d56e8adf8d4 100644 --- a/utils/google-benchmark/src/timers.cc +++ b/utils/google-benchmark/src/timers.cc @@ -35,6 +35,10 @@  #endif  #endif +#ifdef BENCHMARK_OS_EMSCRIPTEN +#include <emscripten.h> +#endif +  #include <cerrno>  #include <cstdint>  #include <cstdio> @@ -100,14 +104,7 @@ BENCHMARK_NORETURN static void DiagnoseAndExit(const char* msg) {  }  // end namespace  double ProcessCPUUsage() { -// FIXME We want to use clock_gettime, but its not available in MacOS 10.11. See -// https://github.com/google/benchmark/pull/292 -#if defined(CLOCK_PROCESS_CPUTIME_ID) && !defined(BENCHMARK_OS_MACOSX) -  struct timespec spec; -  if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &spec) == 0) -    return MakeTime(spec); -  DiagnoseAndExit("clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ...) failed"); -#elif defined(BENCHMARK_OS_WINDOWS) +#if defined(BENCHMARK_OS_WINDOWS)    HANDLE proc = GetCurrentProcess();    FILETIME creation_time;    FILETIME exit_time; @@ -117,21 +114,28 @@ double ProcessCPUUsage() {                        &user_time))      return MakeTime(kernel_time, user_time);    DiagnoseAndExit("GetProccessTimes() failed"); +#elif defined(BENCHMARK_OS_EMSCRIPTEN) +  // clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ...) returns 0 on Emscripten. +  // Use Emscripten-specific API. Reported CPU time would be exactly the +  // same as total time, but this is ok because there aren't long-latency +  // syncronous system calls in Emscripten. +  return emscripten_get_now() * 1e-3; +#elif defined(CLOCK_PROCESS_CPUTIME_ID) && !defined(BENCHMARK_OS_MACOSX) +  // FIXME We want to use clock_gettime, but its not available in MacOS 10.11. See +  // https://github.com/google/benchmark/pull/292 +  struct timespec spec; +  if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &spec) == 0) +    return MakeTime(spec); +  DiagnoseAndExit("clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ...) failed");  #else    struct rusage ru;    if (getrusage(RUSAGE_SELF, &ru) == 0) return MakeTime(ru); -  DiagnoseAndExit("clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ...) failed"); +  DiagnoseAndExit("getrusage(RUSAGE_SELF, ...) failed");  #endif  }  double ThreadCPUUsage() { -// FIXME We want to use clock_gettime, but its not available in MacOS 10.11. See -// https://github.com/google/benchmark/pull/292 -#if defined(CLOCK_THREAD_CPUTIME_ID) && !defined(BENCHMARK_OS_MACOSX) -  struct timespec ts; -  if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) == 0) return MakeTime(ts); -  DiagnoseAndExit("clock_gettime(CLOCK_THREAD_CPUTIME_ID, ...) failed"); -#elif defined(BENCHMARK_OS_WINDOWS) +#if defined(BENCHMARK_OS_WINDOWS)    HANDLE this_thread = GetCurrentThread();    FILETIME creation_time;    FILETIME exit_time; @@ -141,6 +145,8 @@ double ThreadCPUUsage() {                   &user_time);    return MakeTime(kernel_time, user_time);  #elif defined(BENCHMARK_OS_MACOSX) +  // FIXME We want to use clock_gettime, but its not available in MacOS 10.11. See +  // https://github.com/google/benchmark/pull/292    mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;    thread_basic_info_data_t info;    mach_port_t thread = pthread_mach_thread_np(pthread_self()); @@ -149,6 +155,13 @@ double ThreadCPUUsage() {      return MakeTime(info);    }    DiagnoseAndExit("ThreadCPUUsage() failed when evaluating thread_info"); +#elif defined(BENCHMARK_OS_EMSCRIPTEN) +  // Emscripten doesn't support traditional threads +  return ProcessCPUUsage(); +#elif defined(CLOCK_THREAD_CPUTIME_ID) +  struct timespec ts; +  if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) == 0) return MakeTime(ts); +  DiagnoseAndExit("clock_gettime(CLOCK_THREAD_CPUTIME_ID, ...) failed");  #else  #error Per-thread timing is not available on your system.  #endif  | 
