Commit d038472c by Eric Committed by GitHub

Refactor benchmark.cc into benchmark_register.cc and benchmark.cc (#287)

* Refactor benchmark.cc into benchmark_register.cc and benchmark_run.cc The benchmark.cc file is getting really big and it contains a bunch of unrelated components. This patch separates the files into two separate parts. The "runtime" parts and the "registration" parts. This patch also removes the PIMPL used by Benchmark. Previously we couldn't have STL types in the interface but now we can. Therefore there is no reason to keep BenchmarkImp. * add missing include * rework windows timers again * Guard timespec on older Windows versions * Remove old thread safety annotation workarounds
parent 9c261681
...@@ -147,9 +147,14 @@ if (BENCHMARK_USE_LIBCXX) ...@@ -147,9 +147,14 @@ if (BENCHMARK_USE_LIBCXX)
endif(BENCHMARK_USE_LIBCXX) endif(BENCHMARK_USE_LIBCXX)
# C++ feature checks # C++ feature checks
# Determine the correct regular expression engine to use
cxx_feature_check(STD_REGEX) cxx_feature_check(STD_REGEX)
cxx_feature_check(GNU_POSIX_REGEX) cxx_feature_check(GNU_POSIX_REGEX)
cxx_feature_check(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) cxx_feature_check(STEADY_CLOCK)
# Ensure we have pthreads # Ensure we have pthreads
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
......
...@@ -154,6 +154,7 @@ BENCHMARK(BM_test)->Unit(benchmark::kMillisecond); ...@@ -154,6 +154,7 @@ BENCHMARK(BM_test)->Unit(benchmark::kMillisecond);
#include <stdint.h> #include <stdint.h>
#include <vector> #include <vector>
#include <string>
#include "macros.h" #include "macros.h"
...@@ -273,6 +274,17 @@ typedef double(BigOFunc)(int); ...@@ -273,6 +274,17 @@ typedef double(BigOFunc)(int);
namespace internal { namespace internal {
class ThreadTimer; class ThreadTimer;
class ThreadManager; 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 // State is passed to a running Benchmark and contains state for the
...@@ -616,9 +628,25 @@ protected: ...@@ -616,9 +628,25 @@ protected:
Benchmark(Benchmark const&); Benchmark(Benchmark const&);
void SetName(const char* name); void SetName(const char* name);
int ArgsCnt() const;
static void AddRange(std::vector<int>* dst, int lo, int hi, int mult);
private: private:
friend class BenchmarkFamilies; friend class BenchmarkFamilies;
BenchmarkImp* imp_;
std::string name_;
ReportMode report_mode_;
std::vector< std::vector<int> > 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<int> thread_counts_;
Benchmark& operator=(Benchmark const&); Benchmark& operator=(Benchmark const&);
}; };
......
...@@ -6,35 +6,13 @@ if (DEFINED BENCHMARK_CXX_LINKER_FLAGS) ...@@ -6,35 +6,13 @@ if (DEFINED BENCHMARK_CXX_LINKER_FLAGS)
list(APPEND CMAKE_MODULE_LINKER_FLAGS ${BENCHMARK_CXX_LINKER_FLAGS}) list(APPEND CMAKE_MODULE_LINKER_FLAGS ${BENCHMARK_CXX_LINKER_FLAGS})
endif() endif()
# Define the source files file(GLOB
set(SOURCE_FILES "benchmark.cc" "colorprint.cc" "commandlineflags.cc" SOURCE_FILES
"console_reporter.cc" "csv_reporter.cc" *.cc
"json_reporter.cc" "reporter.cc" "sleep.cc" ${PROJECT_SOURCE_DIR}/include/benchmark/*.h
"string_util.cc" "sysinfo.cc" "complexity.cc" "timers.cc") ${CMAKE_CURRENT_SOURCE_DIR}/*.h)
# 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})
add_library(benchmark ${SOURCE_FILES})
set_target_properties(benchmark PROPERTIES set_target_properties(benchmark PROPERTIES
OUTPUT_NAME "benchmark" OUTPUT_NAME "benchmark"
VERSION ${GENERIC_LIB_VERSION} VERSION ${GENERIC_LIB_VERSION}
......
#ifndef BENCHMARK_API_INTERNAL_H
#define BENCHMARK_API_INTERNAL_H
#include "benchmark/benchmark_api.h"
#include <string>
#include <vector>
#include <limits>
#include <cmath>
#include <iosfwd>
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<int> 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<Benchmark::Instance>* benchmarks, std::ostream* Err);
namespace {
bool IsZero(double n) {
return std::abs(n) < std::numeric_limits<double>::epsilon();
}
} // end namespace
} // end namespace internal
} // end namespace benchmark
#endif // BENCHMARK_API_INTERNAL_H
...@@ -26,13 +26,16 @@ ...@@ -26,13 +26,16 @@
#endif #endif
#include <string> #include <string>
#include "check.h"
namespace benchmark { namespace benchmark {
// A wrapper around the POSIX regular expression API that provides automatic // A wrapper around the POSIX regular expression API that provides automatic
// cleanup // cleanup
class Regex { class Regex {
public: public:
Regex(); Regex() : init_(false) {}
~Regex(); ~Regex();
// Compile a regular expression matcher from spec. Returns true on success. // Compile a regular expression matcher from spec. Returns true on success.
...@@ -43,7 +46,7 @@ class Regex { ...@@ -43,7 +46,7 @@ class Regex {
// Returns whether str matches the compiled regular expression. // Returns whether str matches the compiled regular expression.
bool Match(const std::string& str); bool Match(const std::string& str);
private: private:
bool init_; bool init_;
// Underlying regular expression object // Underlying regular expression object
#if defined(HAVE_STD_REGEX) #if defined(HAVE_STD_REGEX)
...@@ -55,6 +58,69 @@ class Regex { ...@@ -55,6 +58,69 @@ class Regex {
#endif #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 } // end namespace benchmark
#endif // BENCHMARK_RE_H_ #endif // BENCHMARK_RE_H_
// 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
// 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
...@@ -54,7 +54,6 @@ namespace benchmark { ...@@ -54,7 +54,6 @@ namespace benchmark {
// Suppress unused warnings on helper functions. // Suppress unused warnings on helper functions.
#if defined(__GNUC__) #if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function" #pragma GCC diagnostic ignored "-Wunused-function"
#endif #endif
...@@ -72,9 +71,6 @@ double MakeTime(FILETIME const& kernel_time, FILETIME const& user_time) { ...@@ -72,9 +71,6 @@ double MakeTime(FILETIME const& kernel_time, FILETIME const& user_time) {
1e-7; 1e-7;
} }
#else #else
double MakeTime(struct timespec const& ts) {
return ts.tv_sec + (static_cast<double>(ts.tv_nsec) * 1e-9);
}
double MakeTime(struct rusage ru) { double MakeTime(struct rusage ru) {
return (static_cast<double>(ru.ru_utime.tv_sec) + return (static_cast<double>(ru.ru_utime.tv_sec) +
static_cast<double>(ru.ru_utime.tv_usec) * 1e-6 + static_cast<double>(ru.ru_utime.tv_usec) * 1e-6 +
...@@ -90,6 +86,11 @@ double MakeTime(thread_basic_info_data_t const& info) { ...@@ -90,6 +86,11 @@ double MakeTime(thread_basic_info_data_t const& info) {
static_cast<double>(info.user_time.microseconds) * 1e-6); static_cast<double>(info.user_time.microseconds) * 1e-6);
} }
#endif #endif
#if defined(CLOCK_PROCESS_CPUTIME_ID) || defined(CLOCK_THREAD_CPUTIME_ID)
double MakeTime(struct timespec const& ts) {
return ts.tv_sec + (static_cast<double>(ts.tv_nsec) * 1e-9);
}
#endif
BENCHMARK_NORETURN static void DiagnoseAndExit(const char* msg) { BENCHMARK_NORETURN static void DiagnoseAndExit(const char* msg) {
std::cerr << "ERROR: " << msg << std::endl; std::cerr << "ERROR: " << msg << std::endl;
...@@ -98,12 +99,14 @@ BENCHMARK_NORETURN static void DiagnoseAndExit(const char* msg) { ...@@ -98,12 +99,14 @@ BENCHMARK_NORETURN static void DiagnoseAndExit(const char* msg) {
} // end namespace } // end namespace
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
double ProcessCPUUsage() { 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(); HANDLE proc = GetCurrentProcess();
FILETIME creation_time; FILETIME creation_time;
FILETIME exit_time; FILETIME exit_time;
...@@ -112,11 +115,6 @@ double ProcessCPUUsage() { ...@@ -112,11 +115,6 @@ double ProcessCPUUsage() {
if (GetProcessTimes(proc, &creation_time, &exit_time, &kernel_time, &user_time)) if (GetProcessTimes(proc, &creation_time, &exit_time, &kernel_time, &user_time))
return MakeTime(kernel_time, user_time); return MakeTime(kernel_time, user_time);
DiagnoseAndExit("GetProccessTimes() failed"); 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 #else
struct rusage ru; struct rusage ru;
if (getrusage(RUSAGE_SELF, &ru) == 0) if (getrusage(RUSAGE_SELF, &ru) == 0)
...@@ -126,7 +124,12 @@ double ProcessCPUUsage() { ...@@ -126,7 +124,12 @@ double ProcessCPUUsage() {
} }
double ThreadCPUUsage() { 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(); HANDLE this_thread = GetCurrentThread();
FILETIME creation_time; FILETIME creation_time;
FILETIME exit_time; FILETIME exit_time;
...@@ -135,11 +138,6 @@ double ThreadCPUUsage() { ...@@ -135,11 +138,6 @@ double ThreadCPUUsage() {
GetThreadTimes(this_thread, &creation_time, &exit_time, &kernel_time, GetThreadTimes(this_thread, &creation_time, &exit_time, &kernel_time,
&user_time); &user_time);
return MakeTime(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) #elif defined(BENCHMARK_OS_MACOSX)
mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT; mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
thread_basic_info_data_t info; thread_basic_info_data_t info;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment