Commit 4f9ce87e by Eric Fiselier

Merge branch 'master' into new-benchmark-interface

parents c5f238b1 0a0bb8fe
...@@ -54,6 +54,7 @@ endif() ...@@ -54,6 +54,7 @@ endif()
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)
cxx_feature_check(STEADY_CLOCK)
# Set up directories # Set up directories
include_directories(${PROJECT_SOURCE_DIR}/include) include_directories(${PROJECT_SOURCE_DIR}/include)
......
#include <chrono>
int main() {
typedef std::chrono::steady_clock Clock;
Clock::time_point tp = Clock::now();
((void)tp);
}
...@@ -432,14 +432,27 @@ class Benchmark { ...@@ -432,14 +432,27 @@ class Benchmark {
// ------------------------------------------------------ // ------------------------------------------------------
// Macro to register benchmarks // Macro to register benchmarks
// Check that __COUNTER__ is defined and that __COUNTER__ increases by 1
// every time it is expanded. X + 1 == X + 0 is used in case X is defined to be
// empty. If X is empty the expression becomes (+1 == +0).
#if defined(__COUNTER__) && (__COUNTER__ + 1 == __COUNTER__ + 0)
#define BENCHMARK_PRIVATE_UNIQUE_ID __COUNTER__
#else
#define BENCHMARK_PRIVATE_UNIQUE_ID __LINE__
#endif
// Helpers for generating unique variable names // Helpers for generating unique variable names
#define BENCHMARK_CONCAT(a, b, c) BENCHMARK_CONCAT2(a, b, c) #define BENCHMARK_PRIVATE_NAME(n) \
#define BENCHMARK_CONCAT2(a, b, c) a##b##c BENCHMARK_PRIVATE_CONCAT(_benchmark_, BENCHMARK_PRIVATE_UNIQUE_ID, n)
#define BENCHMARK_PRIVATE_CONCAT(a, b, c) BENCHMARK_PRIVATE_CONCAT2(a, b, c)
#define BENCHMARK_PRIVATE_CONCAT2(a, b, c) a##b##c
#define BENCHMARK_PRIVATE_DECLARE(n) \
static ::benchmark::internal::Benchmark* \
BENCHMARK_PRIVATE_NAME(n) BENCHMARK_UNUSED
#define BENCHMARK(n) \ #define BENCHMARK(n) \
static ::benchmark::internal::Benchmark* BENCHMARK_CONCAT( \ BENCHMARK_PRIVATE_DECLARE(n) = (new ::benchmark::internal::Benchmark(#n, n))
_benchmark_, n, __LINE__) BENCHMARK_UNUSED = \
(new ::benchmark::internal::Benchmark(#n, n))
// Old-style macros // Old-style macros
#define BENCHMARK_WITH_ARG(n, a) BENCHMARK(n)->Arg((a)) #define BENCHMARK_WITH_ARG(n, a) BENCHMARK(n)->Arg((a))
...@@ -456,21 +469,18 @@ class Benchmark { ...@@ -456,21 +469,18 @@ class Benchmark {
// BENCHMARK_TEMPLATE(BM_Foo, 1); // BENCHMARK_TEMPLATE(BM_Foo, 1);
// //
// will register BM_Foo<1> as a benchmark. // will register BM_Foo<1> as a benchmark.
#define BENCHMARK_TEMPLATE1(n, a) \ #define BENCHMARK_TEMPLATE1(n, a) \
static ::benchmark::internal::Benchmark* BENCHMARK_CONCAT( \ BENCHMARK_PRIVATE_DECLARE(n) = \
_benchmark_, n, __LINE__) BENCHMARK_UNUSED = \
(new ::benchmark::internal::Benchmark(#n "<" #a ">", n<a>)) (new ::benchmark::internal::Benchmark(#n "<" #a ">", n<a>))
#define BENCHMARK_TEMPLATE2(n, a, b) \ #define BENCHMARK_TEMPLATE2(n, a, b) \
static ::benchmark::internal::Benchmark* BENCHMARK_CONCAT( \ BENCHMARK_PRIVATE_DECLARE(n) = \
_benchmark_, n, __LINE__) BENCHMARK_UNUSED = \
(new ::benchmark::internal::Benchmark(#n "<" #a "," #b ">", n<a, b>)) (new ::benchmark::internal::Benchmark(#n "<" #a "," #b ">", n<a, b>))
#if __cplusplus >= 201103L #if __cplusplus >= 201103L
#define BENCHMARK_TEMPLATE(n, ...) \ #define BENCHMARK_TEMPLATE(n, ...) \
static ::benchmark::internal::Benchmark* BENCHMARK_CONCAT( \ BENCHMARK_PRIVATE_DECLARE(n) = \
_benchmark_, n, __LINE__) BENCHMARK_UNUSED = \ (new ::benchmark::internal::Benchmark( \
(new ::benchmark::internal::Benchmark( \
#n "<" #__VA_ARGS__ ">", n<__VA_ARGS__>)) #n "<" #__VA_ARGS__ ">", n<__VA_ARGS__>))
#else #else
#define BENCHMARK_TEMPLATE(n, a) BENCHMARK_TEMPLATE1(n, a) #define BENCHMARK_TEMPLATE(n, a) BENCHMARK_TEMPLATE1(n, a)
......
...@@ -65,17 +65,6 @@ DEFINE_bool(color_print, true, "Enables colorized logging."); ...@@ -65,17 +65,6 @@ DEFINE_bool(color_print, true, "Enables colorized logging.");
DEFINE_int32(v, 0, "The level of verbose logging to output"); DEFINE_int32(v, 0, "The level of verbose logging to output");
// 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)
namespace benchmark { namespace benchmark {
namespace internal { namespace internal {
...@@ -116,23 +105,6 @@ std::string* GetReportLabel() { ...@@ -116,23 +105,6 @@ std::string* GetReportLabel() {
// TODO(ericwf): support MallocCounter. // TODO(ericwf): support MallocCounter.
//static benchmark::MallocCounter *benchmark_mc; //static benchmark::MallocCounter *benchmark_mc;
static bool CpuScalingEnabled() {
// 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;
}
return false;
}
struct ThreadStats { struct ThreadStats {
ThreadStats() : bytes_processed(0), items_processed(0) {} ThreadStats() : bytes_processed(0), items_processed(0) {}
int64_t bytes_processed; int64_t bytes_processed;
......
...@@ -65,11 +65,8 @@ bool JSONReporter::ReportContext(const Context& context) { ...@@ -65,11 +65,8 @@ bool JSONReporter::ReportContext(const Context& context) {
// Open context block and print context information. // Open context block and print context information.
out << inner_indent << "\"context\": {\n"; out << inner_indent << "\"context\": {\n";
std::string indent(4, ' '); std::string indent(4, ' ');
int remainder_us;
std::string walltime_value = walltime::Print( std::string walltime_value = LocalDateTimeString();
walltime::Now(), "%Y/%m/%d-%H:%M:%S",
true, // use local timezone
&remainder_us);
out << indent << FormatKV("date", walltime_value) << ",\n"; out << indent << FormatKV("date", walltime_value) << ",\n";
out << indent out << indent
......
...@@ -98,16 +98,13 @@ bool ConsoleReporter::ReportContext(const Context& context) { ...@@ -98,16 +98,13 @@ bool ConsoleReporter::ReportContext(const Context& context) {
context.mhz_per_cpu, context.mhz_per_cpu,
(context.num_cpus > 1) ? "s" : ""); (context.num_cpus > 1) ? "s" : "");
int remainder_us; std::string walltime_str = LocalDateTimeString();
std::string walltime_str = walltime::Print(
walltime::Now(), "%Y/%m/%d-%H:%M:%S",
true, // use local timezone
&remainder_us);
fprintf(stdout, "%s\n", walltime_str.c_str()); fprintf(stdout, "%s\n", walltime_str.c_str());
if (context.cpu_scaling_enabled) { if (context.cpu_scaling_enabled) {
fprintf(stdout, "***WARNING*** CPU scaling is enabled, the benchmark " fprintf(stdout, "***WARNING*** CPU scaling is enabled, the benchmark "
"timings may be noisy\n"); "real time measurements may be noisy and will incure extra "
"overhead.\n");
} }
#ifndef NDEBUG #ifndef NDEBUG
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include "internal_macros.h" #include "internal_macros.h"
#include "log.h" #include "log.h"
#include "sleep.h" #include "sleep.h"
#include "string_util.h"
namespace benchmark { namespace benchmark {
namespace { namespace {
...@@ -352,4 +353,32 @@ int NumCPUs(void) { ...@@ -352,4 +353,32 @@ int NumCPUs(void) {
std::call_once(cpuinfo_init, InitializeSystemInfo); std::call_once(cpuinfo_init, InitializeSystemInfo);
return cpuinfo_num_cpus; return cpuinfo_num_cpus;
} }
// 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() {
// 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;
}
return false;
}
} // end namespace benchmark } // end namespace benchmark
...@@ -6,6 +6,7 @@ double MyCPUUsage(); ...@@ -6,6 +6,7 @@ double MyCPUUsage();
double ChildrenCPUUsage(); double ChildrenCPUUsage();
int NumCPUs(); int NumCPUs();
double CyclesPerSecond(); double CyclesPerSecond();
bool CpuScalingEnabled();
} // end namespace benchmark } // end namespace benchmark
#endif // BENCHMARK_SYSINFO_H_ #endif // BENCHMARK_SYSINFO_H_
...@@ -22,37 +22,40 @@ ...@@ -22,37 +22,40 @@
#include <ctime> #include <ctime>
#include <atomic> #include <atomic>
#include <chrono>
#include <limits> #include <limits>
#include <type_traits> #include <type_traits>
#include "arraysize.h"
#include "check.h" #include "check.h"
#include "cycleclock.h" #include "cycleclock.h"
#include "log.h"
#include "sysinfo.h" #include "sysinfo.h"
namespace benchmark { namespace benchmark {
namespace walltime { namespace walltime {
namespace {
bool SplitTimezone(WallTime value, bool local, struct tm* t, namespace {
double* subsecond) {
memset(t, 0, sizeof(*t));
if ((value < 0) || (value > std::numeric_limits<time_t>::max())) {
*subsecond = 0.0;
return false;
}
const time_t whole_time = static_cast<time_t>(value);
*subsecond = value - whole_time;
if (local)
localtime_r(&whole_time, t);
else
gmtime_r(&whole_time, t);
return true;
}
} // end anonymous namespace #if defined(HAVE_STEADY_CLOCK)
template <bool HighResIsSteady = std::chrono::high_resolution_clock::is_steady>
struct ChooseSteadyClock {
typedef std::chrono::high_resolution_clock type;
};
template <>
struct ChooseSteadyClock<false> {
typedef std::chrono::steady_clock type;
};
#endif
namespace { struct ChooseClockType {
#if defined(HAVE_STEADY_CLOCK)
typedef typename ChooseSteadyClock<>::type type;
#else
typedef std::chrono::high_resolution_clock type;
#endif
};
class WallTimeImp class WallTimeImp
{ {
...@@ -160,32 +163,74 @@ WallTimeImp::WallTimeImp() ...@@ -160,32 +163,74 @@ WallTimeImp::WallTimeImp()
last_adjust_time_ = static_cast<uint32_t>(uint64_t(base_cycletime_) >> 32); last_adjust_time_ = static_cast<uint32_t>(uint64_t(base_cycletime_) >> 32);
} }
} // end anonymous namespace WallTime CPUWalltimeNow() {
static WallTimeImp& imp = WallTimeImp::GetWallTimeImp();
return imp.Now();
}
WallTime ChronoWalltimeNow() {
typedef ChooseClockType::type Clock;
typedef std::chrono::duration<WallTime, std::chrono::seconds::period>
FPSeconds;
static_assert(std::chrono::treat_as_floating_point<WallTime>::value,
"This type must be treated as a floating point type.");
auto now = Clock::now().time_since_epoch();
return std::chrono::duration_cast<FPSeconds>(now).count();
}
bool UseCpuCycleClock() {
bool useWallTime = !CpuScalingEnabled();
if (useWallTime) {
VLOG(1) << "Using the CPU cycle clock to provide walltime::Now().\n";
} else {
VLOG(1) << "Using std::chrono to provide walltime::Now().\n";
}
return useWallTime;
}
} // end anonymous namespace
// WallTimeImp doesn't work when CPU Scaling is enabled. If CPU Scaling is
// enabled at the start of the program then std::chrono::system_clock is used
// instead.
WallTime Now() WallTime Now()
{ {
static WallTimeImp& imp = WallTimeImp::GetWallTimeImp(); static bool useCPUClock = UseCpuCycleClock();
return imp.Now(); if (useCPUClock) {
return CPUWalltimeNow();
} else {
return ChronoWalltimeNow();
}
} }
std::string Print(WallTime time, const char* format, bool local, } // end namespace walltime
int* remainder_us) {
char storage[32];
struct tm split; namespace {
double subsecond;
if (!SplitTimezone(time, local, &split, &subsecond)) { std::string DateTimeString(bool local) {
snprintf(storage, sizeof(storage), "Invalid time: %f", time); typedef std::chrono::system_clock Clock;
std::time_t now = Clock::to_time_t(Clock::now());
char storage[128];
std::tm timeinfo;
std::memset(&timeinfo, 0, sizeof(std::tm));
if (local) {
::localtime_r(&now, &timeinfo);
} else { } else {
if (remainder_us != nullptr) { ::gmtime_r(&now, &timeinfo);
*remainder_us = static_cast<int>((subsecond * 1000000) + 0.5);
if (*remainder_us > 999999) *remainder_us = 999999;
if (*remainder_us < 0) *remainder_us = 0;
}
strftime(storage, sizeof(storage), format, &split);
} }
std::size_t written = std::strftime(storage, sizeof(storage), "%F %T", &timeinfo);
CHECK(written < arraysize(storage));
((void)written); // prevent unused variable in optimized mode.
return std::string(storage); return std::string(storage);
} }
} // end namespace walltime } // end namespace
std::string LocalDateTimeString() {
return DateTimeString(true);
}
} // end namespace benchmark } // end namespace benchmark
...@@ -8,15 +8,10 @@ typedef double WallTime; ...@@ -8,15 +8,10 @@ typedef double WallTime;
namespace walltime { namespace walltime {
WallTime Now(); WallTime Now();
// GIVEN: walltime, generic format string (as understood by strftime),
// a boolean flag specifying if the time is local or UTC (true=local).
// RETURNS: the formatted string. ALSO RETURNS: the remaining number of
// microseconds (never printed in the string since strftime does not understand
// it)
std::string Print(WallTime time, const char *format, bool local,
int *remainder_us);
} // end namespace walltime } // end namespace walltime
std::string LocalDateTimeString();
} // end namespace benchmark } // end namespace benchmark
#endif // BENCHMARK_WALLTIME_H_ #endif // BENCHMARK_WALLTIME_H_
...@@ -59,6 +59,24 @@ void BM_spin_pause_during(benchmark::State& state) { ...@@ -59,6 +59,24 @@ void BM_spin_pause_during(benchmark::State& state) {
BASIC_BENCHMARK_TEST(BM_spin_pause_during); BASIC_BENCHMARK_TEST(BM_spin_pause_during);
BASIC_BENCHMARK_TEST(BM_spin_pause_during)->ThreadPerCpu(); BASIC_BENCHMARK_TEST(BM_spin_pause_during)->ThreadPerCpu();
void BM_pause_during(benchmark::State& state) {
while(state.KeepRunning()) {
state.PauseTiming();
state.ResumeTiming();
}
}
BENCHMARK(BM_pause_during);
BENCHMARK(BM_pause_during)->ThreadPerCpu();
void BM_pause_during_realtime(benchmark::State& state) {
state.UseRealTime();
while(state.KeepRunning()) {
state.PauseTiming();
state.ResumeTiming();
}
}
BENCHMARK(BM_pause_during_realtime);
BENCHMARK(BM_pause_during_realtime)->ThreadPerCpu();
void BM_spin_pause_after(benchmark::State& state) { void BM_spin_pause_after(benchmark::State& state) {
while(state.KeepRunning()) { while(state.KeepRunning()) {
......
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