Commit 8ed7d766 by Eric Fiselier

The second step towards merging the timer changes.

This patch does two things: 1. It overhalls the static initialization in Walltime to be simplier. It uses a static variable inside WallTime::Now() to initialize the timer. 2. Add a logging mechanism so that the -v flag actually has meaning and reimplement the CHECK macros to allow extra messages to be streamed in.
parent b1f34d38
...@@ -3,7 +3,7 @@ include_directories(${PROJECT_SOURCE_DIR}/src) ...@@ -3,7 +3,7 @@ include_directories(${PROJECT_SOURCE_DIR}/src)
# Define the source files # Define the source files
set(SOURCE_FILES "benchmark.cc" "colorprint.cc" "commandlineflags.cc" set(SOURCE_FILES "benchmark.cc" "colorprint.cc" "commandlineflags.cc"
"sleep.cc" "sysinfo.cc" "walltime.cc") "log.cc" "sleep.cc" "sysinfo.cc" "walltime.cc")
# Determine the correct regular expression engine to use # Determine the correct regular expression engine to use
if(HAVE_STD_REGEX) if(HAVE_STD_REGEX)
set(RE_FILES "re_std.cc") set(RE_FILES "re_std.cc")
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "colorprint.h" #include "colorprint.h"
#include "commandlineflags.h" #include "commandlineflags.h"
#include "internal_macros.h" #include "internal_macros.h"
#include "log.h"
#include "re.h" #include "re.h"
#include "sleep.h" #include "sleep.h"
#include "stat.h" #include "stat.h"
...@@ -382,9 +383,7 @@ void BenchmarkFamilies::FindBenchmarks( ...@@ -382,9 +383,7 @@ void BenchmarkFamilies::FindBenchmarks(
// Match against filter. // Match against filter.
if (!re.Match(family->name_)) { if (!re.Match(family->name_)) {
#ifdef DEBUG VLOG(1) << "Skipping " << family->name_ << "\n";
std::cout << "Skipping " << family->name_ << "\n";
#endif
continue; continue;
} }
...@@ -845,9 +844,7 @@ void Benchmark::MeasureOverhead() { ...@@ -845,9 +844,7 @@ void Benchmark::MeasureOverhead() {
} }
overhead = state.runs[0].real_accumulated_time / overhead = state.runs[0].real_accumulated_time /
static_cast<double>(state.runs[0].iterations); static_cast<double>(state.runs[0].iterations);
#ifdef DEBUG VLOG(1) << "Per-iteration overhead for doing nothing: " << overhead << "\n";
std::cout << "Per-iteration overhead for doing nothing: " << overhead << "\n";
#endif
} }
void Benchmark::RunInstance(const Instance& b, const BenchmarkReporter* br) { void Benchmark::RunInstance(const Instance& b, const BenchmarkReporter* br) {
...@@ -1091,20 +1088,16 @@ bool State::StartRunning() { ...@@ -1091,20 +1088,16 @@ bool State::StartRunning() {
void State::NewInterval() { void State::NewInterval() {
stop_time_micros_ = clock_->NowMicros() + interval_micros_; stop_time_micros_ = clock_->NowMicros() + interval_micros_;
if (!is_continuation_) { if (!is_continuation_) {
#ifdef DEBUG VLOG(1) << "Starting new interval; stopping in " << interval_micros_
std::cout << "Starting new interval; stopping in " << interval_micros_ << "\n";
<< "\n";
#endif
iterations_ = 0; iterations_ = 0;
pause_cpu_time_ = 0; pause_cpu_time_ = 0;
pause_real_time_ = 0; pause_real_time_ = 0;
start_cpu_ = MyCPUUsage() + ChildrenCPUUsage(); start_cpu_ = MyCPUUsage() + ChildrenCPUUsage();
start_time_ = walltime::Now(); start_time_ = walltime::Now();
} else { } else {
#ifdef DEBUG VLOG(1) << "Continuing interval; stopping in " << interval_micros_
std::cout << "Continuing interval; stopping in " << interval_micros_ << "\n";
<< "\n";
#endif
} }
} }
...@@ -1114,10 +1107,8 @@ bool State::FinishInterval() { ...@@ -1114,10 +1107,8 @@ bool State::FinishInterval() {
FLAGS_benchmark_iterations / FLAGS_benchmark_repetitions) || FLAGS_benchmark_iterations / FLAGS_benchmark_repetitions) ||
iterations_ < 1) { iterations_ < 1) {
interval_micros_ *= 2; interval_micros_ *= 2;
#ifdef DEBUG VLOG(1) << "Not enough iterations in interval; "
std::cout << "Not enough iterations in interval; " << "Trying again for " << interval_micros_ << " useconds.\n";
<< "Trying again for " << interval_micros_ << " useconds.\n";
#endif
is_continuation_ = false; is_continuation_ = false;
NewInterval(); NewInterval();
return true; return true;
...@@ -1287,8 +1278,8 @@ void RunSpecifiedBenchmarks(const BenchmarkReporter* reporter /*= nullptr*/) { ...@@ -1287,8 +1278,8 @@ void RunSpecifiedBenchmarks(const BenchmarkReporter* reporter /*= nullptr*/) {
} }
void Initialize(int* argc, const char** argv) { void Initialize(int* argc, const char** argv) {
walltime::Initialize();
internal::ParseCommandLineFlags(argc, argv); internal::ParseCommandLineFlags(argc, argv);
internal::SetLogLevel(FLAGS_v);
internal::Benchmark::MeasureOverhead(); internal::Benchmark::MeasureOverhead();
} }
......
#ifndef CHECK_H_ #ifndef CHECK_H_
#define CHECK_H_ #define CHECK_H_
#include <cassert> #include <cstdlib>
#include <ostream>
#include "internal_macros.h"
#include "log.h"
namespace benchmark {
namespace internal {
// CheckHandler is the class constructed by failing CHECK macros. CheckHandler
// will log information about the failures and abort when it is destructed.
class CheckHandler {
public:
CheckHandler(const char* check, const char* file, const char* func, int line)
: log_(GetErrorLogInstance())
{
log_ << file << ":" << line << ": " << func << ": Check `"
<< check << "' failed. ";
}
std::ostream& GetLog() {
return log_;
}
BENCHMARK_NORETURN ~CheckHandler() {
log_ << std::endl;
std::abort();
}
private:
std::ostream& log_;
};
} // end namespace internal
} // end namespace benchmark
// The CHECK macro returns a std::ostream object that can have extra information
// written to it.
#ifndef NDEBUG
# define CHECK(b) (b ? ::benchmark::internal::GetNullLogInstance() \
: ::benchmark::internal::CheckHandler( \
#b, __FILE__, __func__, __LINE__).GetLog())
#else
# define CHECK(b) ::benchmark::internal::GetNullLogInstance()
#endif
#define CHECK(b) \
do { \
if (!(b)) assert(false); \
} while (0)
#define CHECK_EQ(a, b) CHECK((a) == (b)) #define CHECK_EQ(a, b) CHECK((a) == (b))
#define CHECK_NE(a, b) CHECK((a) != (b)) #define CHECK_NE(a, b) CHECK((a) != (b))
#define CHECK_GE(a, b) CHECK((a) >= (b)) #define CHECK_GE(a, b) CHECK((a) >= (b))
......
#include "log.h"
#include <iostream>
namespace benchmark {
namespace internal {
int& LoggingLevelImp() {
static int level = 0;
return level;
}
void SetLogLevel(int value) {
LoggingLevelImp() = value;
}
int GetLogLevel() {
return LoggingLevelImp();
}
class NullLogBuffer : public std::streambuf
{
public:
int overflow(int c) {
return c;
}
};
std::ostream& GetNullLogInstance() {
static NullLogBuffer log_buff;
static std::ostream null_log(&log_buff);
return null_log;
}
std::ostream& GetErrorLogInstance() {
return std::clog;
}
} // end namespace internal
} // end namespace benchmark
\ No newline at end of file
#ifndef BENCHMARK_LOG_H_
#define BENCHMARK_LOG_H_
#include <ostream>
namespace benchmark {
namespace internal {
int GetLogLevel();
void SetLogLevel(int level);
std::ostream& GetNullLogInstance();
std::ostream& GetErrorLogInstance();
inline std::ostream& GetLogInstanceForLevel(int level) {
if (level >= GetLogLevel()) {
return GetErrorLogInstance();
}
return GetNullLogInstance();
}
} // end namespace internal
} // end namespace benchmark
#define VLOG(x) (::benchmark::internal::GetLogInstanceForLevel(x) \
<< "-- LOG(" << x << "): ")
#endif
\ No newline at end of file
// Copyright 2014 Google Inc. All rights reserved. // Copyright 2015 Google Inc. All rights reserved.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
...@@ -14,14 +14,14 @@ ...@@ -14,14 +14,14 @@
#include "walltime.h" #include "walltime.h"
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <sys/time.h> #include <sys/time.h>
#include <time.h> #include <time.h>
#include <cstdio>
#include <cstdint>
#include <cstring>
#include <atomic> #include <atomic>
#include <limits> #include <limits>
#include <type_traits>
#include "check.h" #include "check.h"
#include "cycleclock.h" #include "cycleclock.h"
...@@ -30,44 +30,9 @@ ...@@ -30,44 +30,9 @@
namespace benchmark { namespace benchmark {
namespace walltime { namespace walltime {
namespace { namespace {
const double kMaxErrorInterval = 100e-6;
std::atomic<bool> initialized(false);
WallTime base_walltime = 0.0;
int64_t base_cycletime = 0;
int64_t cycles_per_second;
double seconds_per_cycle;
uint32_t last_adjust_time = 0;
std::atomic<int32_t> drift_adjust(0);
int64_t max_interval_cycles = 0;
// Helper routines to load/store a float from an AtomicWord. Required because
// g++ < 4.7 doesn't support std::atomic<float> correctly. I cannot wait to get
// rid of this horror show.
inline void SetDrift(float f) {
int32_t w;
memcpy(&w, &f, sizeof(f));
std::atomic_store(&drift_adjust, w);
}
inline float GetDrift() {
float f;
int32_t w = std::atomic_load(&drift_adjust);
memcpy(&f, &w, sizeof(f));
return f;
}
static_assert(sizeof(float) <= sizeof(int32_t),
"type sizes don't allow the drift_adjust hack");
WallTime Slow() { inline bool SplitTimezone(WallTime value, bool local, struct tm* t,
struct timeval tv; double* subsecond) {
gettimeofday(&tv, NULL);
return tv.tv_sec + tv.tv_usec * 1e-6;
}
bool SplitTimezone(WallTime value, bool local, struct tm* t,
double* subsecond) {
memset(t, 0, sizeof(*t)); memset(t, 0, sizeof(*t));
if ((value < 0) || (value > std::numeric_limits<time_t>::max())) { if ((value < 0) || (value > std::numeric_limits<time_t>::max())) {
*subsecond = 0.0; *subsecond = 0.0;
...@@ -81,58 +46,127 @@ bool SplitTimezone(WallTime value, bool local, struct tm* t, ...@@ -81,58 +46,127 @@ bool SplitTimezone(WallTime value, bool local, struct tm* t,
gmtime_r(&whole_time, t); gmtime_r(&whole_time, t);
return true; return true;
} }
} // end namespace
// This routine should be invoked to initialize walltime.
// It is not intended for general purpose use.
void Initialize() {
CHECK(!std::atomic_load(&initialized));
cycles_per_second = static_cast<int64_t>(CyclesPerSecond());
CHECK(cycles_per_second != 0);
seconds_per_cycle = 1.0 / cycles_per_second;
max_interval_cycles =
static_cast<int64_t>(cycles_per_second * kMaxErrorInterval);
do {
base_cycletime = cycleclock::Now();
base_walltime = Slow();
} while (cycleclock::Now() - base_cycletime > max_interval_cycles);
// We are now sure that "base_walltime" and "base_cycletime" were produced
// within kMaxErrorInterval of one another.
SetDrift(0.0); } // end anonymous namespace
last_adjust_time = static_cast<uint32_t>(uint64_t(base_cycletime) >> 32);
std::atomic_store(&initialized, true);
} namespace internal {
class WallTimeImp
{
public:
WallTime Now();
static WallTimeImp & GetWallTimeImp() {
static WallTimeImp imp;
#if __cplusplus >= 201103L
static_assert(std::is_trivially_destructible<WallTimeImp>::value,
"WallTimeImp must be trivially destructible to prevent "
"issues with static destruction");
#endif
return imp;
}
private:
WallTimeImp();
// Helper routines to load/store a float from an AtomicWord. Required because
// g++ < 4.7 doesn't support std::atomic<float> correctly. I cannot wait to
// get rid of this horror show.
void SetDrift(float f) {
int32_t w;
memcpy(&w, &f, sizeof(f));
std::atomic_store(&drift_adjust_, w);
}
float GetDrift() const {
float f;
int32_t w = std::atomic_load(&drift_adjust_);
memcpy(&f, &w, sizeof(f));
return f;
}
WallTime Slow() {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec + tv.tv_usec * 1e-6;
}
private:
static_assert(sizeof(float) <= sizeof(int32_t),
"type sizes don't allow the drift_adjust hack");
static constexpr double kMaxErrorInterval = 100e-6;
WallTime Now() { WallTime base_walltime_;
if (!std::atomic_load(&initialized)) return Slow(); int64_t base_cycletime_;
int64_t cycles_per_second_;
double seconds_per_cycle_;
uint32_t last_adjust_time_;
std::atomic<int32_t> drift_adjust_;
int64_t max_interval_cycles_;
BENCHMARK_DISALLOW_COPY_AND_ASSIGN(WallTimeImp);
};
WallTime WallTimeImp::Now() {
WallTime now = 0.0; WallTime now = 0.0;
WallTime result = 0.0; WallTime result = 0.0;
int64_t ct = 0; int64_t ct = 0;
uint32_t top_bits = 0; uint32_t top_bits = 0;
do { do {
ct = cycleclock::Now(); ct = cycleclock::Now();
int64_t cycle_delta = ct - base_cycletime; int64_t cycle_delta = ct - base_cycletime_;
result = base_walltime + cycle_delta * seconds_per_cycle; result = base_walltime_ + cycle_delta * seconds_per_cycle_;
top_bits = static_cast<uint32_t>(uint64_t(ct) >> 32); top_bits = static_cast<uint32_t>(uint64_t(ct) >> 32);
// Recompute drift no more often than every 2^32 cycles. // Recompute drift no more often than every 2^32 cycles.
// I.e., @2GHz, ~ every two seconds // I.e., @2GHz, ~ every two seconds
if (top_bits == last_adjust_time) { // don't need to recompute drift if (top_bits == last_adjust_time_) { // don't need to recompute drift
return result + GetDrift(); return result + GetDrift();
} }
now = Slow(); now = Slow();
} while (cycleclock::Now() - ct > max_interval_cycles); } while (cycleclock::Now() - ct > max_interval_cycles_);
// We are now sure that "now" and "result" were produced within // We are now sure that "now" and "result" were produced within
// kMaxErrorInterval of one another. // kMaxErrorInterval of one another.
SetDrift(now - result); SetDrift(now - result);
last_adjust_time = top_bits; last_adjust_time_ = top_bits;
return now; return now;
} }
WallTimeImp::WallTimeImp()
: base_walltime_(0.0), base_cycletime_(0),
cycles_per_second_(0), seconds_per_cycle_(0.0),
last_adjust_time_(0), drift_adjust_(0),
max_interval_cycles_(0) {
cycles_per_second_ = static_cast<int64_t>(CyclesPerSecond());
CHECK(cycles_per_second_ != 0);
seconds_per_cycle_ = 1.0 / cycles_per_second_;
max_interval_cycles_ =
static_cast<int64_t>(cycles_per_second_ * kMaxErrorInterval);
do {
base_cycletime_ = cycleclock::Now();
base_walltime_ = Slow();
} while (cycleclock::Now() - base_cycletime_ > max_interval_cycles_);
// We are now sure that "base_walltime" and "base_cycletime" were produced
// within kMaxErrorInterval of one another.
SetDrift(0.0);
last_adjust_time_ = static_cast<uint32_t>(uint64_t(base_cycletime_) >> 32);
}
} // end namespace internal
WallTime Now()
{
static internal::WallTimeImp& imp = internal::WallTimeImp::GetWallTimeImp();
return imp.Now();
}
std::string Print(WallTime time, const char* format, bool local, std::string Print(WallTime time, const char* format, bool local,
int* remainder_us) { int* remainder_us) {
char storage[32]; char storage[32];
...@@ -150,5 +184,6 @@ std::string Print(WallTime time, const char* format, bool local, ...@@ -150,5 +184,6 @@ std::string Print(WallTime time, const char* format, bool local,
} }
return std::string(storage); return std::string(storage);
} }
} // end namespace walltime } // end namespace walltime
} // end namespace benchmark } // end namespace benchmark
...@@ -7,7 +7,6 @@ namespace benchmark { ...@@ -7,7 +7,6 @@ namespace benchmark {
typedef double WallTime; typedef double WallTime;
namespace walltime { namespace walltime {
void Initialize();
WallTime Now(); WallTime Now();
// GIVEN: walltime, generic format string (as understood by strftime), // GIVEN: walltime, generic format string (as understood by strftime),
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
namespace { namespace {
#ifdef DEBUG #ifndef NDEBUG
int BENCHMARK_NOINLINE Factorial(uint32_t n) { int BENCHMARK_NOINLINE Factorial(uint32_t n) {
return (n == 1) ? 1 : n * Factorial(n - 1); return (n == 1) ? 1 : n * Factorial(n - 1);
} }
...@@ -52,7 +52,7 @@ std::vector<int>* test_vector = nullptr; ...@@ -52,7 +52,7 @@ std::vector<int>* test_vector = nullptr;
} // end namespace } // end namespace
#ifdef DEBUG #ifndef NDEBUG
static void BM_Factorial(benchmark::State& state) { static void BM_Factorial(benchmark::State& state) {
int fac_42 = 0; int fac_42 = 0;
while (state.KeepRunning()) while (state.KeepRunning())
...@@ -179,7 +179,7 @@ class TestReporter : public benchmark::internal::ConsoleReporter { ...@@ -179,7 +179,7 @@ class TestReporter : public benchmark::internal::ConsoleReporter {
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
benchmark::Initialize(&argc, argv); benchmark::Initialize(&argc, argv);
#ifdef DEBUG #ifndef NDEBUG
assert(Factorial(8) == 40320); assert(Factorial(8) == 40320);
#endif #endif
assert(CalculatePi(1) == 0.0); assert(CalculatePi(1) == 0.0);
......
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