| // 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 |