diff options
Diffstat (limited to 'lib/libcxx/utils/google-benchmark/src')
29 files changed, 1540 insertions, 749 deletions
diff --git a/lib/libcxx/utils/google-benchmark/src/CMakeLists.txt b/lib/libcxx/utils/google-benchmark/src/CMakeLists.txt index 7707773930c..977474f43f2 100644 --- a/lib/libcxx/utils/google-benchmark/src/CMakeLists.txt +++ b/lib/libcxx/utils/google-benchmark/src/CMakeLists.txt @@ -11,6 +11,10 @@ file(GLOB *.cc ${PROJECT_SOURCE_DIR}/include/benchmark/*.h ${CMAKE_CURRENT_SOURCE_DIR}/*.h) +file(GLOB BENCHMARK_MAIN "benchmark_main.cc") +foreach(item ${BENCHMARK_MAIN}) + list(REMOVE_ITEM SOURCE_FILES "${item}") +endforeach() add_library(benchmark ${SOURCE_FILES}) set_target_properties(benchmark PROPERTIES @@ -18,6 +22,9 @@ set_target_properties(benchmark PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} ) +target_include_directories(benchmark PUBLIC + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include> + ) # Link threads. target_link_libraries(benchmark ${BENCHMARK_CXX_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) @@ -31,45 +38,71 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") target_link_libraries(benchmark Shlwapi) endif() +# We need extra libraries on Solaris +if(${CMAKE_SYSTEM_NAME} MATCHES "SunOS") + target_link_libraries(benchmark kstat) +endif() + +# Benchmark main library +add_library(benchmark_main "benchmark_main.cc") +set_target_properties(benchmark_main PROPERTIES + OUTPUT_NAME "benchmark_main" + VERSION ${GENERIC_LIB_VERSION} + SOVERSION ${GENERIC_LIB_SOVERSION} +) +target_include_directories(benchmark PUBLIC + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include> + ) +target_link_libraries(benchmark_main benchmark) + set(include_install_dir "include") set(lib_install_dir "lib/") set(bin_install_dir "bin/") set(config_install_dir "lib/cmake/${PROJECT_NAME}") +set(pkgconfig_install_dir "lib/pkgconfig") 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(pkg_config "${generated_dir}/${PROJECT_NAME}.pc") 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 + "${version_config}" VERSION ${GENERIC_LIB_VERSION} COMPATIBILITY SameMajorVersion ) configure_file("${PROJECT_SOURCE_DIR}/cmake/Config.cmake.in" "${project_config}" @ONLY) +configure_file("${PROJECT_SOURCE_DIR}/cmake/benchmark.pc.in" "${pkg_config}" @ONLY) -# Install target (will install the library to specified CMAKE_INSTALL_PREFIX variable) -install( - TARGETS benchmark - 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_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}") +if (BENCHMARK_ENABLE_INSTALL) + # Install target (will install the library to specified CMAKE_INSTALL_PREFIX variable) + install( + TARGETS benchmark benchmark_main + 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_install_dir} + FILES_MATCHING PATTERN "*.*h") + + install( + FILES "${project_config}" "${version_config}" + DESTINATION "${config_install_dir}") + + install( + FILES "${pkg_config}" + DESTINATION "${pkgconfig_install_dir}") + + install( + EXPORT "${targets_export_name}" + NAMESPACE "${namespace}" + DESTINATION "${config_install_dir}") +endif() diff --git a/lib/libcxx/utils/google-benchmark/src/benchmark.cc b/lib/libcxx/utils/google-benchmark/src/benchmark.cc index 00ffa07ff56..b14bc629143 100644 --- a/lib/libcxx/utils/google-benchmark/src/benchmark.cc +++ b/lib/libcxx/utils/google-benchmark/src/benchmark.cc @@ -17,7 +17,9 @@ #include "internal_macros.h" #ifndef BENCHMARK_OS_WINDOWS +#ifndef BENCHMARK_OS_FUCHSIA #include <sys/resource.h> +#endif #include <sys/time.h> #include <unistd.h> #endif @@ -27,10 +29,10 @@ #include <condition_variable> #include <cstdio> #include <cstdlib> -#include <cstring> #include <fstream> #include <iostream> #include <memory> +#include <string> #include <thread> #include "check.h" @@ -38,13 +40,14 @@ #include "commandlineflags.h" #include "complexity.h" #include "counter.h" +#include "internal_macros.h" #include "log.h" #include "mutex.h" #include "re.h" -#include "stat.h" +#include "statistics.h" #include "string_util.h" -#include "sysinfo.h" -#include "timers.h" +#include "thread_manager.h" +#include "thread_timer.h" DEFINE_bool(benchmark_list_tests, false, "Print a list of benchmarks. This option overrides all other " @@ -82,7 +85,7 @@ DEFINE_string(benchmark_out_format, "json", "The format to use for file output. Valid values are " "'console', 'json', or 'csv'."); -DEFINE_string(benchmark_out, "", "The file to write additonal output to"); +DEFINE_string(benchmark_out, "", "The file to write additional output to"); DEFINE_string(benchmark_color, "auto", "Whether to use colors in the output. Valid values: " @@ -91,134 +94,28 @@ DEFINE_string(benchmark_color, "auto", "environment variable is set to a terminal type that supports " "colors."); +DEFINE_bool(benchmark_counters_tabular, false, + "Whether to use tabular format when printing user counters to " + "the console. Valid values: 'true'/'yes'/1, 'false'/'no'/0." + "Defaults to false."); + DEFINE_int32(v, 0, "The level of verbose logging to output"); namespace benchmark { -namespace internal { - -void UseCharPointer(char const volatile*) {} - -} // end namespace internal namespace { - static const size_t kMaxIterations = 1000000000; - } // end namespace namespace internal { -class ThreadManager { - public: - ThreadManager(int num_threads) - : alive_threads_(num_threads), start_stop_barrier_(num_threads) {} - - Mutex& GetBenchmarkMutex() const RETURN_CAPABILITY(benchmark_mutex_) { - return benchmark_mutex_; - } - - bool StartStopBarrier() EXCLUDES(end_cond_mutex_) { - return start_stop_barrier_.wait(); - } - - void NotifyThreadComplete() EXCLUDES(end_cond_mutex_) { - start_stop_barrier_.removeThread(); - if (--alive_threads_ == 0) { - MutexLock lock(end_cond_mutex_); - end_condition_.notify_all(); - } - } - - void WaitForAllThreads() EXCLUDES(end_cond_mutex_) { - MutexLock lock(end_cond_mutex_); - end_condition_.wait(lock.native_handle(), - [this]() { return alive_threads_ == 0; }); - } - - public: - struct Result { - double real_time_used = 0; - double cpu_time_used = 0; - double manual_time_used = 0; - int64_t bytes_processed = 0; - int64_t items_processed = 0; - int complexity_n = 0; - std::string report_label_; - std::string error_message_; - bool has_error_ = false; - UserCounters counters; - }; - GUARDED_BY(GetBenchmarkMutex()) Result results; - - private: - mutable Mutex benchmark_mutex_; - std::atomic<int> alive_threads_; - Barrier start_stop_barrier_; - Mutex end_cond_mutex_; - Condition end_condition_; -}; - -// Timer management class -class ThreadTimer { - public: - ThreadTimer() = default; - - // Called by each thread - void StartTimer() { - running_ = true; - start_real_time_ = ChronoClockNow(); - start_cpu_time_ = ThreadCPUUsage(); - } - - // Called by each thread - void StopTimer() { - CHECK(running_); - running_ = false; - real_time_used_ += ChronoClockNow() - start_real_time_; - cpu_time_used_ += ThreadCPUUsage() - start_cpu_time_; - } - - // Called by each thread - void SetIterationTime(double seconds) { manual_time_used_ += seconds; } - - bool running() const { return running_; } - - // REQUIRES: timer is not running - double real_time_used() { - CHECK(!running_); - return real_time_used_; - } - - // REQUIRES: timer is not running - double cpu_time_used() { - CHECK(!running_); - return cpu_time_used_; - } - - // REQUIRES: timer is not running - double manual_time_used() { - CHECK(!running_); - return manual_time_used_; - } - - private: - bool running_ = false; // Is the timer running - double start_real_time_ = 0; // If running_ - double start_cpu_time_ = 0; // If running_ - - // Accumulated time so far (does not contain current slice if running_) - double real_time_used_ = 0; - double cpu_time_used_ = 0; - // Manually set iteration time. User sets this with SetIterationTime(seconds). - double manual_time_used_ = 0; -}; +void UseCharPointer(char const volatile*) {} namespace { BenchmarkReporter::Run CreateRunReport( const benchmark::internal::Benchmark::Instance& b, - const internal::ThreadManager::Result& results, size_t iters, - double seconds) { + const internal::ThreadManager::Result& results, double seconds) { // Create report about this benchmark run. BenchmarkReporter::Run report; @@ -226,8 +123,8 @@ BenchmarkReporter::Run CreateRunReport( report.error_occurred = results.has_error_; report.error_message = results.error_message_; report.report_label = results.report_label_; - // Report the total iterations across all threads. - report.iterations = static_cast<int64_t>(iters) * b.threads; + // This is the total iterations across all threads. + report.iterations = results.iterations; report.time_unit = b.time_unit; if (!report.error_occurred) { @@ -251,7 +148,9 @@ BenchmarkReporter::Run CreateRunReport( report.complexity_n = results.complexity_n; report.complexity = b.complexity; report.complexity_lambda = b.complexity_lambda; + report.statistics = b.statistics; report.counters = results.counters; + internal::Finish(&report.counters, results.iterations, seconds, b.threads); } return report; } @@ -264,11 +163,12 @@ void RunInThread(const benchmark::internal::Benchmark::Instance* b, internal::ThreadTimer timer; State st(iters, b->arg, thread_id, b->threads, &timer, manager); b->benchmark->Run(st); - CHECK(st.iterations() == st.max_iterations) + CHECK(st.iterations() >= st.max_iterations) << "Benchmark returned before State::KeepRunning() returned false!"; { MutexLock l(manager->GetBenchmarkMutex()); internal::ThreadManager::Result& results = manager->results; + results.iterations += st.iterations(); results.cpu_time_used += timer.cpu_time_used(); results.real_time_used += timer.real_time_used(); results.manual_time_used += timer.manual_time_used(); @@ -333,21 +233,23 @@ std::vector<BenchmarkReporter::Run> RunBenchmark( const double min_time = !IsZero(b.min_time) ? b.min_time : FLAGS_benchmark_min_time; + // clang-format off + // turn off clang-format since it mangles prettiness here // 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 + || has_explicit_iteration_count // An exact iteration count was requested || results.has_error_ - || iters >= kMaxIterations - || seconds >= min_time // the elapsed time is large enough + || iters >= kMaxIterations // No chance to try again, we hit the limit. + || 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); + // clang-format on if (should_report) { - BenchmarkReporter::Run report = - CreateRunReport(b, results, iters, seconds); + BenchmarkReporter::Run report = CreateRunReport(b, results, seconds); if (!report.error_occurred && b.complexity != oNone) complexity_reports->push_back(report); reports.push_back(report); @@ -390,25 +292,50 @@ std::vector<BenchmarkReporter::Run> RunBenchmark( } // namespace } // namespace internal -State::State(size_t max_iters, const std::vector<int>& ranges, int thread_i, +State::State(size_t max_iters, const std::vector<int64_t>& ranges, int thread_i, int n_threads, internal::ThreadTimer* timer, internal::ThreadManager* manager) - : started_(false), + : total_iterations_(0), + batch_leftover_(0), + max_iterations(max_iters), + started_(false), finished_(false), - total_iterations_(0), + error_occurred_(false), range_(ranges), bytes_processed_(0), items_processed_(0), complexity_n_(0), - error_occurred_(false), counters(), thread_index(thread_i), threads(n_threads), - max_iterations(max_iters), timer_(timer), manager_(manager) { CHECK(max_iterations != 0) << "At least one iteration must be run"; CHECK_LT(thread_index, threads) << "thread_index must be less than threads"; + + // Note: The use of offsetof below is technically undefined until C++17 + // because State is not a standard layout type. However, all compilers + // currently provide well-defined behavior as an extension (which is + // demonstrated since constexpr evaluation must diagnose all undefined + // behavior). However, GCC and Clang also warn about this use of offsetof, + // which must be suppressed. +#if defined(__INTEL_COMPILER) +#pragma warning push +#pragma warning(disable:1875) +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Winvalid-offsetof" +#endif + // Offset tests to ensure commonly accessed data is on the first cache line. + const int cache_line_size = 64; + static_assert(offsetof(State, error_occurred_) <= + (cache_line_size - sizeof(error_occurred_)), + ""); +#if defined(__INTEL_COMPILER) +#pragma warning pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif } void State::PauseTiming() { @@ -432,7 +359,7 @@ void State::SkipWithError(const char* msg) { manager_->results.has_error_ = true; } } - total_iterations_ = max_iterations; + total_iterations_ = 0; if (timer_->running()) timer_->StopTimer(); } @@ -448,6 +375,7 @@ void State::SetLabel(const char* label) { void State::StartKeepRunning() { CHECK(!started_ && !finished_); started_ = true; + total_iterations_ = error_occurred_ ? 0 : max_iterations; manager_->StartStopBarrier(); if (!error_occurred_) ResumeTiming(); } @@ -457,8 +385,8 @@ void State::FinishKeepRunning() { if (!error_occurred_) { PauseTiming(); } - // Total iterations now is one greater than max iterations. Fix this. - total_iterations_ = max_iterations; + // Total iterations has now wrapped around past 0. Fix this. + total_iterations_ = 0; finished_ = true; manager_->StartStopBarrier(); } @@ -467,30 +395,30 @@ namespace internal { namespace { void RunBenchmarks(const std::vector<Benchmark::Instance>& benchmarks, - BenchmarkReporter* console_reporter, - BenchmarkReporter* file_reporter) { + BenchmarkReporter* console_reporter, + BenchmarkReporter* file_reporter) { // Note the file_reporter can be null. CHECK(console_reporter != nullptr); // Determine the width of the name field using a minimum width of 10. bool has_repetitions = FLAGS_benchmark_repetitions > 1; size_t name_field_width = 10; + size_t stat_field_width = 0; for (const Benchmark::Instance& benchmark : benchmarks) { name_field_width = std::max<size_t>(name_field_width, benchmark.name.size()); has_repetitions |= benchmark.repetitions > 1; + + for (const auto& Stat : *benchmark.statistics) + stat_field_width = std::max<size_t>(stat_field_width, Stat.name_.size()); } - if (has_repetitions) name_field_width += std::strlen("_stddev"); + if (has_repetitions) name_field_width += 1 + stat_field_width; // Print header here BenchmarkReporter::Context context; - context.num_cpus = NumCPUs(); - context.mhz_per_cpu = CyclesPerSecond() / 1000000.0f; - - context.cpu_scaling_enabled = CpuScalingEnabled(); context.name_field_width = name_field_width; - // Keep track of runing times of all instances of current benchmark + // Keep track of running times of all instances of current benchmark std::vector<BenchmarkReporter::Run> complexity_reports; // We flush streams after invoking reporter methods that write to them. This @@ -521,10 +449,10 @@ void RunBenchmarks(const std::vector<Benchmark::Instance>& benchmarks, } std::unique_ptr<BenchmarkReporter> CreateReporter( - std::string const& name, ConsoleReporter::OutputOptions allow_color) { + std::string const& name, ConsoleReporter::OutputOptions output_opts) { typedef std::unique_ptr<BenchmarkReporter> PtrType; if (name == "console") { - return PtrType(new ConsoleReporter(allow_color)); + return PtrType(new ConsoleReporter(output_opts)); } else if (name == "json") { return PtrType(new JSONReporter); } else if (name == "csv") { @@ -536,6 +464,30 @@ std::unique_ptr<BenchmarkReporter> CreateReporter( } } // end namespace + +bool IsZero(double n) { + return std::abs(n) < std::numeric_limits<double>::epsilon(); +} + +ConsoleReporter::OutputOptions GetOutputOptions(bool force_no_color) { + int output_opts = ConsoleReporter::OO_Defaults; + if ((FLAGS_benchmark_color == "auto" && IsColorTerminal()) || + IsTruthyFlagValue(FLAGS_benchmark_color)) { + output_opts |= ConsoleReporter::OO_Color; + } else { + output_opts &= ~ConsoleReporter::OO_Color; + } + if (force_no_color) { + output_opts &= ~ConsoleReporter::OO_Color; + } + if (FLAGS_benchmark_counters_tabular) { + output_opts |= ConsoleReporter::OO_Tabular; + } else { + output_opts &= ~ConsoleReporter::OO_Tabular; + } + return static_cast<ConsoleReporter::OutputOptions>(output_opts); +} + } // end namespace internal size_t RunSpecifiedBenchmarks() { @@ -557,29 +509,21 @@ size_t RunSpecifiedBenchmarks(BenchmarkReporter* console_reporter, std::unique_ptr<BenchmarkReporter> default_console_reporter; std::unique_ptr<BenchmarkReporter> default_file_reporter; if (!console_reporter) { - auto output_opts = ConsoleReporter::OO_None; - if (FLAGS_benchmark_color == "auto") - output_opts = IsColorTerminal() ? ConsoleReporter::OO_Color - : ConsoleReporter::OO_None; - else - output_opts = IsTruthyFlagValue(FLAGS_benchmark_color) - ? ConsoleReporter::OO_Color - : ConsoleReporter::OO_None; - default_console_reporter = - internal::CreateReporter(FLAGS_benchmark_format, output_opts); + default_console_reporter = internal::CreateReporter( + FLAGS_benchmark_format, internal::GetOutputOptions()); console_reporter = default_console_reporter.get(); } auto& Out = console_reporter->GetOutputStream(); auto& Err = console_reporter->GetErrorStream(); std::string const& fname = FLAGS_benchmark_out; - if (fname == "" && file_reporter) { + if (fname.empty() && file_reporter) { Err << "A custom file reporter was provided but " "--benchmark_out=<file> was not specified." << std::endl; std::exit(1); } - if (fname != "") { + if (!fname.empty()) { output_file.open(fname); if (!output_file.is_open()) { Err << "invalid file name: '" << fname << std::endl; @@ -625,12 +569,15 @@ void PrintUsageAndExit() { " [--benchmark_out=<filename>]\n" " [--benchmark_out_format=<json|console|csv>]\n" " [--benchmark_color={auto|true|false}]\n" + " [--benchmark_counters_tabular={true|false}]\n" " [--v=<verbosity>]\n"); exit(0); } void ParseCommandLineFlags(int* argc, char** argv) { using namespace benchmark; + BenchmarkReporter::Context::executable_name = + (argc && *argc > 0) ? argv[0] : "unknown"; for (int i = 1; i < *argc; ++i) { if (ParseBoolFlag(argv[i], "benchmark_list_tests", &FLAGS_benchmark_list_tests) || @@ -649,6 +596,8 @@ void ParseCommandLineFlags(int* argc, char** argv) { // "color_print" is the deprecated name for "benchmark_color". // TODO: Remove this. ParseStringFlag(argv[i], "color_print", &FLAGS_benchmark_color) || + ParseBoolFlag(argv[i], "benchmark_counters_tabular", + &FLAGS_benchmark_counters_tabular) || ParseInt32Flag(argv[i], "v", &FLAGS_v)) { for (int j = i; j != *argc - 1; ++j) argv[j] = argv[j + 1]; @@ -682,7 +631,8 @@ void Initialize(int* argc, char** argv) { 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]); + fprintf(stderr, "%s: error: unrecognized command-line flag: %s\n", argv[0], + argv[i]); } return argc > 1; } diff --git a/lib/libcxx/utils/google-benchmark/src/benchmark_api_internal.h b/lib/libcxx/utils/google-benchmark/src/benchmark_api_internal.h index 828ed12165f..dd7a3ffe8cb 100644 --- a/lib/libcxx/utils/google-benchmark/src/benchmark_api_internal.h +++ b/lib/libcxx/utils/google-benchmark/src/benchmark_api_internal.h @@ -1,7 +1,7 @@ #ifndef BENCHMARK_API_INTERNAL_H #define BENCHMARK_API_INTERNAL_H -#include "benchmark/benchmark_api.h" +#include "benchmark/benchmark.h" #include <cmath> #include <iosfwd> @@ -17,7 +17,7 @@ struct Benchmark::Instance { std::string name; Benchmark* benchmark; ReportMode report_mode; - std::vector<int> arg; + std::vector<int64_t> arg; TimeUnit time_unit; int range_multiplier; bool use_real_time; @@ -25,6 +25,7 @@ struct Benchmark::Instance { BigO complexity; BigOFunc* complexity_lambda; UserCounters counters; + const std::vector<Statistics>* statistics; bool last_benchmark_instance; int repetitions; double min_time; @@ -36,13 +37,10 @@ bool FindBenchmarksInternal(const std::string& re, std::vector<Benchmark::Instance>* benchmarks, std::ostream* Err); -namespace { +bool IsZero(double n); -bool IsZero(double n) { - return std::abs(n) < std::numeric_limits<double>::epsilon(); -} +ConsoleReporter::OutputOptions GetOutputOptions(bool force_no_color = false); -} // end namespace } // end namespace internal } // end namespace benchmark diff --git a/lib/libcxx/utils/google-benchmark/src/benchmark_main.cc b/lib/libcxx/utils/google-benchmark/src/benchmark_main.cc new file mode 100644 index 00000000000..b3b24783149 --- /dev/null +++ b/lib/libcxx/utils/google-benchmark/src/benchmark_main.cc @@ -0,0 +1,17 @@ +// Copyright 2018 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.h" + +BENCHMARK_MAIN(); diff --git a/lib/libcxx/utils/google-benchmark/src/benchmark_register.cc b/lib/libcxx/utils/google-benchmark/src/benchmark_register.cc index fe373204189..26a89721c78 100644 --- a/lib/libcxx/utils/google-benchmark/src/benchmark_register.cc +++ b/lib/libcxx/utils/google-benchmark/src/benchmark_register.cc @@ -12,12 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "benchmark/benchmark.h" -#include "benchmark_api_internal.h" -#include "internal_macros.h" +#include "benchmark_register.h" #ifndef BENCHMARK_OS_WINDOWS +#ifndef BENCHMARK_OS_FUCHSIA #include <sys/resource.h> +#endif #include <sys/time.h> #include <unistd.h> #endif @@ -31,17 +31,20 @@ #include <fstream> #include <iostream> #include <memory> +#include <sstream> #include <thread> +#include "benchmark/benchmark.h" +#include "benchmark_api_internal.h" #include "check.h" #include "commandlineflags.h" #include "complexity.h" +#include "internal_macros.h" #include "log.h" #include "mutex.h" #include "re.h" -#include "stat.h" +#include "statistics.h" #include "string_util.h" -#include "sysinfo.h" #include "timers.h" namespace benchmark { @@ -69,9 +72,12 @@ class BenchmarkFamilies { // Registers a benchmark family and returns the index assigned to it. size_t AddBenchmark(std::unique_ptr<Benchmark> family); + // Clear all registered benchmark families. + void ClearBenchmarks(); + // Extract the list of benchmark instances that match the specified // regular expression. - bool FindBenchmarks(const std::string& re, + bool FindBenchmarks(std::string re, std::vector<Benchmark::Instance>* benchmarks, std::ostream* Err); @@ -94,14 +100,25 @@ size_t BenchmarkFamilies::AddBenchmark(std::unique_ptr<Benchmark> family) { return index; } +void BenchmarkFamilies::ClearBenchmarks() { + MutexLock l(mutex_); + families_.clear(); + families_.shrink_to_fit(); +} + bool BenchmarkFamilies::FindBenchmarks( - const std::string& spec, std::vector<Benchmark::Instance>* benchmarks, + std::string spec, std::vector<Benchmark::Instance>* benchmarks, std::ostream* ErrStream) { CHECK(ErrStream); auto& Err = *ErrStream; // Make regular expression out of command-line flag std::string error_msg; Regex re; + bool isNegativeFilter = false; + if (spec[0] == '-') { + spec.replace(0, 1, ""); + isNegativeFilter = true; + } if (!re.Init(spec, &error_msg)) { Err << "Could not compile benchmark re: " << error_msg << std::endl; return false; @@ -149,6 +166,7 @@ bool BenchmarkFamilies::FindBenchmarks( instance.use_manual_time = family->use_manual_time_; instance.complexity = family->complexity_; instance.complexity_lambda = family->complexity_lambda_; + instance.statistics = &family->statistics_; instance.threads = num_threads; // Add arguments to instance name @@ -160,20 +178,20 @@ bool BenchmarkFamilies::FindBenchmarks( const auto& arg_name = family->arg_names_[arg_i]; if (!arg_name.empty()) { instance.name += - StringPrintF("%s:", family->arg_names_[arg_i].c_str()); + StrFormat("%s:", family->arg_names_[arg_i].c_str()); } } - instance.name += std::to_string(arg); + instance.name += StrFormat("%d", arg); ++arg_i; } if (!IsZero(family->min_time_)) - instance.name += StringPrintF("/min_time:%0.3f", family->min_time_); + instance.name += StrFormat("/min_time:%0.3f", family->min_time_); if (family->iterations_ != 0) - instance.name += StringPrintF("/iterations:%d", family->iterations_); + instance.name += StrFormat("/iterations:%d", family->iterations_); if (family->repetitions_ != 0) - instance.name += StringPrintF("/repeats:%d", family->repetitions_); + instance.name += StrFormat("/repeats:%d", family->repetitions_); if (family->use_manual_time_) { instance.name += "/manual_time"; @@ -183,10 +201,11 @@ bool BenchmarkFamilies::FindBenchmarks( // Add the number of threads used to the name if (!family->thread_counts_.empty()) { - instance.name += StringPrintF("/threads:%d", instance.threads); + instance.name += StrFormat("/threads:%d", instance.threads); } - if (re.Match(instance.name)) { + if ((re.Match(instance.name) && !isNegativeFilter) || + (!re.Match(instance.name) && isNegativeFilter)) { instance.last_benchmark_instance = (&args == &family->args_.back()); benchmarks->push_back(std::move(instance)); } @@ -226,34 +245,15 @@ Benchmark::Benchmark(const char* name) use_real_time_(false), use_manual_time_(false), complexity_(oNone), - complexity_lambda_(nullptr) {} + complexity_lambda_(nullptr) { + ComputeStatistics("mean", StatisticsMean); + ComputeStatistics("median", StatisticsMedian); + ComputeStatistics("stddev", StatisticsStdDev); +} Benchmark::~Benchmark() {} -void Benchmark::AddRange(std::vector<int>* dst, int lo, int hi, int mult) { - CHECK_GE(lo, 0); - CHECK_GE(hi, lo); - CHECK_GE(mult, 2); - - // Add "lo" - dst->push_back(lo); - - static const int kint32max = std::numeric_limits<int32_t>::max(); - - // Now space out the benchmarks in multiples of "mult" - for (int32_t i = 1; i < kint32max / mult; i *= mult) { - if (i >= hi) break; - if (i > lo) { - dst->push_back(i); - } - } - // Add "hi" (if different from "lo") - if (hi != lo) { - dst->push_back(hi); - } -} - -Benchmark* Benchmark::Arg(int x) { +Benchmark* Benchmark::Arg(int64_t x) { CHECK(ArgsCnt() == -1 || ArgsCnt() == 1); args_.push_back({x}); return this; @@ -264,20 +264,21 @@ Benchmark* Benchmark::Unit(TimeUnit unit) { return this; } -Benchmark* Benchmark::Range(int start, int limit) { +Benchmark* Benchmark::Range(int64_t start, int64_t limit) { CHECK(ArgsCnt() == -1 || ArgsCnt() == 1); - std::vector<int> arglist; + std::vector<int64_t> arglist; AddRange(&arglist, start, limit, range_multiplier_); - for (int i : arglist) { + for (int64_t i : arglist) { args_.push_back({i}); } return this; } -Benchmark* Benchmark::Ranges(const std::vector<std::pair<int, int>>& ranges) { +Benchmark* Benchmark::Ranges( + const std::vector<std::pair<int64_t, int64_t>>& ranges) { CHECK(ArgsCnt() == -1 || ArgsCnt() == static_cast<int>(ranges.size())); - std::vector<std::vector<int>> arglists(ranges.size()); + std::vector<std::vector<int64_t>> arglists(ranges.size()); std::size_t total = 1; for (std::size_t i = 0; i < ranges.size(); i++) { AddRange(&arglists[i], ranges[i].first, ranges[i].second, @@ -288,7 +289,7 @@ Benchmark* Benchmark::Ranges(const std::vector<std::pair<int, int>>& ranges) { std::vector<std::size_t> ctr(arglists.size(), 0); for (std::size_t i = 0; i < total; i++) { - std::vector<int> tmp; + std::vector<int64_t> tmp; tmp.reserve(arglists.size()); for (std::size_t j = 0; j < arglists.size(); j++) { @@ -320,17 +321,17 @@ Benchmark* Benchmark::ArgNames(const std::vector<std::string>& names) { return this; } -Benchmark* Benchmark::DenseRange(int start, int limit, int step) { +Benchmark* Benchmark::DenseRange(int64_t start, int64_t limit, int step) { CHECK(ArgsCnt() == -1 || ArgsCnt() == 1); CHECK_GE(start, 0); CHECK_LE(start, limit); - for (int arg = start; arg <= limit; arg += step) { + for (int64_t arg = start; arg <= limit; arg += step) { args_.push_back({arg}); } return this; } -Benchmark* Benchmark::Args(const std::vector<int>& args) { +Benchmark* Benchmark::Args(const std::vector<int64_t>& args) { CHECK(ArgsCnt() == -1 || ArgsCnt() == static_cast<int>(args.size())); args_.push_back(args); return this; @@ -347,7 +348,6 @@ Benchmark* Benchmark::RangeMultiplier(int multiplier) { return this; } - Benchmark* Benchmark::MinTime(double t) { CHECK(t > 0.0); CHECK(iterations_ == 0); @@ -355,7 +355,6 @@ Benchmark* Benchmark::MinTime(double t) { return this; } - Benchmark* Benchmark::Iterations(size_t n) { CHECK(n > 0); CHECK(IsZero(min_time_)); @@ -399,6 +398,12 @@ Benchmark* Benchmark::Complexity(BigOFunc* complexity) { return this; } +Benchmark* Benchmark::ComputeStatistics(std::string name, + StatisticsFunc* statistics) { + statistics_.emplace_back(name, statistics); + return this; +} + Benchmark* Benchmark::Threads(int t) { CHECK_GT(t, 0); thread_counts_.push_back(t); @@ -427,8 +432,7 @@ Benchmark* Benchmark::DenseThreadRange(int min_threads, int max_threads, } Benchmark* Benchmark::ThreadPerCpu() { - static int num_cpus = NumCPUs(); - thread_counts_.push_back(num_cpus); + thread_counts_.push_back(CPUInfo::Get().num_cpus); return this; } @@ -449,4 +453,9 @@ int Benchmark::ArgsCnt() const { void FunctionBenchmark::Run(State& st) { func_(st); } } // end namespace internal + +void ClearRegisteredBenchmarks() { + internal::BenchmarkFamilies::GetInstance()->ClearBenchmarks(); +} + } // end namespace benchmark diff --git a/lib/libcxx/utils/google-benchmark/src/benchmark_register.h b/lib/libcxx/utils/google-benchmark/src/benchmark_register.h new file mode 100644 index 00000000000..0705e219f2f --- /dev/null +++ b/lib/libcxx/utils/google-benchmark/src/benchmark_register.h @@ -0,0 +1,33 @@ +#ifndef BENCHMARK_REGISTER_H +#define BENCHMARK_REGISTER_H + +#include <vector> + +#include "check.h" + +template <typename T> +void AddRange(std::vector<T>* dst, T lo, T hi, int mult) { + CHECK_GE(lo, 0); + CHECK_GE(hi, lo); + CHECK_GE(mult, 2); + + // Add "lo" + dst->push_back(lo); + + static const T kmax = std::numeric_limits<T>::max(); + + // Now space out the benchmarks in multiples of "mult" + for (T i = 1; i < kmax / mult; i *= mult) { + if (i >= hi) break; + if (i > lo) { + dst->push_back(i); + } + } + + // Add "hi" (if different from "lo") + if (hi != lo) { + dst->push_back(hi); + } +} + +#endif // BENCHMARK_REGISTER_H diff --git a/lib/libcxx/utils/google-benchmark/src/check.h b/lib/libcxx/utils/google-benchmark/src/check.h index 6f1fe0cf860..f5f8253f804 100644 --- a/lib/libcxx/utils/google-benchmark/src/check.h +++ b/lib/libcxx/utils/google-benchmark/src/check.h @@ -1,6 +1,7 @@ #ifndef CHECK_H_ #define CHECK_H_ +#include <cmath> #include <cstdlib> #include <ostream> @@ -61,6 +62,8 @@ class CheckHandler { #define CHECK(b) ::benchmark::internal::GetNullLogInstance() #endif +// clang-format off +// preserve whitespacing between operators for alignment #define CHECK_EQ(a, b) CHECK((a) == (b)) #define CHECK_NE(a, b) CHECK((a) != (b)) #define CHECK_GE(a, b) CHECK((a) >= (b)) @@ -68,4 +71,12 @@ class CheckHandler { #define CHECK_GT(a, b) CHECK((a) > (b)) #define CHECK_LT(a, b) CHECK((a) < (b)) +#define CHECK_FLOAT_EQ(a, b, eps) CHECK(std::fabs((a) - (b)) < (eps)) +#define CHECK_FLOAT_NE(a, b, eps) CHECK(std::fabs((a) - (b)) >= (eps)) +#define CHECK_FLOAT_GE(a, b, eps) CHECK((a) - (b) > -(eps)) +#define CHECK_FLOAT_LE(a, b, eps) CHECK((b) - (a) > -(eps)) +#define CHECK_FLOAT_GT(a, b, eps) CHECK((a) - (b) > (eps)) +#define CHECK_FLOAT_LT(a, b, eps) CHECK((b) - (a) > (eps)) +//clang-format on + #endif // CHECK_H_ diff --git a/lib/libcxx/utils/google-benchmark/src/colorprint.cc b/lib/libcxx/utils/google-benchmark/src/colorprint.cc index 513376b14b1..2dec4a8b28b 100644 --- a/lib/libcxx/utils/google-benchmark/src/colorprint.cc +++ b/lib/libcxx/utils/google-benchmark/src/colorprint.cc @@ -89,7 +89,7 @@ std::string FormatString(const char* msg, va_list args) { std::size_t size = 256; char local_buff[256]; - auto ret = std::vsnprintf(local_buff, size, msg, args_cp); + auto ret = vsnprintf(local_buff, size, msg, args_cp); va_end(args_cp); @@ -104,7 +104,7 @@ std::string FormatString(const char* msg, va_list args) { // we did not provide a long enough buffer on our first attempt. size = (size_t)ret + 1; // + 1 for the null byte std::unique_ptr<char[]> buff(new char[size]); - ret = std::vsnprintf(buff.get(), size, msg, args); + ret = vsnprintf(buff.get(), size, msg, args); CHECK(ret > 0 && ((size_t)ret) < size); return buff.get(); } diff --git a/lib/libcxx/utils/google-benchmark/src/commandlineflags.cc b/lib/libcxx/utils/google-benchmark/src/commandlineflags.cc index 72534e022a8..734e88bbec6 100644 --- a/lib/libcxx/utils/google-benchmark/src/commandlineflags.cc +++ b/lib/libcxx/utils/google-benchmark/src/commandlineflags.cc @@ -45,7 +45,7 @@ bool ParseInt32(const std::string& src_text, const char* str, int32_t* value) { // LONG_MAX or LONG_MIN when the input overflows.) result != long_value // The parsed value overflows as an Int32. - ) { + ) { std::cerr << src_text << " is expected to be a 32-bit integer, " << "but actually has value \"" << str << "\", " << "which overflows.\n"; @@ -209,9 +209,9 @@ bool IsFlag(const char* str, const char* flag) { return (ParseFlagValue(str, flag, true) != nullptr); } -bool IsTruthyFlagValue(const std::string& str) { - if (str.empty()) return true; - char ch = str[0]; +bool IsTruthyFlagValue(const std::string& value) { + if (value.empty()) return true; + char ch = value[0]; return isalnum(ch) && !(ch == '0' || ch == 'f' || ch == 'F' || ch == 'n' || ch == 'N'); } diff --git a/lib/libcxx/utils/google-benchmark/src/complexity.cc b/lib/libcxx/utils/google-benchmark/src/complexity.cc index 02adbef6292..aafd538df21 100644 --- a/lib/libcxx/utils/google-benchmark/src/complexity.cc +++ b/lib/libcxx/utils/google-benchmark/src/complexity.cc @@ -15,32 +15,34 @@ // Source project : https://github.com/ismaelJimenez/cpp.leastsq // Adapted to be used with google benchmark -#include "benchmark/benchmark_api.h" +#include "benchmark/benchmark.h" #include <algorithm> #include <cmath> #include "check.h" #include "complexity.h" -#include "stat.h" namespace benchmark { // Internal function to calculate the different scalability forms BigOFunc* FittingCurve(BigO complexity) { + static const double kLog2E = 1.44269504088896340736; switch (complexity) { case oN: - return [](int n) -> double { return n; }; + return [](int64_t n) -> double { return static_cast<double>(n); }; case oNSquared: - return [](int n) -> double { return std::pow(n, 2); }; + return [](int64_t n) -> double { return std::pow(n, 2); }; case oNCubed: - return [](int n) -> double { return std::pow(n, 3); }; + return [](int64_t n) -> double { return std::pow(n, 3); }; case oLogN: - return [](int n) { return std::log2(n); }; + /* Note: can't use log2 because Android's GNU STL lacks it */ + return [](int64_t n) { return kLog2E * log(static_cast<double>(n)); }; case oNLogN: - return [](int n) { return n * std::log2(n); }; + /* Note: can't use log2 because Android's GNU STL lacks it */ + return [](int64_t n) { return kLog2E * n * log(static_cast<double>(n)); }; case o1: default: - return [](int) { return 1.0; }; + return [](int64_t) { return 1.0; }; } } @@ -66,15 +68,15 @@ std::string GetBigOString(BigO complexity) { // Find the coefficient for the high-order term in the running time, by // minimizing the sum of squares of relative error, for the fitting curve -// given by the lambda expresion. +// given by the lambda expression. // - n : Vector containing the size of the benchmark tests. // - time : Vector containing the times for the benchmark tests. -// - fitting_curve : lambda expresion (e.g. [](int n) {return n; };). +// - fitting_curve : lambda expression (e.g. [](int64_t n) {return n; };). // For a deeper explanation on the algorithm logic, look the README file at // http://github.com/ismaelJimenez/Minimal-Cpp-Least-Squared-Fit -LeastSq MinimalLeastSq(const std::vector<int>& n, +LeastSq MinimalLeastSq(const std::vector<int64_t>& n, const std::vector<double>& time, BigOFunc* fitting_curve) { double sigma_gn = 0.0; @@ -118,7 +120,7 @@ LeastSq MinimalLeastSq(const std::vector<int>& n, // - complexity : If different than oAuto, the fitting curve will stick to // this one. If it is oAuto, it will be calculated the best // fitting curve. -LeastSq MinimalLeastSq(const std::vector<int>& n, +LeastSq MinimalLeastSq(const std::vector<int64_t>& n, const std::vector<double>& time, const BigO complexity) { CHECK_EQ(n.size(), time.size()); CHECK_GE(n.size(), 2); // Do not compute fitting curve is less than two @@ -150,109 +152,6 @@ LeastSq MinimalLeastSq(const std::vector<int>& n, return best_fit; } -std::vector<BenchmarkReporter::Run> ComputeStats( - const std::vector<BenchmarkReporter::Run>& reports) { - typedef BenchmarkReporter::Run Run; - std::vector<Run> results; - - auto error_count = - std::count_if(reports.begin(), reports.end(), - [](Run const& run) { return run.error_occurred; }); - - if (reports.size() - error_count < 2) { - // We don't report aggregated data if there was a single run. - return results; - } - // Accumulators. - Stat1_d real_accumulated_time_stat; - Stat1_d cpu_accumulated_time_stat; - Stat1_d bytes_per_second_stat; - Stat1_d items_per_second_stat; - // 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) { - CHECK_EQ(reports[0].benchmark_name, run.benchmark_name); - CHECK_EQ(run_iterations, run.iterations); - if (run.error_occurred) continue; - real_accumulated_time_stat += - Stat1_d(run.real_accumulated_time / run.iterations, run.iterations); - cpu_accumulated_time_stat += - 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. - Run mean_data; - mean_data.benchmark_name = reports[0].benchmark_name + "_mean"; - mean_data.iterations = run_iterations; - mean_data.real_accumulated_time = - real_accumulated_time_stat.Mean() * run_iterations; - mean_data.cpu_accumulated_time = - cpu_accumulated_time_stat.Mean() * run_iterations; - 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; - for (std::size_t i = 1; i < reports.size(); i++) { - if (reports[i].report_label != reports[0].report_label) { - mean_data.report_label = ""; - break; - } - } - - Run stddev_data; - stddev_data.benchmark_name = reports[0].benchmark_name + "_stddev"; - stddev_data.report_label = mean_data.report_label; - stddev_data.iterations = 0; - stddev_data.real_accumulated_time = real_accumulated_time_stat.StdDev(); - stddev_data.cpu_accumulated_time = cpu_accumulated_time_stat.StdDev(); - 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); - return results; -} - std::vector<BenchmarkReporter::Run> ComputeBigO( const std::vector<BenchmarkReporter::Run>& reports) { typedef BenchmarkReporter::Run Run; @@ -261,7 +160,7 @@ std::vector<BenchmarkReporter::Run> ComputeBigO( if (reports.size() < 2) return results; // Accumulators. - std::vector<int> n; + std::vector<int64_t> n; std::vector<double> real_time; std::vector<double> cpu_time; diff --git a/lib/libcxx/utils/google-benchmark/src/complexity.h b/lib/libcxx/utils/google-benchmark/src/complexity.h index 23cd9bbc8c7..df29b48d29b 100644 --- a/lib/libcxx/utils/google-benchmark/src/complexity.h +++ b/lib/libcxx/utils/google-benchmark/src/complexity.h @@ -21,17 +21,10 @@ #include <string> #include <vector> -#include "benchmark/benchmark_api.h" -#include "benchmark/reporter.h" +#include "benchmark/benchmark.h" namespace benchmark { -// Return a vector containing the mean and standard devation information for -// the specified list of reports. If 'reports' contains less than two -// non-errored runs an empty vector is returned -std::vector<BenchmarkReporter::Run> ComputeStats( - const std::vector<BenchmarkReporter::Run>& reports); - // Return a vector containing the bigO and RMS information for the specified // list of reports. If 'reports.size() < 2' an empty vector is returned. std::vector<BenchmarkReporter::Run> ComputeBigO( @@ -58,4 +51,5 @@ struct LeastSq { std::string GetBigOString(BigO complexity); } // end namespace benchmark + #endif // COMPLEXITY_H_ diff --git a/lib/libcxx/utils/google-benchmark/src/console_reporter.cc b/lib/libcxx/utils/google-benchmark/src/console_reporter.cc index 3f3de02945a..48920ca7829 100644 --- a/lib/libcxx/utils/google-benchmark/src/console_reporter.cc +++ b/lib/libcxx/utils/google-benchmark/src/console_reporter.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "benchmark/reporter.h" +#include "benchmark/benchmark.h" #include "complexity.h" #include "counter.h" @@ -36,15 +36,16 @@ namespace benchmark { bool ConsoleReporter::ReportContext(const Context& context) { name_field_width_ = context.name_field_width; printed_header_ = false; + prev_counters_.clear(); PrintBasicContext(&GetErrorStream(), context); #ifdef BENCHMARK_OS_WINDOWS - if (color_output_ && &std::cout != &GetOutputStream()) { + if ((output_options_ & OO_Color) && &std::cout != &GetOutputStream()) { GetErrorStream() << "Color printing is only supported for stdout on windows." " Disabling color printing\n"; - color_output_ = false; + output_options_ = static_cast< OutputOptions >(output_options_ & ~OO_Color); } #endif @@ -52,25 +53,39 @@ bool ConsoleReporter::ReportContext(const Context& context) { } 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"); + std::string str = FormatString("%-*s %13s %13s %10s", static_cast<int>(name_field_width_), + "Benchmark", "Time", "CPU", "Iterations"); if(!run.counters.empty()) { - str += " UserCounters..."; + if(output_options_ & OO_Tabular) { + for(auto const& c : run.counters) { + str += FormatString(" %10s", c.first.c_str()); + } + } else { + str += " UserCounters..."; + } } + str += "\n"; 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) { - // print the header if none was printed yet - if (!printed_header_) { + // print the header: + // --- if none was printed yet + bool print_header = !printed_header_; + // --- or if the format is tabular and this run + // has different fields from the prev header + print_header |= (output_options_ & OO_Tabular) && + (!internal::SameNames(run.counters, prev_counters_)); + if (print_header) { printed_header_ = true; + prev_counters_ = run.counters; PrintHeader(run); } // As an alternative to printing the headers like this, we could sort - // the benchmarks by header and then print like that. + // the benchmarks by header and then print. But this would require + // waiting for the full results before printing, or printing twice. PrintRunData(run); } } @@ -86,8 +101,8 @@ static void IgnoreColorPrint(std::ostream& out, LogColor, const char* fmt, void ConsoleReporter::PrintRunData(const Run& result) { typedef void(PrinterFn)(std::ostream&, LogColor, const char*, ...); auto& Out = GetOutputStream(); - PrinterFn* printer = - color_output_ ? (PrinterFn*)ColorPrintf : IgnoreColorPrint; + PrinterFn* printer = (output_options_ & OO_Color) ? + (PrinterFn*)ColorPrintf : IgnoreColorPrint; auto name_color = (result.report_big_o || result.report_rms) ? COLOR_BLUE : COLOR_GREEN; printer(Out, name_color, "%-*s ", name_field_width_, @@ -133,8 +148,20 @@ void ConsoleReporter::PrintRunData(const Run& result) { } 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()); + const std::size_t cNameLen = std::max(std::string::size_type(10), + c.first.length()); + auto const& s = HumanReadableNumber(c.second.value, 1000); + if (output_options_ & OO_Tabular) { + if (c.second.flags & Counter::kIsRate) { + printer(Out, COLOR_DEFAULT, " %*s/s", cNameLen - 2, s.c_str()); + } else { + printer(Out, COLOR_DEFAULT, " %*s", cNameLen, s.c_str()); + } + } else { + const char* unit = (c.second.flags & Counter::kIsRate) ? "/s" : ""; + printer(Out, COLOR_DEFAULT, " %s=%s%s", c.first.c_str(), s.c_str(), + unit); + } } if (!rate.empty()) { diff --git a/lib/libcxx/utils/google-benchmark/src/counter.cc b/lib/libcxx/utils/google-benchmark/src/counter.cc index 307863d3c4d..cb604e060b6 100644 --- a/lib/libcxx/utils/google-benchmark/src/counter.cc +++ b/lib/libcxx/utils/google-benchmark/src/counter.cc @@ -17,7 +17,8 @@ namespace benchmark { namespace internal { -double Finish(Counter const& c, double cpu_time, double num_threads) { +double Finish(Counter const& c, int64_t iterations, double cpu_time, + double num_threads) { double v = c.value; if (c.flags & Counter::kIsRate) { v /= cpu_time; @@ -25,25 +26,31 @@ double Finish(Counter const& c, double cpu_time, double num_threads) { if (c.flags & Counter::kAvgThreads) { v /= num_threads; } + if (c.flags & Counter::kIsIterationInvariant) { + v *= iterations; + } + if (c.flags & Counter::kAvgIterations) { + v /= iterations; + } 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 Finish(UserCounters* l, int64_t iterations, double cpu_time, double num_threads) { + for (auto& c : *l) { + c.second.value = Finish(c.second, iterations, cpu_time, num_threads); } } -void Increment(UserCounters *l, UserCounters const& r) { +void Increment(UserCounters* l, UserCounters const& r) { // add counters present in both or just in *l - for (auto &c : *l) { + for (auto& c : *l) { auto it = r.find(c.first); if (it != r.end()) { - c.second = c.second + it->second; + c.second.value = c.second + it->second; } } // add counters present in r, but not in *l - for (auto const &tc : r) { + for (auto const& tc : r) { auto it = l->find(tc.first); if (it == l->end()) { (*l)[tc.first] = tc.second; @@ -57,12 +64,12 @@ bool SameNames(UserCounters const& l, UserCounters const& r) { return false; } for (auto const& c : l) { - if ( r.find(c.first) == r.end()) { + if (r.find(c.first) == r.end()) { return false; } } return true; } -} // end namespace internal -} // end namespace benchmark +} // end namespace internal +} // end namespace benchmark diff --git a/lib/libcxx/utils/google-benchmark/src/counter.h b/lib/libcxx/utils/google-benchmark/src/counter.h index bbb92d9a298..d884e50aa12 100644 --- a/lib/libcxx/utils/google-benchmark/src/counter.h +++ b/lib/libcxx/utils/google-benchmark/src/counter.h @@ -12,15 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "benchmark/benchmark_api.h" +#include "benchmark/benchmark.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); +void Finish(UserCounters* l, int64_t iterations, 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 internal -} //end namespace benchmark +} // end namespace benchmark diff --git a/lib/libcxx/utils/google-benchmark/src/csv_reporter.cc b/lib/libcxx/utils/google-benchmark/src/csv_reporter.cc index 6779815b3c3..4a641909d80 100644 --- a/lib/libcxx/utils/google-benchmark/src/csv_reporter.cc +++ b/lib/libcxx/utils/google-benchmark/src/csv_reporter.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "benchmark/reporter.h" +#include "benchmark/benchmark.h" #include "complexity.h" #include <algorithm> @@ -22,9 +22,9 @@ #include <tuple> #include <vector> +#include "check.h" #include "string_util.h" #include "timers.h" -#include "check.h" // File format reference: http://edoceo.com/utilitas/csv-file-format. @@ -35,14 +35,14 @@ std::vector<std::string> elements = { "name", "iterations", "real_time", "cpu_time", "time_unit", "bytes_per_second", "items_per_second", "label", "error_occurred", "error_message"}; -} +} // namespace bool CSVReporter::ReportContext(const Context& context) { PrintBasicContext(&GetErrorStream(), context); return true; } -void CSVReporter::ReportRuns(const std::vector<Run> & reports) { +void CSVReporter::ReportRuns(const std::vector<Run>& reports) { std::ostream& Out = GetOutputStream(); if (!printed_header_) { @@ -58,7 +58,8 @@ void CSVReporter::ReportRuns(const std::vector<Run> & reports) { Out << *B++; if (B != elements.end()) Out << ","; } - for (auto B = user_counter_names_.begin(); B != user_counter_names_.end();) { + for (auto B = user_counter_names_.begin(); + B != user_counter_names_.end();) { Out << ",\"" << *B++ << "\""; } Out << "\n"; @@ -69,9 +70,9 @@ void CSVReporter::ReportRuns(const std::vector<Run> & reports) { 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"; + << "All counters must be present in each run. " + << "Counter named \"" << cnt.first + << "\" was not in a run after being added to the header"; } } } @@ -80,10 +81,9 @@ 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 @@ -135,10 +135,13 @@ void CSVReporter::PrintRunData(const Run & run) { Out << ",,"; // for error_occurred and error_message // Print user counters - for (const auto &ucn : user_counter_names_) { + for (const auto& ucn : user_counter_names_) { auto it = run.counters.find(ucn); - CHECK(it != run.counters.end()); - Out << "," << it->second; + if (it == run.counters.end()) { + Out << ","; + } else { + Out << "," << it->second; + } } Out << '\n'; } diff --git a/lib/libcxx/utils/google-benchmark/src/cycleclock.h b/lib/libcxx/utils/google-benchmark/src/cycleclock.h index e0f9b01f9d3..00d57641676 100644 --- a/lib/libcxx/utils/google-benchmark/src/cycleclock.h +++ b/lib/libcxx/utils/google-benchmark/src/cycleclock.h @@ -23,7 +23,7 @@ #include <cstdint> -#include "benchmark/macros.h" +#include "benchmark/benchmark.h" #include "internal_macros.h" #if defined(BENCHMARK_OS_MACOSX) @@ -121,7 +121,7 @@ inline BENCHMARK_ALWAYS_INLINE int64_t Now() { // 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 }; + 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__) @@ -159,6 +159,11 @@ inline BENCHMARK_ALWAYS_INLINE int64_t Now() { struct timeval tv; gettimeofday(&tv, nullptr); return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec; +#elif defined(__s390__) // Covers both s390 and s390x. + // Return the CPU clock. + uint64_t tsc; + asm("stck %0" : "=Q"(tsc) : : "cc"); + return tsc; #else // The soft failover to a generic implementation is automatic only for ARM. // For other platforms the developer is expected to make an attempt to create diff --git a/lib/libcxx/utils/google-benchmark/src/internal_macros.h b/lib/libcxx/utils/google-benchmark/src/internal_macros.h index ab9dd85c102..b7e9203ff60 100644 --- a/lib/libcxx/utils/google-benchmark/src/internal_macros.h +++ b/lib/libcxx/utils/google-benchmark/src/internal_macros.h @@ -1,36 +1,51 @@ #ifndef BENCHMARK_INTERNAL_MACROS_H_ #define BENCHMARK_INTERNAL_MACROS_H_ -#include "benchmark/macros.h" +#include "benchmark/benchmark.h" + +/* Needed to detect STL */ +#include <cstdlib> + +// clang-format off #ifndef __has_feature #define __has_feature(x) 0 #endif +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif #if defined(__clang__) -#define COMPILER_CLANG + #if !defined(COMPILER_CLANG) + #define COMPILER_CLANG + #endif #elif defined(_MSC_VER) -#define COMPILER_MSVC + #if !defined(COMPILER_MSVC) + #define COMPILER_MSVC + #endif #elif defined(__GNUC__) -#define COMPILER_GCC + #if !defined(COMPILER_GCC) + #define COMPILER_GCC + #endif #endif #if __has_feature(cxx_attributes) -#define BENCHMARK_NORETURN [[noreturn]] + #define BENCHMARK_NORETURN [[noreturn]] #elif defined(__GNUC__) -#define BENCHMARK_NORETURN __attribute__((noreturn)) + #define BENCHMARK_NORETURN __attribute__((noreturn)) #elif defined(COMPILER_MSVC) -#define BENCHMARK_NORETURN __declspec(noreturn) + #define BENCHMARK_NORETURN __declspec(noreturn) #else -#define BENCHMARK_NORETURN + #define BENCHMARK_NORETURN #endif #if defined(__CYGWIN__) -#define BENCHMARK_OS_CYGWIN 1 + #define BENCHMARK_OS_CYGWIN 1 #elif defined(_WIN32) -#define BENCHMARK_OS_WINDOWS 1 + #define BENCHMARK_OS_WINDOWS 1 #elif defined(__APPLE__) -#include "TargetConditionals.h" + #define BENCHMARK_OS_APPLE 1 + #include "TargetConditionals.h" #if defined(TARGET_OS_MAC) #define BENCHMARK_OS_MACOSX 1 #if defined(TARGET_OS_IPHONE) @@ -38,18 +53,48 @@ #endif #endif #elif defined(__FreeBSD__) -#define BENCHMARK_OS_FREEBSD 1 + #define BENCHMARK_OS_FREEBSD 1 +#elif defined(__NetBSD__) + #define BENCHMARK_OS_NETBSD 1 +#elif defined(__OpenBSD__) + #define BENCHMARK_OS_OPENBSD 1 #elif defined(__linux__) -#define BENCHMARK_OS_LINUX 1 + #define BENCHMARK_OS_LINUX 1 #elif defined(__native_client__) -#define BENCHMARK_OS_NACL 1 -#elif defined(EMSCRIPTEN) -#define BENCHMARK_OS_EMSCRIPTEN 1 + #define BENCHMARK_OS_NACL 1 +#elif defined(__EMSCRIPTEN__) + #define BENCHMARK_OS_EMSCRIPTEN 1 +#elif defined(__rtems__) + #define BENCHMARK_OS_RTEMS 1 +#elif defined(__Fuchsia__) +#define BENCHMARK_OS_FUCHSIA 1 +#elif defined (__SVR4) && defined (__sun) +#define BENCHMARK_OS_SOLARIS 1 +#endif + +#if defined(__ANDROID__) && defined(__GLIBCXX__) +#define BENCHMARK_STL_ANDROID_GNUSTL 1 #endif #if !__has_feature(cxx_exceptions) && !defined(__cpp_exceptions) \ && !defined(__EXCEPTIONS) -#define BENCHMARK_HAS_NO_EXCEPTIONS + #define BENCHMARK_HAS_NO_EXCEPTIONS #endif +#if defined(COMPILER_CLANG) || defined(COMPILER_GCC) + #define BENCHMARK_MAYBE_UNUSED __attribute__((unused)) +#else + #define BENCHMARK_MAYBE_UNUSED +#endif + +#if defined(COMPILER_GCC) || __has_builtin(__builtin_unreachable) + #define BENCHMARK_UNREACHABLE() __builtin_unreachable() +#elif defined(COMPILER_MSVC) + #define BENCHMARK_UNREACHABLE() __assume(false) +#else + #define BENCHMARK_UNREACHABLE() ((void)0) +#endif + +// clang-format on + #endif // BENCHMARK_INTERNAL_MACROS_H_ diff --git a/lib/libcxx/utils/google-benchmark/src/json_reporter.cc b/lib/libcxx/utils/google-benchmark/src/json_reporter.cc index 5a653088e5b..611605af6b5 100644 --- a/lib/libcxx/utils/google-benchmark/src/json_reporter.cc +++ b/lib/libcxx/utils/google-benchmark/src/json_reporter.cc @@ -12,12 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "benchmark/reporter.h" +#include "benchmark/benchmark.h" #include "complexity.h" #include <algorithm> #include <cstdint> +#include <iomanip> // for setprecision #include <iostream> +#include <limits> #include <string> #include <tuple> #include <vector> @@ -30,15 +32,15 @@ namespace benchmark { namespace { std::string FormatKV(std::string const& key, std::string const& value) { - return StringPrintF("\"%s\": \"%s\"", key.c_str(), value.c_str()); + return StrFormat("\"%s\": \"%s\"", key.c_str(), value.c_str()); } std::string FormatKV(std::string const& key, const char* value) { - return StringPrintF("\"%s\": \"%s\"", key.c_str(), value); + return StrFormat("\"%s\": \"%s\"", key.c_str(), value); } std::string FormatKV(std::string const& key, bool value) { - return StringPrintF("\"%s\": %s", key.c_str(), value ? "true" : "false"); + return StrFormat("\"%s\": %s", key.c_str(), value ? "true" : "false"); } std::string FormatKV(std::string const& key, int64_t value) { @@ -48,7 +50,14 @@ std::string FormatKV(std::string const& key, int64_t value) { } std::string FormatKV(std::string const& key, double value) { - return StringPrintF("\"%s\": %.2f", key.c_str(), value); + std::stringstream ss; + ss << '"' << key << "\": "; + + const auto max_digits10 = std::numeric_limits<decltype(value)>::max_digits10; + const auto max_fractional_digits10 = max_digits10 - 1; + + ss << std::scientific << std::setprecision(max_fractional_digits10) << value; + return ss.str(); } int64_t RoundDouble(double v) { return static_cast<int64_t>(v + 0.5); } @@ -68,13 +77,41 @@ bool JSONReporter::ReportContext(const Context& context) { std::string walltime_value = LocalDateTimeString(); out << indent << FormatKV("date", walltime_value) << ",\n"; - out << indent << FormatKV("num_cpus", static_cast<int64_t>(context.num_cpus)) + if (Context::executable_name) { + out << indent << FormatKV("executable", Context::executable_name) << ",\n"; + } + + CPUInfo const& info = context.cpu_info; + out << indent << FormatKV("num_cpus", static_cast<int64_t>(info.num_cpus)) << ",\n"; - out << indent << FormatKV("mhz_per_cpu", RoundDouble(context.mhz_per_cpu)) + out << indent + << FormatKV("mhz_per_cpu", + RoundDouble(info.cycles_per_second / 1000000.0)) << ",\n"; - out << indent << FormatKV("cpu_scaling_enabled", context.cpu_scaling_enabled) + out << indent << FormatKV("cpu_scaling_enabled", info.scaling_enabled) << ",\n"; + out << indent << "\"caches\": [\n"; + indent = std::string(6, ' '); + std::string cache_indent(8, ' '); + for (size_t i = 0; i < info.caches.size(); ++i) { + auto& CI = info.caches[i]; + out << indent << "{\n"; + out << cache_indent << FormatKV("type", CI.type) << ",\n"; + out << cache_indent << FormatKV("level", static_cast<int64_t>(CI.level)) + << ",\n"; + out << cache_indent + << FormatKV("size", static_cast<int64_t>(CI.size) * 1000u) << ",\n"; + out << cache_indent + << FormatKV("num_sharing", static_cast<int64_t>(CI.num_sharing)) + << "\n"; + out << indent << "}"; + if (i != info.caches.size() - 1) out << ","; + out << "\n"; + } + indent = std::string(4, ' '); + out << indent << "],\n"; + #if defined(NDEBUG) const char build_type[] = "release"; #else @@ -124,40 +161,30 @@ void JSONReporter::PrintRunData(Run const& run) { } if (!run.report_big_o && !run.report_rms) { out << indent << FormatKV("iterations", run.iterations) << ",\n"; - out << indent - << FormatKV("real_time", RoundDouble(run.GetAdjustedRealTime())) - << ",\n"; - out << indent - << FormatKV("cpu_time", RoundDouble(run.GetAdjustedCPUTime())); + out << indent << FormatKV("real_time", run.GetAdjustedRealTime()) << ",\n"; + out << indent << FormatKV("cpu_time", run.GetAdjustedCPUTime()); out << ",\n" << indent << FormatKV("time_unit", GetTimeUnitString(run.time_unit)); } else if (run.report_big_o) { - out << indent - << FormatKV("cpu_coefficient", RoundDouble(run.GetAdjustedCPUTime())) + out << indent << FormatKV("cpu_coefficient", run.GetAdjustedCPUTime()) << ",\n"; - out << indent - << FormatKV("real_coefficient", RoundDouble(run.GetAdjustedRealTime())) + out << indent << FormatKV("real_coefficient", run.GetAdjustedRealTime()) << ",\n"; out << indent << FormatKV("big_o", GetBigOString(run.complexity)) << ",\n"; out << indent << FormatKV("time_unit", GetTimeUnitString(run.time_unit)); } else if (run.report_rms) { - out << indent - << FormatKV("rms", run.GetAdjustedCPUTime()); + out << indent << FormatKV("rms", run.GetAdjustedCPUTime()); } if (run.bytes_per_second > 0.0) { out << ",\n" - << indent - << FormatKV("bytes_per_second", RoundDouble(run.bytes_per_second)); + << indent << FormatKV("bytes_per_second", run.bytes_per_second); } if (run.items_per_second > 0.0) { out << ",\n" - << indent - << FormatKV("items_per_second", RoundDouble(run.items_per_second)); + << indent << FormatKV("items_per_second", run.items_per_second); } - for(auto &c : run.counters) { - out << ",\n" - << indent - << FormatKV(c.first, RoundDouble(c.second)); + for (auto& c : run.counters) { + out << ",\n" << indent << FormatKV(c.first, c.second); } if (!run.report_label.empty()) { out << ",\n" << indent << FormatKV("label", run.report_label); @@ -165,4 +192,4 @@ void JSONReporter::PrintRunData(Run const& run) { out << '\n'; } -} // end namespace benchmark +} // end namespace benchmark diff --git a/lib/libcxx/utils/google-benchmark/src/log.h b/lib/libcxx/utils/google-benchmark/src/log.h index 978cb0b4c8c..47d0c35c018 100644 --- a/lib/libcxx/utils/google-benchmark/src/log.h +++ b/lib/libcxx/utils/google-benchmark/src/log.h @@ -4,7 +4,7 @@ #include <iostream> #include <ostream> -#include "benchmark/macros.h" +#include "benchmark/benchmark.h" namespace benchmark { namespace internal { @@ -66,8 +66,9 @@ inline LogType& GetLogInstanceForLevel(int level) { } // end namespace internal } // end namespace benchmark +// clang-format off #define VLOG(x) \ (::benchmark::internal::GetLogInstanceForLevel(x) << "-- LOG(" << x << "):" \ " ") - -#endif
\ No newline at end of file +// clang-format on +#endif diff --git a/lib/libcxx/utils/google-benchmark/src/re.h b/lib/libcxx/utils/google-benchmark/src/re.h index 01e9736505e..fbe25037b46 100644 --- a/lib/libcxx/utils/google-benchmark/src/re.h +++ b/lib/libcxx/utils/google-benchmark/src/re.h @@ -17,22 +17,39 @@ #include "internal_macros.h" +// clang-format off + +#if !defined(HAVE_STD_REGEX) && \ + !defined(HAVE_GNU_POSIX_REGEX) && \ + !defined(HAVE_POSIX_REGEX) + // No explicit regex selection; detect based on builtin hints. + #if defined(BENCHMARK_OS_LINUX) || defined(BENCHMARK_OS_APPLE) + #define HAVE_POSIX_REGEX 1 + #elif __cplusplus >= 199711L + #define HAVE_STD_REGEX 1 + #endif +#endif + // 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) && \ +#if defined(BENCHMARK_HAS_NO_EXCEPTIONS) && \ + defined(BENCHMARK_HAVE_STD_REGEX) && \ (defined(HAVE_GNU_POSIX_REGEX) || defined(HAVE_POSIX_REGEX)) -#undef HAVE_STD_REGEX + #undef HAVE_STD_REGEX #endif #if defined(HAVE_STD_REGEX) -#include <regex> + #include <regex> #elif defined(HAVE_GNU_POSIX_REGEX) -#include <gnuregex.h> + #include <gnuregex.h> #elif defined(HAVE_POSIX_REGEX) -#include <regex.h> + #include <regex.h> #else #error No regular expression backend was found! #endif + +// clang-format on + #include <string> #include "check.h" @@ -72,20 +89,21 @@ class Regex { inline bool Regex::Init(const std::string& spec, std::string* error) { #ifdef BENCHMARK_HAS_NO_EXCEPTIONS - ((void)error); // suppress unused warning + ((void)error); // suppress unused warning #else try { #endif - re_ = std::regex(spec, std::regex_constants::extended); - init_ = true; + 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(); - } +} +catch (const std::regex_error& e) { + if (error) { + *error = e.what(); } +} #endif - return init_; +return init_; } inline Regex::~Regex() {} diff --git a/lib/libcxx/utils/google-benchmark/src/reporter.cc b/lib/libcxx/utils/google-benchmark/src/reporter.cc index 64742426cd3..541661a25f0 100644 --- a/lib/libcxx/utils/google-benchmark/src/reporter.cc +++ b/lib/libcxx/utils/google-benchmark/src/reporter.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "benchmark/reporter.h" +#include "benchmark/benchmark.h" #include "timers.h" #include <cstdlib> @@ -22,7 +22,6 @@ #include <vector> #include "check.h" -#include "stat.h" namespace benchmark { @@ -31,17 +30,32 @@ BenchmarkReporter::BenchmarkReporter() BenchmarkReporter::~BenchmarkReporter() {} -void BenchmarkReporter::PrintBasicContext(std::ostream *out_ptr, +void BenchmarkReporter::PrintBasicContext(std::ostream *out, Context const &context) { - CHECK(out_ptr) << "cannot be null"; - auto &Out = *out_ptr; - - Out << "Run on (" << context.num_cpus << " X " << context.mhz_per_cpu - << " MHz CPU " << ((context.num_cpus > 1) ? "s" : "") << ")\n"; + CHECK(out) << "cannot be null"; + auto &Out = *out; Out << LocalDateTimeString() << "\n"; - if (context.cpu_scaling_enabled) { + if (context.executable_name) + Out << "Running " << context.executable_name << "\n"; + + const CPUInfo &info = context.cpu_info; + Out << "Run on (" << info.num_cpus << " X " + << (info.cycles_per_second / 1000000.0) << " MHz CPU " + << ((info.num_cpus > 1) ? "s" : "") << ")\n"; + if (info.caches.size() != 0) { + Out << "CPU Caches:\n"; + for (auto &CInfo : info.caches) { + Out << " L" << CInfo.level << " " << CInfo.type << " " + << (CInfo.size / 1000) << "K"; + if (CInfo.num_sharing != 0) + Out << " (x" << (info.num_cpus / CInfo.num_sharing) << ")"; + Out << "\n"; + } + } + + if (info.scaling_enabled) { Out << "***WARNING*** CPU scaling is enabled, the benchmark " "real time measurements may be noisy and will incur extra " "overhead.\n"; @@ -53,6 +67,11 @@ void BenchmarkReporter::PrintBasicContext(std::ostream *out_ptr, #endif } +// No initializer because it's already initialized to NULL. +const char *BenchmarkReporter::Context::executable_name; + +BenchmarkReporter::Context::Context() : cpu_info(CPUInfo::Get()) {} + double BenchmarkReporter::Run::GetAdjustedRealTime() const { double new_time = real_accumulated_time * GetTimeUnitMultiplier(time_unit); if (iterations != 0) new_time /= static_cast<double>(iterations); diff --git a/lib/libcxx/utils/google-benchmark/src/statistics.cc b/lib/libcxx/utils/google-benchmark/src/statistics.cc new file mode 100644 index 00000000000..612dda2d1a7 --- /dev/null +++ b/lib/libcxx/utils/google-benchmark/src/statistics.cc @@ -0,0 +1,177 @@ +// Copyright 2016 Ismael Jimenez Martinez. All rights reserved. +// Copyright 2017 Roman Lebedev. 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.h" + +#include <algorithm> +#include <cmath> +#include <numeric> +#include <string> +#include <vector> +#include "check.h" +#include "statistics.h" + +namespace benchmark { + +auto StatisticsSum = [](const std::vector<double>& v) { + return std::accumulate(v.begin(), v.end(), 0.0); +}; + +double StatisticsMean(const std::vector<double>& v) { + if (v.empty()) return 0.0; + return StatisticsSum(v) * (1.0 / v.size()); +} + +double StatisticsMedian(const std::vector<double>& v) { + if (v.size() < 3) return StatisticsMean(v); + std::vector<double> copy(v); + + auto center = copy.begin() + v.size() / 2; + std::nth_element(copy.begin(), center, copy.end()); + + // did we have an odd number of samples? + // if yes, then center is the median + // it no, then we are looking for the average between center and the value + // before + if (v.size() % 2 == 1) return *center; + auto center2 = copy.begin() + v.size() / 2 - 1; + std::nth_element(copy.begin(), center2, copy.end()); + return (*center + *center2) / 2.0; +} + +// Return the sum of the squares of this sample set +auto SumSquares = [](const std::vector<double>& v) { + return std::inner_product(v.begin(), v.end(), v.begin(), 0.0); +}; + +auto Sqr = [](const double dat) { return dat * dat; }; +auto Sqrt = [](const double dat) { + // Avoid NaN due to imprecision in the calculations + if (dat < 0.0) return 0.0; + return std::sqrt(dat); +}; + +double StatisticsStdDev(const std::vector<double>& v) { + const auto mean = StatisticsMean(v); + if (v.empty()) return mean; + + // Sample standard deviation is undefined for n = 1 + if (v.size() == 1) return 0.0; + + const double avg_squares = SumSquares(v) * (1.0 / v.size()); + return Sqrt(v.size() / (v.size() - 1.0) * (avg_squares - Sqr(mean))); +} + +std::vector<BenchmarkReporter::Run> ComputeStats( + const std::vector<BenchmarkReporter::Run>& reports) { + typedef BenchmarkReporter::Run Run; + std::vector<Run> results; + + auto error_count = + std::count_if(reports.begin(), reports.end(), + [](Run const& run) { return run.error_occurred; }); + + if (reports.size() - error_count < 2) { + // We don't report aggregated data if there was a single run. + return results; + } + + // Accumulators. + std::vector<double> real_accumulated_time_stat; + std::vector<double> cpu_accumulated_time_stat; + std::vector<double> bytes_per_second_stat; + std::vector<double> items_per_second_stat; + + real_accumulated_time_stat.reserve(reports.size()); + cpu_accumulated_time_stat.reserve(reports.size()); + bytes_per_second_stat.reserve(reports.size()); + items_per_second_stat.reserve(reports.size()); + + // 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; + std::vector<double> 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, std::vector<double>{}}}); + it = counter_stats.find(cnt.first); + it->second.s.reserve(reports.size()); + } else { + CHECK_EQ(counter_stats[cnt.first].c.flags, cnt.second.flags); + } + } + } + + // Populate the accumulators. + for (Run const& run : reports) { + CHECK_EQ(reports[0].benchmark_name, run.benchmark_name); + CHECK_EQ(run_iterations, run.iterations); + if (run.error_occurred) continue; + real_accumulated_time_stat.emplace_back(run.real_accumulated_time); + cpu_accumulated_time_stat.emplace_back(run.cpu_accumulated_time); + items_per_second_stat.emplace_back(run.items_per_second); + bytes_per_second_stat.emplace_back(run.bytes_per_second); + // user counters + for (auto const& cnt : run.counters) { + auto it = counter_stats.find(cnt.first); + CHECK_NE(it, counter_stats.end()); + it->second.s.emplace_back(cnt.second); + } + } + + // Only add label if it is same for all runs + std::string report_label = reports[0].report_label; + for (std::size_t i = 1; i < reports.size(); i++) { + if (reports[i].report_label != report_label) { + report_label = ""; + break; + } + } + + for (const auto& Stat : *reports[0].statistics) { + // Get the data from the accumulator to BenchmarkReporter::Run's. + Run data; + data.benchmark_name = reports[0].benchmark_name + "_" + Stat.name_; + data.report_label = report_label; + data.iterations = run_iterations; + + data.real_accumulated_time = Stat.compute_(real_accumulated_time_stat); + data.cpu_accumulated_time = Stat.compute_(cpu_accumulated_time_stat); + data.bytes_per_second = Stat.compute_(bytes_per_second_stat); + data.items_per_second = Stat.compute_(items_per_second_stat); + + data.time_unit = reports[0].time_unit; + + // user counters + for (auto const& kv : counter_stats) { + const auto uc_stat = Stat.compute_(kv.second.s); + auto c = Counter(uc_stat, counter_stats[kv.first].c.flags); + data.counters[kv.first] = c; + } + + results.push_back(data); + } + + return results; +} + +} // end namespace benchmark diff --git a/lib/libcxx/utils/google-benchmark/src/statistics.h b/lib/libcxx/utils/google-benchmark/src/statistics.h new file mode 100644 index 00000000000..7eccc85536a --- /dev/null +++ b/lib/libcxx/utils/google-benchmark/src/statistics.h @@ -0,0 +1,37 @@ +// Copyright 2016 Ismael Jimenez Martinez. All rights reserved. +// Copyright 2017 Roman Lebedev. 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. + +#ifndef STATISTICS_H_ +#define STATISTICS_H_ + +#include <vector> + +#include "benchmark/benchmark.h" + +namespace benchmark { + +// Return a vector containing the mean, median and standard devation information +// (and any user-specified info) for the specified list of reports. If 'reports' +// contains less than two non-errored runs an empty vector is returned +std::vector<BenchmarkReporter::Run> ComputeStats( + const std::vector<BenchmarkReporter::Run>& reports); + +double StatisticsMean(const std::vector<double>& v); +double StatisticsMedian(const std::vector<double>& v); +double StatisticsStdDev(const std::vector<double>& v); + +} // end namespace benchmark + +#endif // STATISTICS_H_ diff --git a/lib/libcxx/utils/google-benchmark/src/string_util.cc b/lib/libcxx/utils/google-benchmark/src/string_util.cc index cd4e7cfde57..05ac5b4ea36 100644 --- a/lib/libcxx/utils/google-benchmark/src/string_util.cc +++ b/lib/libcxx/utils/google-benchmark/src/string_util.cc @@ -27,8 +27,6 @@ static_assert(arraysize(kSmallSIUnits) == arraysize(kBigSIUnits), static const int64_t kUnitsSize = arraysize(kBigSIUnits); -} // end anonymous namespace - void ToExponentAndMantissa(double val, double thresh, int precision, double one_k, std::string* mantissa, int64_t* exponent) { @@ -100,14 +98,16 @@ std::string ExponentToPrefix(int64_t exponent, bool iec) { } std::string ToBinaryStringFullySpecified(double value, double threshold, - int precision) { + int precision, double one_k = 1024.0) { std::string mantissa; int64_t exponent; - ToExponentAndMantissa(value, threshold, precision, 1024.0, &mantissa, + ToExponentAndMantissa(value, threshold, precision, one_k, &mantissa, &exponent); return mantissa + ExponentToPrefix(exponent, false); } +} // end namespace + void AppendHumanReadable(int n, std::string* str) { std::stringstream ss; // Round down to the nearest SI prefix. @@ -115,14 +115,14 @@ void AppendHumanReadable(int n, std::string* str) { *str += ss.str(); } -std::string HumanReadableNumber(double n) { +std::string HumanReadableNumber(double n, double one_k) { // 1.1 means that figures up to 1.1k should be shown with the next unit down; // this softens edge effects. // 1 means that we should show one decimal place of precision. - return ToBinaryStringFullySpecified(n, 1.1, 1); + return ToBinaryStringFullySpecified(n, 1.1, 1, one_k); } -std::string StringPrintFImp(const char* msg, va_list args) { +std::string StrFormatImp(const char* msg, va_list args) { // we might need a second shot at this, so pre-emptivly make a copy va_list args_cp; va_copy(args_cp, args); @@ -152,10 +152,10 @@ std::string StringPrintFImp(const char* msg, va_list args) { return std::string(buff_ptr.get()); } -std::string StringPrintF(const char* format, ...) { +std::string StrFormat(const char* format, ...) { va_list args; va_start(args, format); - std::string tmp = StringPrintFImp(format, args); + std::string tmp = StrFormatImp(format, args); va_end(args); return tmp; } @@ -169,4 +169,93 @@ void ReplaceAll(std::string* str, const std::string& from, } } +#ifdef BENCHMARK_STL_ANDROID_GNUSTL +/* + * GNU STL in Android NDK lacks support for some C++11 functions, including + * stoul, stoi, stod. We reimplement them here using C functions strtoul, + * strtol, strtod. Note that reimplemented functions are in benchmark:: + * namespace, not std:: namespace. + */ +unsigned long stoul(const std::string& str, size_t* pos, int base) { + /* Record previous errno */ + const int oldErrno = errno; + errno = 0; + + const char* strStart = str.c_str(); + char* strEnd = const_cast<char*>(strStart); + const unsigned long result = strtoul(strStart, &strEnd, base); + + const int strtoulErrno = errno; + /* Restore previous errno */ + errno = oldErrno; + + /* Check for errors and return */ + if (strtoulErrno == ERANGE) { + throw std::out_of_range( + "stoul failed: " + str + " is outside of range of unsigned long"); + } else if (strEnd == strStart || strtoulErrno != 0) { + throw std::invalid_argument( + "stoul failed: " + str + " is not an integer"); + } + if (pos != nullptr) { + *pos = static_cast<size_t>(strEnd - strStart); + } + return result; +} + +int stoi(const std::string& str, size_t* pos, int base) { + /* Record previous errno */ + const int oldErrno = errno; + errno = 0; + + const char* strStart = str.c_str(); + char* strEnd = const_cast<char*>(strStart); + const long result = strtol(strStart, &strEnd, base); + + const int strtolErrno = errno; + /* Restore previous errno */ + errno = oldErrno; + + /* Check for errors and return */ + if (strtolErrno == ERANGE || long(int(result)) != result) { + throw std::out_of_range( + "stoul failed: " + str + " is outside of range of int"); + } else if (strEnd == strStart || strtolErrno != 0) { + throw std::invalid_argument( + "stoul failed: " + str + " is not an integer"); + } + if (pos != nullptr) { + *pos = static_cast<size_t>(strEnd - strStart); + } + return int(result); +} + +double stod(const std::string& str, size_t* pos) { + /* Record previous errno */ + const int oldErrno = errno; + errno = 0; + + const char* strStart = str.c_str(); + char* strEnd = const_cast<char*>(strStart); + const double result = strtod(strStart, &strEnd); + + /* Restore previous errno */ + const int strtodErrno = errno; + errno = oldErrno; + + /* Check for errors and return */ + if (strtodErrno == ERANGE) { + throw std::out_of_range( + "stoul failed: " + str + " is outside of range of int"); + } else if (strEnd == strStart || strtodErrno != 0) { + throw std::invalid_argument( + "stoul failed: " + str + " is not an integer"); + } + if (pos != nullptr) { + *pos = static_cast<size_t>(strEnd - strStart); + } + return result; +} +#endif + } // end namespace benchmark diff --git a/lib/libcxx/utils/google-benchmark/src/string_util.h b/lib/libcxx/utils/google-benchmark/src/string_util.h index 0b190b91a16..4a5501273cf 100644 --- a/lib/libcxx/utils/google-benchmark/src/string_util.h +++ b/lib/libcxx/utils/google-benchmark/src/string_util.h @@ -10,31 +10,47 @@ namespace benchmark { void AppendHumanReadable(int n, std::string* str); -std::string HumanReadableNumber(double n); +std::string HumanReadableNumber(double n, double one_k = 1024.0); -std::string StringPrintF(const char* format, ...); +std::string StrFormat(const char* format, ...); -inline std::ostream& StringCatImp(std::ostream& out) BENCHMARK_NOEXCEPT { +inline std::ostream& StrCatImp(std::ostream& out) BENCHMARK_NOEXCEPT { return out; } template <class First, class... Rest> -inline std::ostream& StringCatImp(std::ostream& out, First&& f, - Rest&&... rest) { +inline std::ostream& StrCatImp(std::ostream& out, First&& f, Rest&&... rest) { out << std::forward<First>(f); - return StringCatImp(out, std::forward<Rest>(rest)...); + return StrCatImp(out, std::forward<Rest>(rest)...); } template <class... Args> inline std::string StrCat(Args&&... args) { std::ostringstream ss; - StringCatImp(ss, std::forward<Args>(args)...); + StrCatImp(ss, std::forward<Args>(args)...); return ss.str(); } void ReplaceAll(std::string* str, const std::string& from, const std::string& to); +#ifdef BENCHMARK_STL_ANDROID_GNUSTL +/* + * GNU STL in Android NDK lacks support for some C++11 functions, including + * stoul, stoi, stod. We reimplement them here using C functions strtoul, + * strtol, strtod. Note that reimplemented functions are in benchmark:: + * namespace, not std:: namespace. + */ +unsigned long stoul(const std::string& str, size_t* pos = nullptr, + int base = 10); +int stoi(const std::string& str, size_t* pos = nullptr, int base = 10); +double stod(const std::string& str, size_t* pos = nullptr); +#else +using std::stoul; +using std::stoi; +using std::stod; +#endif + } // end namespace benchmark #endif // BENCHMARK_STRING_UTIL_H_ diff --git a/lib/libcxx/utils/google-benchmark/src/sysinfo.cc b/lib/libcxx/utils/google-benchmark/src/sysinfo.cc index 7feb79e65f2..73064b97ba2 100644 --- a/lib/libcxx/utils/google-benchmark/src/sysinfo.cc +++ b/lib/libcxx/utils/google-benchmark/src/sysinfo.cc @@ -12,34 +12,47 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "sysinfo.h" #include "internal_macros.h" #ifdef BENCHMARK_OS_WINDOWS #include <Shlwapi.h> +#undef StrCat // Don't let StrCat in string_util.h be renamed to lstrcatA #include <VersionHelpers.h> #include <Windows.h> #else #include <fcntl.h> +#ifndef BENCHMARK_OS_FUCHSIA #include <sys/resource.h> +#endif #include <sys/time.h> #include <sys/types.h> // this header must be included before 'sys/sysctl.h' to avoid compilation error on FreeBSD #include <unistd.h> -#if defined BENCHMARK_OS_FREEBSD || defined BENCHMARK_OS_MACOSX +#if defined BENCHMARK_OS_FREEBSD || defined BENCHMARK_OS_MACOSX || \ + defined BENCHMARK_OS_NETBSD || defined BENCHMARK_OS_OPENBSD +#define BENCHMARK_HAS_SYSCTL #include <sys/sysctl.h> #endif #endif +#if defined(BENCHMARK_OS_SOLARIS) +#include <kstat.h> +#endif +#include <algorithm> +#include <array> +#include <bitset> #include <cerrno> +#include <climits> #include <cstdint> #include <cstdio> #include <cstdlib> #include <cstring> +#include <fstream> #include <iostream> +#include <iterator> #include <limits> -#include <mutex> +#include <memory> +#include <sstream> -#include "arraysize.h" #include "check.h" #include "cycleclock.h" #include "internal_macros.h" @@ -49,214 +62,466 @@ namespace benchmark { namespace { -std::once_flag cpuinfo_init; -double cpuinfo_cycles_per_second = 1.0; -int cpuinfo_num_cpus = 1; // Conservative guess -#if !defined BENCHMARK_OS_MACOSX -const int64_t estimate_time_ms = 1000; +void PrintImp(std::ostream& out) { out << std::endl; } -// Helper function estimates cycles/sec by observing cycles elapsed during -// sleep(). Using small sleep time decreases accuracy significantly. -int64_t EstimateCyclesPerSecond() { - const int64_t start_ticks = cycleclock::Now(); - SleepForMilliseconds(estimate_time_ms); - return cycleclock::Now() - start_ticks; +template <class First, class... Rest> +void PrintImp(std::ostream& out, First&& f, Rest&&... rest) { + out << std::forward<First>(f); + PrintImp(out, std::forward<Rest>(rest)...); } -#endif -#if defined BENCHMARK_OS_LINUX || defined BENCHMARK_OS_CYGWIN -// Helper function for reading an int from a file. Returns true if successful -// and the memory location pointed to by value is set to the value read. -bool ReadIntFromFile(const char* file, long* value) { - bool ret = false; - int fd = open(file, O_RDONLY); - if (fd != -1) { - char line[1024]; - char* err; - memset(line, '\0', sizeof(line)); - 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; - ret = true; +template <class... Args> +BENCHMARK_NORETURN void PrintErrorAndDie(Args&&... args) { + PrintImp(std::cerr, std::forward<Args>(args)...); + std::exit(EXIT_FAILURE); +} + +#ifdef BENCHMARK_HAS_SYSCTL + +/// ValueUnion - A type used to correctly alias the byte-for-byte output of +/// `sysctl` with the result type it's to be interpreted as. +struct ValueUnion { + union DataT { + uint32_t uint32_value; + uint64_t uint64_value; + // For correct aliasing of union members from bytes. + char bytes[8]; + }; + using DataPtr = std::unique_ptr<DataT, decltype(&std::free)>; + + // The size of the data union member + its trailing array size. + size_t Size; + DataPtr Buff; + + public: + ValueUnion() : Size(0), Buff(nullptr, &std::free) {} + + explicit ValueUnion(size_t BuffSize) + : Size(sizeof(DataT) + BuffSize), + Buff(::new (std::malloc(Size)) DataT(), &std::free) {} + + ValueUnion(ValueUnion&& other) = default; + + explicit operator bool() const { return bool(Buff); } + + char* data() const { return Buff->bytes; } + + std::string GetAsString() const { return std::string(data()); } + + int64_t GetAsInteger() const { + if (Size == sizeof(Buff->uint32_value)) + return static_cast<int32_t>(Buff->uint32_value); + else if (Size == sizeof(Buff->uint64_value)) + return static_cast<int64_t>(Buff->uint64_value); + BENCHMARK_UNREACHABLE(); + } + + uint64_t GetAsUnsigned() const { + if (Size == sizeof(Buff->uint32_value)) + return Buff->uint32_value; + else if (Size == sizeof(Buff->uint64_value)) + return Buff->uint64_value; + BENCHMARK_UNREACHABLE(); + } + + template <class T, int N> + std::array<T, N> GetAsArray() { + const int ArrSize = sizeof(T) * N; + CHECK_LE(ArrSize, Size); + std::array<T, N> Arr; + std::memcpy(Arr.data(), data(), ArrSize); + return Arr; + } +}; + +ValueUnion GetSysctlImp(std::string const& Name) { +#if defined BENCHMARK_OS_OPENBSD + int mib[2]; + + mib[0] = CTL_HW; + if ((Name == "hw.ncpu") || (Name == "hw.cpuspeed")){ + ValueUnion buff(sizeof(int)); + + if (Name == "hw.ncpu") { + mib[1] = HW_NCPU; + } else { + mib[1] = HW_CPUSPEED; + } + + if (sysctl(mib, 2, buff.data(), &buff.Size, nullptr, 0) == -1) { + return ValueUnion(); } - close(fd); + return buff; } - return ret; + return ValueUnion(); +#else + size_t CurBuffSize = 0; + if (sysctlbyname(Name.c_str(), nullptr, &CurBuffSize, nullptr, 0) == -1) + return ValueUnion(); + + ValueUnion buff(CurBuffSize); + if (sysctlbyname(Name.c_str(), buff.data(), &buff.Size, nullptr, 0) == 0) + return buff; + return ValueUnion(); +#endif +} + +BENCHMARK_MAYBE_UNUSED +bool GetSysctl(std::string const& Name, std::string* Out) { + Out->clear(); + auto Buff = GetSysctlImp(Name); + if (!Buff) return false; + Out->assign(Buff.data()); + return true; +} + +template <class Tp, + class = typename std::enable_if<std::is_integral<Tp>::value>::type> +bool GetSysctl(std::string const& Name, Tp* Out) { + *Out = 0; + auto Buff = GetSysctlImp(Name); + if (!Buff) return false; + *Out = static_cast<Tp>(Buff.GetAsUnsigned()); + return true; +} + +template <class Tp, size_t N> +bool GetSysctl(std::string const& Name, std::array<Tp, N>* Out) { + auto Buff = GetSysctlImp(Name); + if (!Buff) return false; + *Out = Buff.GetAsArray<Tp, N>(); + return true; } #endif -#if defined BENCHMARK_OS_LINUX || defined BENCHMARK_OS_CYGWIN -static std::string convertToLowerCase(std::string s) { - for (auto& ch : s) - ch = std::tolower(ch); - return s; +template <class ArgT> +bool ReadFromFile(std::string const& fname, ArgT* arg) { + *arg = ArgT(); + std::ifstream f(fname.c_str()); + if (!f.is_open()) return false; + f >> *arg; + return f.good(); +} + +bool CpuScalingEnabled(int num_cpus) { + // We don't have a valid CPU count, so don't even bother. + if (num_cpus <= 0) return false; +#ifndef BENCHMARK_OS_WINDOWS + // On Linux, the CPUfreq subsystem exposes CPU information as files on the + // local file system. If reading the exported files fails, then we may not be + // running on Linux, so we silently ignore all the read errors. + std::string res; + for (int cpu = 0; cpu < num_cpus; ++cpu) { + std::string governor_file = + StrCat("/sys/devices/system/cpu/cpu", cpu, "/cpufreq/scaling_governor"); + if (ReadFromFile(governor_file, &res) && res != "performance") return true; + } +#endif + return false; } -static bool startsWithKey(std::string Value, std::string Key, - bool IgnoreCase = true) { - if (IgnoreCase) { - Key = convertToLowerCase(std::move(Key)); - Value = convertToLowerCase(std::move(Value)); + +int CountSetBitsInCPUMap(std::string Val) { + auto CountBits = [](std::string Part) { + using CPUMask = std::bitset<sizeof(std::uintptr_t) * CHAR_BIT>; + Part = "0x" + Part; + CPUMask Mask(benchmark::stoul(Part, nullptr, 16)); + return static_cast<int>(Mask.count()); + }; + size_t Pos; + int total = 0; + while ((Pos = Val.find(',')) != std::string::npos) { + total += CountBits(Val.substr(0, Pos)); + Val = Val.substr(Pos + 1); + } + if (!Val.empty()) { + total += CountBits(Val); + } + return total; +} + +BENCHMARK_MAYBE_UNUSED +std::vector<CPUInfo::CacheInfo> GetCacheSizesFromKVFS() { + std::vector<CPUInfo::CacheInfo> res; + std::string dir = "/sys/devices/system/cpu/cpu0/cache/"; + int Idx = 0; + while (true) { + CPUInfo::CacheInfo info; + std::string FPath = StrCat(dir, "index", Idx++, "/"); + std::ifstream f(StrCat(FPath, "size").c_str()); + if (!f.is_open()) break; + std::string suffix; + f >> info.size; + if (f.fail()) + PrintErrorAndDie("Failed while reading file '", FPath, "size'"); + if (f.good()) { + f >> suffix; + if (f.bad()) + PrintErrorAndDie( + "Invalid cache size format: failed to read size suffix"); + else if (f && suffix != "K") + PrintErrorAndDie("Invalid cache size format: Expected bytes ", suffix); + else if (suffix == "K") + info.size *= 1000; + } + if (!ReadFromFile(StrCat(FPath, "type"), &info.type)) + PrintErrorAndDie("Failed to read from file ", FPath, "type"); + if (!ReadFromFile(StrCat(FPath, "level"), &info.level)) + PrintErrorAndDie("Failed to read from file ", FPath, "level"); + std::string map_str; + if (!ReadFromFile(StrCat(FPath, "shared_cpu_map"), &map_str)) + PrintErrorAndDie("Failed to read from file ", FPath, "shared_cpu_map"); + info.num_sharing = CountSetBitsInCPUMap(map_str); + res.push_back(info); + } + + return res; +} + +#ifdef BENCHMARK_OS_MACOSX +std::vector<CPUInfo::CacheInfo> GetCacheSizesMacOSX() { + std::vector<CPUInfo::CacheInfo> res; + std::array<uint64_t, 4> CacheCounts{{0, 0, 0, 0}}; + GetSysctl("hw.cacheconfig", &CacheCounts); + + struct { + std::string name; + std::string type; + int level; + size_t num_sharing; + } Cases[] = {{"hw.l1dcachesize", "Data", 1, CacheCounts[1]}, + {"hw.l1icachesize", "Instruction", 1, CacheCounts[1]}, + {"hw.l2cachesize", "Unified", 2, CacheCounts[2]}, + {"hw.l3cachesize", "Unified", 3, CacheCounts[3]}}; + for (auto& C : Cases) { + int val; + if (!GetSysctl(C.name, &val)) continue; + CPUInfo::CacheInfo info; + info.type = C.type; + info.level = C.level; + info.size = val; + info.num_sharing = static_cast<int>(C.num_sharing); + res.push_back(std::move(info)); + } + return res; +} +#elif defined(BENCHMARK_OS_WINDOWS) +std::vector<CPUInfo::CacheInfo> GetCacheSizesWindows() { + std::vector<CPUInfo::CacheInfo> res; + DWORD buffer_size = 0; + using PInfo = SYSTEM_LOGICAL_PROCESSOR_INFORMATION; + using CInfo = CACHE_DESCRIPTOR; + + using UPtr = std::unique_ptr<PInfo, decltype(&std::free)>; + GetLogicalProcessorInformation(nullptr, &buffer_size); + UPtr buff((PInfo*)malloc(buffer_size), &std::free); + if (!GetLogicalProcessorInformation(buff.get(), &buffer_size)) + PrintErrorAndDie("Failed during call to GetLogicalProcessorInformation: ", + GetLastError()); + + PInfo* it = buff.get(); + PInfo* end = buff.get() + (buffer_size / sizeof(PInfo)); + + for (; it != end; ++it) { + if (it->Relationship != RelationCache) continue; + using BitSet = std::bitset<sizeof(ULONG_PTR) * CHAR_BIT>; + BitSet B(it->ProcessorMask); + // To prevent duplicates, only consider caches where CPU 0 is specified + if (!B.test(0)) continue; + CInfo* Cache = &it->Cache; + CPUInfo::CacheInfo C; + C.num_sharing = static_cast<int>(B.count()); + C.level = Cache->Level; + C.size = Cache->Size; + switch (Cache->Type) { + case CacheUnified: + C.type = "Unified"; + break; + case CacheInstruction: + C.type = "Instruction"; + break; + case CacheData: + C.type = "Data"; + break; + case CacheTrace: + C.type = "Trace"; + break; + default: + C.type = "Unknown"; + break; + } + res.push_back(C); } - return Value.compare(0, Key.size(), Key) == 0; + return res; +} +#endif + +std::vector<CPUInfo::CacheInfo> GetCacheSizes() { +#ifdef BENCHMARK_OS_MACOSX + return GetCacheSizesMacOSX(); +#elif defined(BENCHMARK_OS_WINDOWS) + return GetCacheSizesWindows(); +#else + return GetCacheSizesFromKVFS(); +#endif } + +int GetNumCPUs() { +#ifdef BENCHMARK_HAS_SYSCTL + int NumCPU = -1; + if (GetSysctl("hw.ncpu", &NumCPU)) return NumCPU; + fprintf(stderr, "Err: %s\n", strerror(errno)); + std::exit(EXIT_FAILURE); +#elif defined(BENCHMARK_OS_WINDOWS) + SYSTEM_INFO sysinfo; + // Use memset as opposed to = {} to avoid GCC missing initializer false + // positives. + std::memset(&sysinfo, 0, sizeof(SYSTEM_INFO)); + GetSystemInfo(&sysinfo); + return sysinfo.dwNumberOfProcessors; // number of logical + // processors in the current + // group +#elif defined(BENCHMARK_OS_SOLARIS) + // Returns -1 in case of a failure. + int NumCPU = sysconf(_SC_NPROCESSORS_ONLN); + if (NumCPU < 0) { + fprintf(stderr, + "sysconf(_SC_NPROCESSORS_ONLN) failed with error: %s\n", + strerror(errno)); + } + return NumCPU; +#else + int NumCPUs = 0; + int MaxID = -1; + std::ifstream f("/proc/cpuinfo"); + if (!f.is_open()) { + std::cerr << "failed to open /proc/cpuinfo\n"; + return -1; + } + const std::string Key = "processor"; + std::string ln; + while (std::getline(f, ln)) { + if (ln.empty()) continue; + size_t SplitIdx = ln.find(':'); + std::string value; + if (SplitIdx != std::string::npos) value = ln.substr(SplitIdx + 1); + if (ln.size() >= Key.size() && ln.compare(0, Key.size(), Key) == 0) { + NumCPUs++; + if (!value.empty()) { + int CurID = benchmark::stoi(value); + MaxID = std::max(CurID, MaxID); + } + } + } + if (f.bad()) { + std::cerr << "Failure reading /proc/cpuinfo\n"; + return -1; + } + if (!f.eof()) { + std::cerr << "Failed to read to end of /proc/cpuinfo\n"; + return -1; + } + f.close(); + + if ((MaxID + 1) != NumCPUs) { + fprintf(stderr, + "CPU ID assignments in /proc/cpuinfo seem messed up." + " This is usually caused by a bad BIOS.\n"); + } + return NumCPUs; #endif + BENCHMARK_UNREACHABLE(); +} -void InitializeSystemInfo() { +double GetCPUCyclesPerSecond() { #if defined BENCHMARK_OS_LINUX || defined BENCHMARK_OS_CYGWIN - char line[1024]; - char* err; long freq; - bool saw_mhz = false; - // If the kernel is exporting the tsc frequency use that. There are issues // where cpuinfo_max_freq cannot be relied on because the BIOS may be // exporintg an invalid p-state (on x86) or p-states may be used to put the // processor in a new mode (turbo mode). Essentially, those frequencies // cannot always be relied upon. The same reasons apply to /proc/cpuinfo as // well. - if (!saw_mhz && - ReadIntFromFile("/sys/devices/system/cpu/cpu0/tsc_freq_khz", &freq)) { + if (ReadFromFile("/sys/devices/system/cpu/cpu0/tsc_freq_khz", &freq) + // If CPU scaling is in effect, we want to use the *maximum* frequency, + // not whatever CPU speed some random processor happens to be using now. + || ReadFromFile("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", + &freq)) { // The value is in kHz (as the file name suggests). For example, on a // 2GHz warpstation, the file contains the value "2000000". - cpuinfo_cycles_per_second = freq * 1000.0; - saw_mhz = true; + return freq * 1000.0; } - // If CPU scaling is in effect, we want to use the *maximum* frequency, - // not whatever CPU speed some random processor happens to be using now. - if (!saw_mhz && - ReadIntFromFile("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", - &freq)) { - // The value is in kHz. For example, on a 2GHz warpstation, the file - // contains the value "2000000". - cpuinfo_cycles_per_second = freq * 1000.0; - saw_mhz = true; - } + const double error_value = -1; + double bogo_clock = error_value; - // Read /proc/cpuinfo for other values, and if there is no cpuinfo_max_freq. - const char* pname = "/proc/cpuinfo"; - int fd = open(pname, O_RDONLY); - if (fd == -1) { - perror(pname); - if (!saw_mhz) { - cpuinfo_cycles_per_second = - static_cast<double>(EstimateCyclesPerSecond()); - } - return; + std::ifstream f("/proc/cpuinfo"); + if (!f.is_open()) { + std::cerr << "failed to open /proc/cpuinfo\n"; + return error_value; } - double bogo_clock = 1.0; - bool saw_bogo = false; - long max_cpu_id = 0; - int num_cpus = 0; - line[0] = line[1] = '\0'; - size_t chars_read = 0; - do { // we'll exit when the last read didn't read anything - // Move the next line to the beginning of the buffer - const size_t oldlinelen = strlen(line); - if (sizeof(line) == oldlinelen + 1) // oldlinelen took up entire line - line[0] = '\0'; - else // still other lines left to save - memmove(line, line + oldlinelen + 1, sizeof(line) - (oldlinelen + 1)); - // Terminate the new line, reading more if we can't find the newline - char* newline = strchr(line, '\n'); - if (newline == nullptr) { - const size_t linelen = strlen(line); - const size_t bytes_to_read = sizeof(line) - 1 - linelen; - CHECK(bytes_to_read > 0); // because the memmove recovered >=1 bytes - chars_read = read(fd, line + linelen, bytes_to_read); - line[linelen + chars_read] = '\0'; - newline = strchr(line, '\n'); - } - if (newline != nullptr) *newline = '\0'; + auto startsWithKey = [](std::string const& Value, std::string const& Key) { + if (Key.size() > Value.size()) return false; + auto Cmp = [&](char X, char Y) { + return std::tolower(X) == std::tolower(Y); + }; + return std::equal(Key.begin(), Key.end(), Value.begin(), Cmp); + }; + std::string ln; + while (std::getline(f, ln)) { + if (ln.empty()) continue; + size_t SplitIdx = ln.find(':'); + std::string value; + if (SplitIdx != std::string::npos) value = ln.substr(SplitIdx + 1); // When parsing the "cpu MHz" and "bogomips" (fallback) entries, we only - // accept postive values. Some environments (virtual machines) report zero, + // accept positive values. Some environments (virtual machines) report zero, // which would cause infinite looping in WallTime_Init. - if (!saw_mhz && startsWithKey(line, "cpu MHz")) { - const char* freqstr = strchr(line, ':'); - if (freqstr) { - cpuinfo_cycles_per_second = strtod(freqstr + 1, &err) * 1000000.0; - if (freqstr[1] != '\0' && *err == '\0' && cpuinfo_cycles_per_second > 0) - saw_mhz = true; + if (startsWithKey(ln, "cpu MHz")) { + if (!value.empty()) { + double cycles_per_second = benchmark::stod(value) * 1000000.0; + if (cycles_per_second > 0) return cycles_per_second; } - } else if (startsWithKey(line, "bogomips")) { - const char* freqstr = strchr(line, ':'); - if (freqstr) { - bogo_clock = strtod(freqstr + 1, &err) * 1000000.0; - if (freqstr[1] != '\0' && *err == '\0' && bogo_clock > 0) - saw_bogo = true; - } - } else if (startsWithKey(line, "processor", /*IgnoreCase*/false)) { - // The above comparison is case-sensitive because ARM kernels often - // include a "Processor" line that tells you about the CPU, distinct - // from the usual "processor" lines that give you CPU ids. No current - // Linux architecture is using "Processor" for CPU ids. - num_cpus++; // count up every time we see an "processor :" entry - const char* id_str = strchr(line, ':'); - if (id_str) { - const long cpu_id = strtol(id_str + 1, &err, 10); - if (id_str[1] != '\0' && *err == '\0' && max_cpu_id < cpu_id) - max_cpu_id = cpu_id; + } else if (startsWithKey(ln, "bogomips")) { + if (!value.empty()) { + bogo_clock = benchmark::stod(value) * 1000000.0; + if (bogo_clock < 0.0) bogo_clock = error_value; } } - } while (chars_read > 0); - close(fd); - - if (!saw_mhz) { - if (saw_bogo) { - // If we didn't find anything better, we'll use bogomips, but - // we're not happy about it. - cpuinfo_cycles_per_second = bogo_clock; - } else { - // If we don't even have bogomips, we'll use the slow estimation. - cpuinfo_cycles_per_second = - static_cast<double>(EstimateCyclesPerSecond()); - } } - if (num_cpus == 0) { - fprintf(stderr, "Failed to read num. CPUs correctly from /proc/cpuinfo\n"); - } else { - if ((max_cpu_id + 1) != num_cpus) { - fprintf(stderr, - "CPU ID assignments in /proc/cpuinfo seem messed up." - " This is usually caused by a bad BIOS.\n"); - } - cpuinfo_num_cpus = num_cpus; + if (f.bad()) { + std::cerr << "Failure reading /proc/cpuinfo\n"; + return error_value; } + if (!f.eof()) { + std::cerr << "Failed to read to end of /proc/cpuinfo\n"; + return error_value; + } + f.close(); + // If we found the bogomips clock, but nothing better, we'll use it (but + // we're not happy about it); otherwise, fallback to the rough estimation + // below. + if (bogo_clock >= 0.0) return bogo_clock; -#elif defined BENCHMARK_OS_FREEBSD -// For this sysctl to work, the machine must be configured without -// SMP, APIC, or APM support. hz should be 64-bit in freebsd 7.0 -// and later. Before that, it's a 32-bit quantity (and gives the -// wrong answer on machines faster than 2^32 Hz). See -// http://lists.freebsd.org/pipermail/freebsd-i386/2004-November/001846.html -// But also compare FreeBSD 7.0: -// http://fxr.watson.org/fxr/source/i386/i386/tsc.c?v=RELENG70#L223 -// 231 error = sysctl_handle_quad(oidp, &freq, 0, req); -// To FreeBSD 6.3 (it's the same in 6-STABLE): -// http://fxr.watson.org/fxr/source/i386/i386/tsc.c?v=RELENG6#L131 -// 139 error = sysctl_handle_int(oidp, &freq, sizeof(freq), req); -#if __FreeBSD__ >= 7 - uint64_t hz = 0; +#elif defined BENCHMARK_HAS_SYSCTL + constexpr auto* FreqStr = +#if defined(BENCHMARK_OS_FREEBSD) || defined(BENCHMARK_OS_NETBSD) + "machdep.tsc_freq"; +#elif defined BENCHMARK_OS_OPENBSD + "hw.cpuspeed"; #else - unsigned int hz = 0; + "hw.cpufrequency"; #endif - size_t sz = sizeof(hz); - const char* sysctl_path = "machdep.tsc_freq"; - if (sysctlbyname(sysctl_path, &hz, &sz, nullptr, 0) != 0) { - fprintf(stderr, "Unable to determine clock rate from sysctl: %s: %s\n", - sysctl_path, strerror(errno)); - cpuinfo_cycles_per_second = static_cast<double>(EstimateCyclesPerSecond()); - } else { - cpuinfo_cycles_per_second = hz; - } -// TODO: also figure out cpuinfo_num_cpus + unsigned long long hz = 0; +#if defined BENCHMARK_OS_OPENBSD + if (GetSysctl(FreqStr, &hz)) return hz * 1000000; +#else + if (GetSysctl(FreqStr, &hz)) return hz; +#endif + fprintf(stderr, "Unable to determine clock rate from sysctl: %s: %s\n", + FreqStr, strerror(errno)); #elif defined BENCHMARK_OS_WINDOWS // In NT, read MHz from the registry. If we fail to do so or we're in win9x @@ -267,89 +532,56 @@ void InitializeSystemInfo() { SHGetValueA(HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", "~MHz", nullptr, &data, &data_size))) - cpuinfo_cycles_per_second = - static_cast<double>((int64_t)data * (int64_t)(1000 * 1000)); // was mhz - else - cpuinfo_cycles_per_second = static_cast<double>(EstimateCyclesPerSecond()); - - SYSTEM_INFO sysinfo; - // Use memset as opposed to = {} to avoid GCC missing initializer false - // positives. - std::memset(&sysinfo, 0, sizeof(SYSTEM_INFO)); - GetSystemInfo(&sysinfo); - cpuinfo_num_cpus = sysinfo.dwNumberOfProcessors; // number of logical - // processors in the current - // group - -#elif defined BENCHMARK_OS_MACOSX - int32_t num_cpus = 0; - size_t size = sizeof(num_cpus); - if (::sysctlbyname("hw.ncpu", &num_cpus, &size, nullptr, 0) == 0 && - (size == sizeof(num_cpus))) { - cpuinfo_num_cpus = num_cpus; - } else { - fprintf(stderr, "%s\n", strerror(errno)); - std::exit(EXIT_FAILURE); + return static_cast<double>((int64_t)data * + (int64_t)(1000 * 1000)); // was mhz +#elif defined (BENCHMARK_OS_SOLARIS) + kstat_ctl_t *kc = kstat_open(); + if (!kc) { + std::cerr << "failed to open /dev/kstat\n"; + return -1; } - int64_t cpu_freq = 0; - size = sizeof(cpu_freq); - if (::sysctlbyname("hw.cpufrequency", &cpu_freq, &size, nullptr, 0) == 0 && - (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 + kstat_t *ksp = kstat_lookup(kc, (char*)"cpu_info", -1, (char*)"cpu_info0"); + if (!ksp) { + std::cerr << "failed to lookup in /dev/kstat\n"; + return -1; } -#else - // Generic cycles per second counter - cpuinfo_cycles_per_second = static_cast<double>(EstimateCyclesPerSecond()); + if (kstat_read(kc, ksp, NULL) < 0) { + std::cerr << "failed to read from /dev/kstat\n"; + return -1; + } + kstat_named_t *knp = + (kstat_named_t*)kstat_data_lookup(ksp, (char*)"current_clock_Hz"); + if (!knp) { + std::cerr << "failed to lookup data in /dev/kstat\n"; + return -1; + } + if (knp->data_type != KSTAT_DATA_UINT64) { + std::cerr << "current_clock_Hz is of unexpected data type: " + << knp->data_type << "\n"; + return -1; + } + double clock_hz = knp->value.ui64; + kstat_close(kc); + return clock_hz; #endif + // If we've fallen through, attempt to roughly estimate the CPU clock rate. + const int estimate_time_ms = 1000; + const auto start_ticks = cycleclock::Now(); + SleepForMilliseconds(estimate_time_ms); + return static_cast<double>(cycleclock::Now() - start_ticks); } } // end namespace -double CyclesPerSecond(void) { - std::call_once(cpuinfo_init, InitializeSystemInfo); - return cpuinfo_cycles_per_second; -} - -int NumCPUs(void) { - std::call_once(cpuinfo_init, InitializeSystemInfo); - return cpuinfo_num_cpus; +const CPUInfo& CPUInfo::Get() { + static const CPUInfo* info = new CPUInfo(); + return *info; } -// The ""'s catch people who don't pass in a literal for "str" -#define strliterallen(str) (sizeof("" str "") - 1) - -// Must use a string literal for prefix. -#define memprefix(str, len, prefix) \ - ((((len) >= strliterallen(prefix)) && \ - std::memcmp(str, prefix, strliterallen(prefix)) == 0) \ - ? str + strliterallen(prefix) \ - : nullptr) - -bool CpuScalingEnabled() { -#ifndef BENCHMARK_OS_WINDOWS - // On Linux, the CPUfreq subsystem exposes CPU information as files on the - // local file system. If reading the exported files fails, then we may not be - // running on Linux, so we silently ignore all the read errors. - for (int cpu = 0, num_cpus = NumCPUs(); cpu < num_cpus; ++cpu) { - std::string governor_file = - StrCat("/sys/devices/system/cpu/cpu", cpu, "/cpufreq/scaling_governor"); - FILE* file = fopen(governor_file.c_str(), "r"); - if (!file) break; - char buff[16]; - size_t bytes_read = fread(buff, 1, sizeof(buff), file); - fclose(file); - if (memprefix(buff, bytes_read, "performance") == nullptr) return true; - } -#endif - return false; -} +CPUInfo::CPUInfo() + : num_cpus(GetNumCPUs()), + cycles_per_second(GetCPUCyclesPerSecond()), + caches(GetCacheSizes()), + scaling_enabled(CpuScalingEnabled(num_cpus)) {} } // end namespace benchmark diff --git a/lib/libcxx/utils/google-benchmark/src/thread_manager.h b/lib/libcxx/utils/google-benchmark/src/thread_manager.h new file mode 100644 index 00000000000..82b4d72b62f --- /dev/null +++ b/lib/libcxx/utils/google-benchmark/src/thread_manager.h @@ -0,0 +1,66 @@ +#ifndef BENCHMARK_THREAD_MANAGER_H +#define BENCHMARK_THREAD_MANAGER_H + +#include <atomic> + +#include "benchmark/benchmark.h" +#include "mutex.h" + +namespace benchmark { +namespace internal { + +class ThreadManager { + public: + ThreadManager(int num_threads) + : alive_threads_(num_threads), start_stop_barrier_(num_threads) {} + + Mutex& GetBenchmarkMutex() const RETURN_CAPABILITY(benchmark_mutex_) { + return benchmark_mutex_; + } + + bool StartStopBarrier() EXCLUDES(end_cond_mutex_) { + return start_stop_barrier_.wait(); + } + + void NotifyThreadComplete() EXCLUDES(end_cond_mutex_) { + start_stop_barrier_.removeThread(); + if (--alive_threads_ == 0) { + MutexLock lock(end_cond_mutex_); + end_condition_.notify_all(); + } + } + + void WaitForAllThreads() EXCLUDES(end_cond_mutex_) { + MutexLock lock(end_cond_mutex_); + end_condition_.wait(lock.native_handle(), + [this]() { return alive_threads_ == 0; }); + } + + public: + struct Result { + int64_t iterations = 0; + double real_time_used = 0; + double cpu_time_used = 0; + double manual_time_used = 0; + int64_t bytes_processed = 0; + int64_t items_processed = 0; + int64_t complexity_n = 0; + std::string report_label_; + std::string error_message_; + bool has_error_ = false; + UserCounters counters; + }; + GUARDED_BY(GetBenchmarkMutex()) Result results; + + private: + mutable Mutex benchmark_mutex_; + std::atomic<int> alive_threads_; + Barrier start_stop_barrier_; + Mutex end_cond_mutex_; + Condition end_condition_; +}; + +} // namespace internal +} // namespace benchmark + +#endif // BENCHMARK_THREAD_MANAGER_H diff --git a/lib/libcxx/utils/google-benchmark/src/thread_timer.h b/lib/libcxx/utils/google-benchmark/src/thread_timer.h new file mode 100644 index 00000000000..eaf108e017d --- /dev/null +++ b/lib/libcxx/utils/google-benchmark/src/thread_timer.h @@ -0,0 +1,69 @@ +#ifndef BENCHMARK_THREAD_TIMER_H +#define BENCHMARK_THREAD_TIMER_H + +#include "check.h" +#include "timers.h" + +namespace benchmark { +namespace internal { + +class ThreadTimer { + public: + ThreadTimer() = default; + + // Called by each thread + void StartTimer() { + running_ = true; + start_real_time_ = ChronoClockNow(); + start_cpu_time_ = ThreadCPUUsage(); + } + + // Called by each thread + void StopTimer() { + CHECK(running_); + running_ = false; + real_time_used_ += ChronoClockNow() - start_real_time_; + // Floating point error can result in the subtraction producing a negative + // time. Guard against that. + cpu_time_used_ += std::max<double>(ThreadCPUUsage() - start_cpu_time_, 0); + } + + // Called by each thread + void SetIterationTime(double seconds) { manual_time_used_ += seconds; } + + bool running() const { return running_; } + + // REQUIRES: timer is not running + double real_time_used() { + CHECK(!running_); + return real_time_used_; + } + + // REQUIRES: timer is not running + double cpu_time_used() { + CHECK(!running_); + return cpu_time_used_; + } + + // REQUIRES: timer is not running + double manual_time_used() { + CHECK(!running_); + return manual_time_used_; + } + + private: + bool running_ = false; // Is the timer running + double start_real_time_ = 0; // If running_ + double start_cpu_time_ = 0; // If running_ + + // Accumulated time so far (does not contain current slice if running_) + double real_time_used_ = 0; + double cpu_time_used_ = 0; + // Manually set iteration time. User sets this with SetIterationTime(seconds). + double manual_time_used_ = 0; +}; + +} // namespace internal +} // namespace benchmark + +#endif // BENCHMARK_THREAD_TIMER_H diff --git a/lib/libcxx/utils/google-benchmark/src/timers.cc b/lib/libcxx/utils/google-benchmark/src/timers.cc index 8d56e8adf8d..2010e2450b4 100644 --- a/lib/libcxx/utils/google-benchmark/src/timers.cc +++ b/lib/libcxx/utils/google-benchmark/src/timers.cc @@ -17,11 +17,14 @@ #ifdef BENCHMARK_OS_WINDOWS #include <Shlwapi.h> +#undef StrCat // Don't let StrCat in string_util.h be renamed to lstrcatA #include <VersionHelpers.h> #include <Windows.h> #else #include <fcntl.h> +#ifndef BENCHMARK_OS_FUCHSIA #include <sys/resource.h> +#endif #include <sys/time.h> #include <sys/types.h> // this header must be included before 'sys/sysctl.h' to avoid compilation error on FreeBSD #include <unistd.h> @@ -74,7 +77,7 @@ double MakeTime(FILETIME const& kernel_time, FILETIME const& user_time) { static_cast<double>(user.QuadPart)) * 1e-7; } -#else +#elif !defined(BENCHMARK_OS_FUCHSIA) double MakeTime(struct rusage const& ru) { return (static_cast<double>(ru.ru_utime.tv_sec) + static_cast<double>(ru.ru_utime.tv_usec) * 1e-6 + @@ -158,6 +161,14 @@ double ThreadCPUUsage() { #elif defined(BENCHMARK_OS_EMSCRIPTEN) // Emscripten doesn't support traditional threads return ProcessCPUUsage(); +#elif defined(BENCHMARK_OS_RTEMS) + // RTEMS doesn't support CLOCK_THREAD_CPUTIME_ID. See + // https://github.com/RTEMS/rtems/blob/master/cpukit/posix/src/clockgettime.c + return ProcessCPUUsage(); +#elif defined(BENCHMARK_OS_SOLARIS) + struct rusage ru; + if (getrusage(RUSAGE_LWP, &ru) == 0) return MakeTime(ru); + DiagnoseAndExit("getrusage(RUSAGE_LWP, ...) failed"); #elif defined(CLOCK_THREAD_CPUTIME_ID) struct timespec ts; if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) == 0) return MakeTime(ts); @@ -182,7 +193,6 @@ std::string DateTimeString(bool local) { std::strftime(storage, sizeof(storage), "%x %X", ::localtime(&now)); #else std::tm timeinfo; - std::memset(&timeinfo, 0, sizeof(std::tm)); ::localtime_r(&now, &timeinfo); written = std::strftime(storage, sizeof(storage), "%F %T", &timeinfo); #endif @@ -191,7 +201,6 @@ std::string DateTimeString(bool local) { written = std::strftime(storage, sizeof(storage), "%x %X", ::gmtime(&now)); #else std::tm timeinfo; - std::memset(&timeinfo, 0, sizeof(std::tm)); ::gmtime_r(&now, &timeinfo); written = std::strftime(storage, sizeof(storage), "%F %T", &timeinfo); #endif |