diff options
Diffstat (limited to '3rdparty/benchmark/src/walltime.cc')
-rw-r--r-- | 3rdparty/benchmark/src/walltime.cc | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/3rdparty/benchmark/src/walltime.cc b/3rdparty/benchmark/src/walltime.cc new file mode 100644 index 00000000000..4bdbaa59859 --- /dev/null +++ b/3rdparty/benchmark/src/walltime.cc @@ -0,0 +1,263 @@ +// 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/macros.h" +#include "internal_macros.h" +#include "walltime.h" + +#if defined(BENCHMARK_OS_WINDOWS) +#include <time.h> +#include <winsock.h> // for timeval +#else +#include <sys/time.h> +#endif + +#include <cstdio> +#include <cstdint> +#include <cstring> +#include <ctime> + +#include <atomic> +#include <chrono> +#include <limits> + +#include "arraysize.h" +#include "check.h" +#include "cycleclock.h" +#include "log.h" +#include "sysinfo.h" + +namespace benchmark { +namespace walltime { + +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 + +struct ChooseClockType { +#if defined(HAVE_STEADY_CLOCK) + typedef ChooseSteadyClock<>::type type; +#else + typedef std::chrono::high_resolution_clock type; +#endif +}; + +class WallTimeImp +{ +public: + WallTime Now(); + + static WallTimeImp& GetWallTimeImp() { + static WallTimeImp* imp = new WallTimeImp(); + 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() const { + struct timeval tv; +#if defined(BENCHMARK_OS_WINDOWS) + FILETIME file_time; + SYSTEMTIME system_time; + ULARGE_INTEGER ularge; + const unsigned __int64 epoch = 116444736000000000LL; + + GetSystemTime(&system_time); + SystemTimeToFileTime(&system_time, &file_time); + ularge.LowPart = file_time.dwLowDateTime; + ularge.HighPart = file_time.dwHighDateTime; + + tv.tv_sec = (long)((ularge.QuadPart - epoch) / (10L * 1000 * 1000)); + tv.tv_usec = (long)(system_time.wMilliseconds * 1000); +#else + gettimeofday(&tv, nullptr); +#endif + 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"); + + WallTime base_walltime_; + 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 result = 0.0; + int64_t ct = 0; + uint32_t top_bits = 0; + do { + ct = cycleclock::Now(); + int64_t cycle_delta = ct - base_cycletime_; + result = base_walltime_ + cycle_delta * seconds_per_cycle_; + + top_bits = static_cast<uint32_t>(uint64_t(ct) >> 32); + // Recompute drift no more often than every 2^32 cycles. + // I.e., @2GHz, ~ every two seconds + if (top_bits == last_adjust_time_) { // don't need to recompute drift + return result + GetDrift(); + } + + now = Slow(); + } while (cycleclock::Now() - ct > max_interval_cycles_); + // We are now sure that "now" and "result" were produced within + // kMaxErrorInterval of one another. + + SetDrift(static_cast<float>(now - result)); + last_adjust_time_ = top_bits; + 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) { + const double kMaxErrorInterval = 100e-6; + 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); +} + +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() +{ + static bool useCPUClock = UseCpuCycleClock(); + if (useCPUClock) { + return CPUWalltimeNow(); + } else { + return ChronoWalltimeNow(); + } +} + +} // end namespace walltime + + +namespace { + +std::string DateTimeString(bool local) { + typedef std::chrono::system_clock Clock; + std::time_t now = Clock::to_time_t(Clock::now()); + char storage[128]; + std::size_t written; + + if (local) { +#if defined(BENCHMARK_OS_WINDOWS) + written = 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 + } else { +#if defined(BENCHMARK_OS_WINDOWS) + 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 + } + CHECK(written < arraysize(storage)); + ((void)written); // prevent unused variable in optimized mode. + return std::string(storage); +} + +} // end namespace + +std::string LocalDateTimeString() { + return DateTimeString(true); +} + +} // end namespace benchmark |