diff --git a/CMakeLists.txt b/CMakeLists.txt index 86f61429..dfa8a651 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -147,9 +147,14 @@ if (BENCHMARK_USE_LIBCXX) endif(BENCHMARK_USE_LIBCXX) # C++ feature checks +# Determine the correct regular expression engine to use cxx_feature_check(STD_REGEX) cxx_feature_check(GNU_POSIX_REGEX) cxx_feature_check(POSIX_REGEX) +if(NOT HAVE_STD_REGEX AND NOT HAVE_GNU_POSIX_REGEX AND NOT HAVE_POSIX_REGEX) + message(FATAL_ERROR "Failed to determine the source files for the regular expression backend") +endif() + cxx_feature_check(STEADY_CLOCK) # Ensure we have pthreads find_package(Threads REQUIRED) diff --git a/include/benchmark/benchmark_api.h b/include/benchmark/benchmark_api.h index abfbad2c..33e481a9 100644 --- a/include/benchmark/benchmark_api.h +++ b/include/benchmark/benchmark_api.h @@ -154,6 +154,7 @@ BENCHMARK(BM_test)->Unit(benchmark::kMillisecond); #include #include +#include #include "macros.h" @@ -273,6 +274,17 @@ typedef double(BigOFunc)(int); namespace internal { class ThreadTimer; class ThreadManager; + +#if defined(BENCHMARK_HAS_CXX11) +enum ReportMode : unsigned { +#else +enum ReportMode { +#endif + RM_Unspecified, // The mode has not been manually specified + RM_Default, // The mode is user-specified as default. + RM_ReportAggregatesOnly +}; + } // State is passed to a running Benchmark and contains state for the @@ -616,9 +628,25 @@ protected: Benchmark(Benchmark const&); void SetName(const char* name); + int ArgsCnt() const; + + static void AddRange(std::vector* dst, int lo, int hi, int mult); + private: friend class BenchmarkFamilies; - BenchmarkImp* imp_; + + std::string name_; + ReportMode report_mode_; + std::vector< std::vector > args_; // Args for all benchmark runs + TimeUnit time_unit_; + int range_multiplier_; + double min_time_; + int repetitions_; + bool use_real_time_; + bool use_manual_time_; + BigO complexity_; + BigOFunc* complexity_lambda_; + std::vector thread_counts_; Benchmark& operator=(Benchmark const&); }; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f78a1fd3..40388751 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,35 +6,13 @@ if (DEFINED BENCHMARK_CXX_LINKER_FLAGS) list(APPEND CMAKE_MODULE_LINKER_FLAGS ${BENCHMARK_CXX_LINKER_FLAGS}) endif() -# Define the source files -set(SOURCE_FILES "benchmark.cc" "colorprint.cc" "commandlineflags.cc" - "console_reporter.cc" "csv_reporter.cc" - "json_reporter.cc" "reporter.cc" "sleep.cc" - "string_util.cc" "sysinfo.cc" "complexity.cc" "timers.cc") -# Add headers to the list of source files. cmake does not require this, -# but IDEs such as Visual Studio need this to add the headers -# to the generated project. -set(_d "${PROJECT_SOURCE_DIR}/include/benchmark") -list(APPEND SOURCE_FILES "${_d}/benchmark.h" "${_d}/benchmark_api.h" - "${_d}/macros.h" "${_d}/reporter.h" "arraysize.h" "check.h" - "colorprint.h" "commandlineflags.h" "complexity.h" - "cycleclock.h" "internal_macros.h" "log.h" "mutex.h" - "re.h" "sleep.h" "stat.h" "string_util.h" "sysinfo.h" "timers.h") -unset(_d) - -# Determine the correct regular expression engine to use -if(HAVE_STD_REGEX) - set(RE_FILES "re_std.cc") -elseif(HAVE_GNU_POSIX_REGEX) - set(RE_FILES "re_posix.cc") -elseif(HAVE_POSIX_REGEX) - set(RE_FILES "re_posix.cc") -else() - message(FATAL_ERROR "Failed to determine the source files for the regular expression backend") -endif() - -add_library(benchmark ${SOURCE_FILES} ${RE_FILES}) +file(GLOB + SOURCE_FILES + *.cc + ${PROJECT_SOURCE_DIR}/include/benchmark/*.h + ${CMAKE_CURRENT_SOURCE_DIR}/*.h) +add_library(benchmark ${SOURCE_FILES}) set_target_properties(benchmark PROPERTIES OUTPUT_NAME "benchmark" VERSION ${GENERIC_LIB_VERSION} diff --git a/src/benchmark.cc b/src/benchmark.cc index a48f5ab3..2ab8e8b8 100644 --- a/src/benchmark.cc +++ b/src/benchmark.cc @@ -14,6 +14,7 @@ #include "benchmark/benchmark.h" #include "internal_macros.h" +#include "benchmark_api_internal.h" #ifndef BENCHMARK_OS_WINDOWS #include @@ -87,7 +88,6 @@ DEFINE_int32(v, 0, "The level of verbose logging to output"); namespace benchmark { - namespace internal { void UseCharPointer(char const volatile*) {} @@ -96,34 +96,19 @@ void UseCharPointer(char const volatile*) {} namespace { -bool IsZero(double n) { - return std::abs(n) < std::numeric_limits::epsilon(); -} - -// For non-dense Range, intermediate values are powers of kRangeMultiplier. -static const int kRangeMultiplier = 8; -// The size of a benchmark family determines is the number of inputs to repeat -// the benchmark on. If this is "large" then warn the user during configuration. -static const size_t kMaxFamilySize = 100; static const size_t kMaxIterations = 1000000000; } // end namespace namespace internal { -// NOTE: This is a dummy "mutex" type used to denote the actual mutex -// returned by GetBenchmarkMutex(). This is only used to placate the thread -// safety warnings by giving the return of GetBenchmarkLock() a name. -struct CAPABILITY("mutex") BenchmarkLockType {}; -BenchmarkLockType BenchmarkLockVar; class ThreadManager { public: ThreadManager(int num_threads) : alive_threads_(num_threads), start_stop_barrier_(num_threads) {} - Mutex& GetBenchmarkMutex() const - RETURN_CAPABILITY(::benchmark::internal::BenchmarkLockVar) { + Mutex& GetBenchmarkMutex() const RETURN_CAPABILITY(benchmark_mutex_) { return benchmark_mutex_; } @@ -224,504 +209,6 @@ class ThreadTimer { }; - -enum ReportMode : unsigned { - RM_Unspecified, // The mode has not been manually specified - RM_Default, // The mode is user-specified as default. - RM_ReportAggregatesOnly -}; - -// Information kept per benchmark we may want to run -struct Benchmark::Instance { - std::string name; - Benchmark* benchmark; - ReportMode report_mode; - std::vector arg; - TimeUnit time_unit; - int range_multiplier; - bool use_real_time; - bool use_manual_time; - BigO complexity; - BigOFunc* complexity_lambda; - bool last_benchmark_instance; - int repetitions; - double min_time; - int threads; // Number of concurrent threads to use - bool multithreaded; // Is benchmark multi-threaded? -}; - -// Class for managing registered benchmarks. Note that each registered -// benchmark identifies a family of related benchmarks to run. -class BenchmarkFamilies { - public: - static BenchmarkFamilies* GetInstance(); - - // Registers a benchmark family and returns the index assigned to it. - size_t AddBenchmark(std::unique_ptr family); - - // Extract the list of benchmark instances that match the specified - // regular expression. - bool FindBenchmarks(const std::string& re, - std::vector* benchmarks, - std::ostream* Err); - private: - BenchmarkFamilies() {} - - std::vector> families_; - Mutex mutex_; -}; - - -class BenchmarkImp { -public: - explicit BenchmarkImp(const char* name); - ~BenchmarkImp(); - - void Arg(int x); - void Unit(TimeUnit unit); - void Range(int start, int limit); - void DenseRange(int start, int limit, int step = 1); - void Args(const std::vector& args); - void Ranges(const std::vector>& ranges); - void RangeMultiplier(int multiplier); - void MinTime(double n); - void Repetitions(int n); - void ReportAggregatesOnly(bool v); - void UseRealTime(); - void UseManualTime(); - void Complexity(BigO complexity); - void ComplexityLambda(BigOFunc* complexity); - void Threads(int t); - void ThreadRange(int min_threads, int max_threads); - void DenseThreadRange(int min_threads, int max_threads, int stride); - void ThreadPerCpu(); - void SetName(const char* name); - - static void AddRange(std::vector* dst, int lo, int hi, int mult); - - int ArgsCnt() const { return args_.empty() ? -1 : static_cast(args_.front().size()); } - -private: - friend class BenchmarkFamilies; - - std::string name_; - ReportMode report_mode_; - std::vector< std::vector > args_; // Args for all benchmark runs - TimeUnit time_unit_; - int range_multiplier_; - double min_time_; - int repetitions_; - bool use_real_time_; - bool use_manual_time_; - BigO complexity_; - BigOFunc* complexity_lambda_; - std::vector thread_counts_; - - BenchmarkImp& operator=(BenchmarkImp const&); -}; - -BenchmarkFamilies* BenchmarkFamilies::GetInstance() { - static BenchmarkFamilies instance; - return &instance; -} - - -size_t BenchmarkFamilies::AddBenchmark(std::unique_ptr family) { - MutexLock l(mutex_); - size_t index = families_.size(); - families_.push_back(std::move(family)); - return index; -} - -bool BenchmarkFamilies::FindBenchmarks( - const std::string& spec, - std::vector* benchmarks, - std::ostream* ErrStream) { - CHECK(ErrStream); - auto& Err = *ErrStream; - // Make regular expression out of command-line flag - std::string error_msg; - Regex re; - if (!re.Init(spec, &error_msg)) { - Err << "Could not compile benchmark re: " << error_msg << std::endl; - return false; - } - - // Special list of thread counts to use when none are specified - const std::vector one_thread = {1}; - - MutexLock l(mutex_); - for (std::unique_ptr& bench_family : families_) { - // Family was deleted or benchmark doesn't match - if (!bench_family) continue; - BenchmarkImp* family = bench_family->imp_; - - if (family->ArgsCnt() == -1) { - family->Args({}); - } - const std::vector* thread_counts = - (family->thread_counts_.empty() - ? &one_thread - : &static_cast&>(family->thread_counts_)); - const size_t family_size = family->args_.size() * thread_counts->size(); - // The benchmark will be run at least 'family_size' different inputs. - // If 'family_size' is very large warn the user. - if (family_size > kMaxFamilySize) { - Err << "The number of inputs is very large. " << family->name_ - << " will be repeated at least " << family_size << " times.\n"; - } - // reserve in the special case the regex ".", since we know the final - // family size. - if (spec == ".") - benchmarks->reserve(family_size); - - for (auto const& args : family->args_) { - for (int num_threads : *thread_counts) { - - Benchmark::Instance instance; - instance.name = family->name_; - instance.benchmark = bench_family.get(); - instance.report_mode = family->report_mode_; - instance.arg = args; - instance.time_unit = family->time_unit_; - instance.range_multiplier = family->range_multiplier_; - instance.min_time = family->min_time_; - instance.repetitions = family->repetitions_; - instance.use_real_time = family->use_real_time_; - instance.use_manual_time = family->use_manual_time_; - instance.complexity = family->complexity_; - instance.complexity_lambda = family->complexity_lambda_; - instance.threads = num_threads; - instance.multithreaded = !(family->thread_counts_.empty()); - - // Add arguments to instance name - for (auto const& arg : args) { - AppendHumanReadable(arg, &instance.name); - } - - if (!IsZero(family->min_time_)) { - instance.name += StringPrintF("/min_time:%0.3f", family->min_time_); - } - if (family->repetitions_ != 0) { - instance.name += StringPrintF("/repeats:%d", family->repetitions_); - } - if (family->use_manual_time_) { - instance.name += "/manual_time"; - } else if (family->use_real_time_) { - instance.name += "/real_time"; - } - - // Add the number of threads used to the name - if (!family->thread_counts_.empty()) { - instance.name += StringPrintF("/threads:%d", instance.threads); - } - - if (re.Match(instance.name)) { - instance.last_benchmark_instance = (&args == &family->args_.back()); - benchmarks->push_back(std::move(instance)); - } - } - } - } - return true; -} - -BenchmarkImp::BenchmarkImp(const char* name) - : name_(name), report_mode_(RM_Unspecified), time_unit_(kNanosecond), - range_multiplier_(kRangeMultiplier), min_time_(0.0), repetitions_(0), - use_real_time_(false), use_manual_time_(false), - complexity_(oNone) { -} - -BenchmarkImp::~BenchmarkImp() { -} - -void BenchmarkImp::Arg(int x) { - CHECK(ArgsCnt() == -1 || ArgsCnt() == 1); - args_.push_back({x}); -} - -void BenchmarkImp::Unit(TimeUnit unit) { - time_unit_ = unit; -} - -void BenchmarkImp::Range(int start, int limit) { - CHECK(ArgsCnt() == -1 || ArgsCnt() == 1); - std::vector arglist; - AddRange(&arglist, start, limit, range_multiplier_); - - for (int i : arglist) { - args_.push_back({i}); - } -} - -void BenchmarkImp::DenseRange(int start, int limit, int step) { - CHECK(ArgsCnt() == -1 || ArgsCnt() == 1); - CHECK_GE(start, 0); - CHECK_LE(start, limit); - for (int arg = start; arg <= limit; arg+= step) { - args_.push_back({arg}); - } -} - -void BenchmarkImp::Args(const std::vector& args) -{ - args_.push_back(args); -} - -void BenchmarkImp::Ranges(const std::vector>& ranges) { - std::vector> 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, range_multiplier_); - total *= arglists[i].size(); - } - - std::vector ctr(arglists.size(), 0); - - for (std::size_t i = 0; i < total; i++) { - std::vector tmp; - tmp.reserve(arglists.size()); - - for (std::size_t j = 0; j < arglists.size(); j++) { - tmp.push_back(arglists[j].at(ctr[j])); - } - - args_.push_back(std::move(tmp)); - - for (std::size_t j = 0; j < arglists.size(); j++) { - if (ctr[j] + 1 < arglists[j].size()) { - ++ctr[j]; - break; - } - ctr[j] = 0; - } - } -} - -void BenchmarkImp::RangeMultiplier(int multiplier) { - CHECK(multiplier > 1); - range_multiplier_ = multiplier; -} - -void BenchmarkImp::MinTime(double t) { - CHECK(t > 0.0); - min_time_ = t; -} - - -void BenchmarkImp::Repetitions(int n) { - CHECK(n > 0); - repetitions_ = n; -} - -void BenchmarkImp::ReportAggregatesOnly(bool value) { - report_mode_ = value ? RM_ReportAggregatesOnly : RM_Default; -} - -void BenchmarkImp::UseRealTime() { - CHECK(!use_manual_time_) << "Cannot set UseRealTime and UseManualTime simultaneously."; - use_real_time_ = true; -} - -void BenchmarkImp::UseManualTime() { - CHECK(!use_real_time_) << "Cannot set UseRealTime and UseManualTime simultaneously."; - use_manual_time_ = true; -} - -void BenchmarkImp::Complexity(BigO complexity){ - complexity_ = complexity; -} - -void BenchmarkImp::ComplexityLambda(BigOFunc* complexity) { - complexity_lambda_ = complexity; -} - -void BenchmarkImp::Threads(int t) { - CHECK_GT(t, 0); - thread_counts_.push_back(t); -} - -void BenchmarkImp::ThreadRange(int min_threads, int max_threads) { - CHECK_GT(min_threads, 0); - CHECK_GE(max_threads, min_threads); - - AddRange(&thread_counts_, min_threads, max_threads, 2); -} - -void BenchmarkImp::DenseThreadRange(int min_threads, int max_threads, - int stride) { - CHECK_GT(min_threads, 0); - CHECK_GE(max_threads, min_threads); - CHECK_GE(stride, 1); - - for (auto i = min_threads; i < max_threads; i += stride) { - thread_counts_.push_back(i); - } - thread_counts_.push_back(max_threads); -} - -void BenchmarkImp::ThreadPerCpu() { - static int num_cpus = NumCPUs(); - thread_counts_.push_back(num_cpus); -} - -void BenchmarkImp::SetName(const char* name) { - name_ = name; -} - -void BenchmarkImp::AddRange(std::vector* 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::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(const char* name) - : imp_(new BenchmarkImp(name)) -{ -} - -Benchmark::~Benchmark() { - delete imp_; -} - -Benchmark::Benchmark(Benchmark const& other) - : imp_(new BenchmarkImp(*other.imp_)) -{ -} - -Benchmark* Benchmark::Arg(int x) { - CHECK(imp_->ArgsCnt() == -1 || imp_->ArgsCnt() == 1); - imp_->Arg(x); - return this; -} - -Benchmark* Benchmark::Unit(TimeUnit unit) { - imp_->Unit(unit); - return this; -} - -Benchmark* Benchmark::Range(int start, int limit) { - CHECK(imp_->ArgsCnt() == -1 || imp_->ArgsCnt() == 1); - imp_->Range(start, limit); - return this; -} - -Benchmark* Benchmark::Ranges(const std::vector>& ranges) -{ - CHECK(imp_->ArgsCnt() == -1 || imp_->ArgsCnt() == static_cast(ranges.size())); - imp_->Ranges(ranges); - return this; -} - -Benchmark* Benchmark::DenseRange(int start, int limit, int step) { - CHECK(imp_->ArgsCnt() == -1 || imp_->ArgsCnt() == 1); - imp_->DenseRange(start, limit, step); - return this; -} - -Benchmark* Benchmark::Args(const std::vector& args) { - CHECK(imp_->ArgsCnt() == -1 || imp_->ArgsCnt() == static_cast(args.size())); - imp_->Args(args); - return this; -} - -Benchmark* Benchmark::Apply(void (*custom_arguments)(Benchmark* benchmark)) { - custom_arguments(this); - return this; -} - -Benchmark* Benchmark::RangeMultiplier(int multiplier) { - imp_->RangeMultiplier(multiplier); - return this; -} - - -Benchmark* Benchmark::Repetitions(int t) { - imp_->Repetitions(t); - return this; -} - -Benchmark* Benchmark::ReportAggregatesOnly(bool value) { - imp_->ReportAggregatesOnly(value); - return this; -} - -Benchmark* Benchmark::MinTime(double t) { - imp_->MinTime(t); - return this; -} - -Benchmark* Benchmark::UseRealTime() { - imp_->UseRealTime(); - return this; -} - -Benchmark* Benchmark::UseManualTime() { - imp_->UseManualTime(); - return this; -} - -Benchmark* Benchmark::Complexity(BigO complexity) { - imp_->Complexity(complexity); - return this; -} - -Benchmark* Benchmark::Complexity(BigOFunc* complexity) { - imp_->Complexity(oLambda); - imp_->ComplexityLambda(complexity); - return this; -} - -Benchmark* Benchmark::Threads(int t) { - imp_->Threads(t); - return this; -} - -Benchmark* Benchmark::ThreadRange(int min_threads, int max_threads) { - imp_->ThreadRange(min_threads, max_threads); - return this; -} - -Benchmark *Benchmark::DenseThreadRange(int min_threads, int max_threads, - int stride) { - imp_->DenseThreadRange(min_threads, max_threads, stride); - return this; -} - -Benchmark* Benchmark::ThreadPerCpu() { - imp_->ThreadPerCpu(); - return this; -} - -void Benchmark::SetName(const char* name) { - imp_->SetName(name); -} - -void FunctionBenchmark::Run(State& st) { - func_(st); -} - -} // end namespace internal - namespace { // Execute one thread of benchmark b for the specified number of iterations. @@ -883,7 +370,8 @@ std::vector RunBenchmark( return reports; } -} // namespace +} // namespace +} // namespace internal State::State(size_t max_iters, const std::vector& ranges, int thread_i, int n_threads, internal::ThreadTimer* timer, @@ -1072,8 +560,7 @@ size_t RunSpecifiedBenchmarks(BenchmarkReporter* console_reporter, } std::vector benchmarks; - auto families = internal::BenchmarkFamilies::GetInstance(); - if (!families->FindBenchmarks(spec, &benchmarks, &Err)) return 0; + if (!FindBenchmarksInternal(spec, &benchmarks, &Err)) return 0; if (FLAGS_benchmark_list_tests) { for (auto const& benchmark : benchmarks) @@ -1141,13 +628,6 @@ void ParseCommandLineFlags(int* argc, char** argv) { } } -Benchmark* RegisterBenchmarkInternal(Benchmark* bench) { - std::unique_ptr bench_ptr(bench); - BenchmarkFamilies* families = BenchmarkFamilies::GetInstance(); - families->AddBenchmark(std::move(bench_ptr)); - return bench; -} - int InitializeStreams() { static std::ios_base::Init init; return 0; diff --git a/src/benchmark_api_internal.h b/src/benchmark_api_internal.h new file mode 100644 index 00000000..a491f347 --- /dev/null +++ b/src/benchmark_api_internal.h @@ -0,0 +1,47 @@ +#ifndef BENCHMARK_API_INTERNAL_H +#define BENCHMARK_API_INTERNAL_H + +#include "benchmark/benchmark_api.h" + +#include +#include +#include +#include +#include + +namespace benchmark { +namespace internal { + +// Information kept per benchmark we may want to run +struct Benchmark::Instance { + std::string name; + Benchmark* benchmark; + ReportMode report_mode; + std::vector arg; + TimeUnit time_unit; + int range_multiplier; + bool use_real_time; + bool use_manual_time; + BigO complexity; + BigOFunc* complexity_lambda; + bool last_benchmark_instance; + int repetitions; + double min_time; + int threads; // Number of concurrent threads to use + bool multithreaded; // Is benchmark multi-threaded? +}; + +bool FindBenchmarksInternal(const std::string& re, + std::vector* benchmarks, std::ostream* Err); + +namespace { + +bool IsZero(double n) { + return std::abs(n) < std::numeric_limits::epsilon(); +} + +} // end namespace +} // end namespace internal +} // end namespace benchmark + +#endif // BENCHMARK_API_INTERNAL_H diff --git a/src/benchmark_register.cc b/src/benchmark_register.cc new file mode 100644 index 00000000..321c6a41 --- /dev/null +++ b/src/benchmark_register.cc @@ -0,0 +1,416 @@ +// Copyright 2015 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "benchmark/benchmark.h" +#include "internal_macros.h" +#include "benchmark_api_internal.h" + +#ifndef BENCHMARK_OS_WINDOWS +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "check.h" +#include "commandlineflags.h" +#include "complexity.h" +#include "log.h" +#include "mutex.h" +#include "re.h" +#include "stat.h" +#include "string_util.h" +#include "sysinfo.h" +#include "timers.h" + +namespace benchmark { + +namespace { +// For non-dense Range, intermediate values are powers of kRangeMultiplier. +static const int kRangeMultiplier = 8; +// The size of a benchmark family determines is the number of inputs to repeat +// the benchmark on. If this is "large" then warn the user during configuration. +static const size_t kMaxFamilySize = 100; +} // end namespace + +namespace internal { + +//=============================================================================// +// BenchmarkFamilies +//=============================================================================// + +// Class for managing registered benchmarks. Note that each registered +// benchmark identifies a family of related benchmarks to run. +class BenchmarkFamilies { + public: + static BenchmarkFamilies* GetInstance(); + + // Registers a benchmark family and returns the index assigned to it. + size_t AddBenchmark(std::unique_ptr family); + + // Extract the list of benchmark instances that match the specified + // regular expression. + bool FindBenchmarks(const std::string& re, + std::vector* benchmarks, + std::ostream* Err); + private: + BenchmarkFamilies() {} + + std::vector> families_; + Mutex mutex_; +}; + +BenchmarkFamilies* BenchmarkFamilies::GetInstance() { + static BenchmarkFamilies instance; + return &instance; +} + +size_t BenchmarkFamilies::AddBenchmark(std::unique_ptr family) { + MutexLock l(mutex_); + size_t index = families_.size(); + families_.push_back(std::move(family)); + return index; +} + +bool BenchmarkFamilies::FindBenchmarks( + const std::string& spec, + std::vector* benchmarks, + std::ostream* ErrStream) { + CHECK(ErrStream); + auto& Err = *ErrStream; + // Make regular expression out of command-line flag + std::string error_msg; + Regex re; + if (!re.Init(spec, &error_msg)) { + Err << "Could not compile benchmark re: " << error_msg << std::endl; + return false; + } + + // Special list of thread counts to use when none are specified + const std::vector one_thread = {1}; + + MutexLock l(mutex_); + for (std::unique_ptr& family : families_) { + // Family was deleted or benchmark doesn't match + if (!family) continue; + + if (family->ArgsCnt() == -1) { + family->Args({}); + } + const std::vector* thread_counts = + (family->thread_counts_.empty() + ? &one_thread + : &static_cast&>(family->thread_counts_)); + const size_t family_size = family->args_.size() * thread_counts->size(); + // The benchmark will be run at least 'family_size' different inputs. + // If 'family_size' is very large warn the user. + if (family_size > kMaxFamilySize) { + Err << "The number of inputs is very large. " << family->name_ + << " will be repeated at least " << family_size << " times.\n"; + } + // reserve in the special case the regex ".", since we know the final + // family size. + if (spec == ".") + benchmarks->reserve(family_size); + + for (auto const& args : family->args_) { + for (int num_threads : *thread_counts) { + + Benchmark::Instance instance; + instance.name = family->name_; + instance.benchmark = family.get(); + instance.report_mode = family->report_mode_; + instance.arg = args; + instance.time_unit = family->time_unit_; + instance.range_multiplier = family->range_multiplier_; + instance.min_time = family->min_time_; + instance.repetitions = family->repetitions_; + instance.use_real_time = family->use_real_time_; + instance.use_manual_time = family->use_manual_time_; + instance.complexity = family->complexity_; + instance.complexity_lambda = family->complexity_lambda_; + instance.threads = num_threads; + instance.multithreaded = !(family->thread_counts_.empty()); + + // Add arguments to instance name + for (auto const& arg : args) { + AppendHumanReadable(arg, &instance.name); + } + + if (!IsZero(family->min_time_)) { + instance.name += StringPrintF("/min_time:%0.3f", family->min_time_); + } + if (family->repetitions_ != 0) { + instance.name += StringPrintF("/repeats:%d", family->repetitions_); + } + if (family->use_manual_time_) { + instance.name += "/manual_time"; + } else if (family->use_real_time_) { + instance.name += "/real_time"; + } + + // Add the number of threads used to the name + if (!family->thread_counts_.empty()) { + instance.name += StringPrintF("/threads:%d", instance.threads); + } + + if (re.Match(instance.name)) { + instance.last_benchmark_instance = (&args == &family->args_.back()); + benchmarks->push_back(std::move(instance)); + } + } + } + } + return true; +} + +Benchmark* RegisterBenchmarkInternal(Benchmark* bench) { + std::unique_ptr bench_ptr(bench); + BenchmarkFamilies* families = BenchmarkFamilies::GetInstance(); + families->AddBenchmark(std::move(bench_ptr)); + return bench; +} + + +// FIXME: This function is a hack so that benchmark.cc can access +// `BenchmarkFamilies` +bool FindBenchmarksInternal(const std::string& re, + std::vector* benchmarks, + std::ostream* Err) +{ + return BenchmarkFamilies::GetInstance()->FindBenchmarks(re, benchmarks, Err); +} + + +//=============================================================================// +// Benchmark +//=============================================================================// + +Benchmark::Benchmark(const char* name) + : name_(name), report_mode_(RM_Unspecified), + time_unit_(kNanosecond), range_multiplier_(kRangeMultiplier), + min_time_(0), repetitions_(0), use_real_time_(false), use_manual_time_(false), + complexity_(oNone) +{ +} + +Benchmark::~Benchmark() { +} + +void Benchmark::AddRange(std::vector* 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::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) { + CHECK(ArgsCnt() == -1 || ArgsCnt() == 1); + args_.push_back({x}); + return this; +} + +Benchmark* Benchmark::Unit(TimeUnit unit) { + time_unit_ = unit; + return this; +} + +Benchmark* Benchmark::Range(int start, int limit) { + CHECK(ArgsCnt() == -1 || ArgsCnt() == 1); + std::vector arglist; + AddRange(&arglist, start, limit, range_multiplier_); + + for (int i : arglist) { + args_.push_back({i}); + } + return this; +} + +Benchmark* Benchmark::Ranges(const std::vector>& ranges) +{ + CHECK(ArgsCnt() == -1 || ArgsCnt() == static_cast(ranges.size())); + std::vector> 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, range_multiplier_); + total *= arglists[i].size(); + } + + std::vector ctr(arglists.size(), 0); + + for (std::size_t i = 0; i < total; i++) { + std::vector tmp; + tmp.reserve(arglists.size()); + + for (std::size_t j = 0; j < arglists.size(); j++) { + tmp.push_back(arglists[j].at(ctr[j])); + } + + args_.push_back(std::move(tmp)); + + for (std::size_t j = 0; j < arglists.size(); j++) { + if (ctr[j] + 1 < arglists[j].size()) { + ++ctr[j]; + break; + } + ctr[j] = 0; + } + } + return this; +} + +Benchmark* Benchmark::DenseRange(int start, int limit, int step) { + CHECK(ArgsCnt() == -1 || ArgsCnt() == 1); + CHECK_GE(start, 0); + CHECK_LE(start, limit); + for (int arg = start; arg <= limit; arg+= step) { + args_.push_back({arg}); + } + return this; +} + +Benchmark* Benchmark::Args(const std::vector& args) { + CHECK(ArgsCnt() == -1 || ArgsCnt() == static_cast(args.size())); + args_.push_back(args); + return this; +} + +Benchmark* Benchmark::Apply(void (*custom_arguments)(Benchmark* benchmark)) { + custom_arguments(this); + return this; +} + +Benchmark* Benchmark::RangeMultiplier(int multiplier) { + CHECK(multiplier > 1); + range_multiplier_ = multiplier; + return this; +} + +Benchmark* Benchmark::Repetitions(int n) { + CHECK(n > 0); + repetitions_ = n; + return this; +} + +Benchmark* Benchmark::ReportAggregatesOnly(bool value) { + report_mode_ = value ? RM_ReportAggregatesOnly : RM_Default; + return this; +} + +Benchmark* Benchmark::MinTime(double t) { + CHECK(t > 0.0); + min_time_ = t; + return this; +} + +Benchmark* Benchmark::UseRealTime() { + CHECK(!use_manual_time_) << "Cannot set UseRealTime and UseManualTime simultaneously."; + use_real_time_ = true; + return this; +} + +Benchmark* Benchmark::UseManualTime() { + CHECK(!use_real_time_) << "Cannot set UseRealTime and UseManualTime simultaneously."; + use_manual_time_ = true; + return this; +} + +Benchmark* Benchmark::Complexity(BigO complexity) { + complexity_ = complexity; + return this; +} + +Benchmark* Benchmark::Complexity(BigOFunc* complexity) { + complexity_lambda_ = complexity; + complexity_ = oLambda; + return this; +} + +Benchmark* Benchmark::Threads(int t) { + CHECK_GT(t, 0); + thread_counts_.push_back(t); + return this; +} + +Benchmark* Benchmark::ThreadRange(int min_threads, int max_threads) { + CHECK_GT(min_threads, 0); + CHECK_GE(max_threads, min_threads); + + AddRange(&thread_counts_, min_threads, max_threads, 2); + return this; +} + +Benchmark *Benchmark::DenseThreadRange(int min_threads, int max_threads, + int stride) { + CHECK_GT(min_threads, 0); + CHECK_GE(max_threads, min_threads); + CHECK_GE(stride, 1); + + for (auto i = min_threads; i < max_threads; i += stride) { + thread_counts_.push_back(i); + } + thread_counts_.push_back(max_threads); + return this; +} + +Benchmark* Benchmark::ThreadPerCpu() { + static int num_cpus = NumCPUs(); + thread_counts_.push_back(num_cpus); + return this; +} + +void Benchmark::SetName(const char* name) { + name_ = name; +} + +int Benchmark::ArgsCnt() const +{ return args_.empty() ? -1 : static_cast(args_.front().size()); } + + +//=============================================================================// +// FunctionBenchmark +//=============================================================================// + +void FunctionBenchmark::Run(State& st) { + func_(st); +} + +} // end namespace internal +} // end namespace benchmark diff --git a/src/re.h b/src/re.h index af57a39c..7c939c05 100644 --- a/src/re.h +++ b/src/re.h @@ -26,13 +26,16 @@ #endif #include +#include "check.h" + namespace benchmark { // A wrapper around the POSIX regular expression API that provides automatic // cleanup class Regex { public: - Regex(); + Regex() : init_(false) {} + ~Regex(); // Compile a regular expression matcher from spec. Returns true on success. @@ -43,7 +46,7 @@ class Regex { // Returns whether str matches the compiled regular expression. bool Match(const std::string& str); - private: +private: bool init_; // Underlying regular expression object #if defined(HAVE_STD_REGEX) @@ -55,6 +58,69 @@ class Regex { #endif }; + +#if defined(HAVE_STD_REGEX) + +inline bool Regex::Init(const std::string& spec, std::string* error) { + try { + re_ = std::regex(spec, std::regex_constants::extended); + + init_ = true; + } catch (const std::regex_error& e) { + if (error) { + *error = e.what(); + } + } + return init_; +} + +inline Regex::~Regex() { } + +inline bool Regex::Match(const std::string& str) { + if (!init_) { + return false; + } + return std::regex_search(str, re_); +} + +#else +inline bool Regex::Init(const std::string& spec, std::string* error) { + int ec = regcomp(&re_, spec.c_str(), REG_EXTENDED | REG_NOSUB); + if (ec != 0) { + if (error) { + size_t needed = regerror(ec, &re_, nullptr, 0); + char* errbuf = new char[needed]; + regerror(ec, &re_, errbuf, needed); + + // regerror returns the number of bytes necessary to null terminate + // the string, so we move that when assigning to error. + CHECK_NE(needed, 0); + error->assign(errbuf, needed - 1); + + delete[] errbuf; + } + + return false; + } + + init_ = true; + return true; +} + +inline Regex::~Regex() { + if (init_) { + regfree(&re_); + } +} + +inline bool Regex::Match(const std::string& str) { + if (!init_) { + return false; + } + return regexec(&re_, str.c_str(), 0, nullptr, 0) == 0; +} +#endif + } // end namespace benchmark #endif // BENCHMARK_RE_H_ diff --git a/src/re_posix.cc b/src/re_posix.cc deleted file mode 100644 index 95b086ff..00000000 --- a/src/re_posix.cc +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2015 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "check.h" -#include "re.h" - -namespace benchmark { - -Regex::Regex() : init_(false) { } - -bool Regex::Init(const std::string& spec, std::string* error) { - int ec = regcomp(&re_, spec.c_str(), REG_EXTENDED | REG_NOSUB); - if (ec != 0) { - if (error) { - size_t needed = regerror(ec, &re_, nullptr, 0); - char* errbuf = new char[needed]; - regerror(ec, &re_, errbuf, needed); - - // regerror returns the number of bytes necessary to null terminate - // the string, so we move that when assigning to error. - CHECK_NE(needed, 0); - error->assign(errbuf, needed - 1); - - delete[] errbuf; - } - - return false; - } - - init_ = true; - return true; -} - -Regex::~Regex() { - if (init_) { - regfree(&re_); - } -} - -bool Regex::Match(const std::string& str) { - if (!init_) { - return false; - } - - return regexec(&re_, str.c_str(), 0, nullptr, 0) == 0; -} - -} // end namespace benchmark diff --git a/src/re_std.cc b/src/re_std.cc deleted file mode 100644 index cfd7a218..00000000 --- a/src/re_std.cc +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2015 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "re.h" - -namespace benchmark { - -Regex::Regex() : init_(false) { } - -bool Regex::Init(const std::string& spec, std::string* error) { - try { - re_ = std::regex(spec, std::regex_constants::extended); - - init_ = true; - } catch (const std::regex_error& e) { - if (error) { - *error = e.what(); - } - } - return init_; -} - -Regex::~Regex() { } - -bool Regex::Match(const std::string& str) { - if (!init_) { - return false; - } - - return std::regex_search(str, re_); -} - -} // end namespace benchmark diff --git a/src/timers.cc b/src/timers.cc index 95e6ceb5..05c7924e 100644 --- a/src/timers.cc +++ b/src/timers.cc @@ -54,7 +54,6 @@ namespace benchmark { // Suppress unused warnings on helper functions. #if defined(__GNUC__) -#pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" #endif @@ -72,9 +71,6 @@ double MakeTime(FILETIME const& kernel_time, FILETIME const& user_time) { 1e-7; } #else -double MakeTime(struct timespec const& ts) { - return ts.tv_sec + (static_cast(ts.tv_nsec) * 1e-9); -} double MakeTime(struct rusage ru) { return (static_cast(ru.ru_utime.tv_sec) + static_cast(ru.ru_utime.tv_usec) * 1e-6 + @@ -90,6 +86,11 @@ double MakeTime(thread_basic_info_data_t const& info) { static_cast(info.user_time.microseconds) * 1e-6); } #endif +#if defined(CLOCK_PROCESS_CPUTIME_ID) || defined(CLOCK_THREAD_CPUTIME_ID) +double MakeTime(struct timespec const& ts) { + return ts.tv_sec + (static_cast(ts.tv_nsec) * 1e-9); +} +#endif BENCHMARK_NORETURN static void DiagnoseAndExit(const char* msg) { std::cerr << "ERROR: " << msg << std::endl; @@ -98,12 +99,14 @@ BENCHMARK_NORETURN static void DiagnoseAndExit(const char* msg) { } // end namespace -#if defined(__GNUC__) -#pragma GCC diagnostic pop -#endif double ProcessCPUUsage() { -#if defined(BENCHMARK_OS_WINDOWS) +#if defined(CLOCK_PROCESS_CPUTIME_ID) + struct timespec spec; + if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &spec) == 0) + return MakeTime(spec); + DiagnoseAndExit("clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ...) failed"); +#elif defined(BENCHMARK_OS_WINDOWS) HANDLE proc = GetCurrentProcess(); FILETIME creation_time; FILETIME exit_time; @@ -112,11 +115,6 @@ double ProcessCPUUsage() { if (GetProcessTimes(proc, &creation_time, &exit_time, &kernel_time, &user_time)) return MakeTime(kernel_time, user_time); DiagnoseAndExit("GetProccessTimes() failed"); -#elif defined(CLOCK_PROCESS_CPUTIME_ID) - struct timespec spec; - if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &spec) == 0) - return MakeTime(spec); - DiagnoseAndExit("clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ...) failed"); #else struct rusage ru; if (getrusage(RUSAGE_SELF, &ru) == 0) @@ -126,7 +124,12 @@ double ProcessCPUUsage() { } double ThreadCPUUsage() { -#if defined(BENCHMARK_OS_WINDOWS) +#if defined(CLOCK_THREAD_CPUTIME_ID) + struct timespec ts; + if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) == 0) + return MakeTime(ts); + DiagnoseAndExit("clock_gettime(CLOCK_THREAD_CPUTIME_ID, ...) failed"); +#elif defined(BENCHMARK_OS_WINDOWS) HANDLE this_thread = GetCurrentThread(); FILETIME creation_time; FILETIME exit_time; @@ -135,11 +138,6 @@ double ThreadCPUUsage() { GetThreadTimes(this_thread, &creation_time, &exit_time, &kernel_time, &user_time); return MakeTime(kernel_time, user_time); -#elif defined(CLOCK_THREAD_CPUTIME_ID) - struct timespec ts; - if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) == 0) - return MakeTime(ts); - DiagnoseAndExit("clock_gettime(CLOCK_THREAD_CPUTIME_ID, ...) failed"); #elif defined(BENCHMARK_OS_MACOSX) mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT; thread_basic_info_data_t info;