| // 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 "walltime.h" |
| |
| #include <sys/time.h> |
| |
| #include <cstdio> |
| #include <cstdint> |
| #include <cstring> |
| #include <ctime> |
| |
| #include <atomic> |
| #include <chrono> |
| #include <limits> |
| #include <type_traits> |
| |
| #include "arraysize.h" |
| #include "check.h" |
| #include "cycleclock.h" |
| #include "sysinfo.h" |
| |
| namespace benchmark { |
| namespace walltime { |
| |
| namespace { |
| |
| 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() const { |
| struct timeval tv; |
| gettimeofday(&tv, nullptr); |
| 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 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(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) { |
| 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 anonymous namespace |
| |
| |
| WallTime CPUWalltimeNow() { |
| static WallTimeImp& imp = WallTimeImp::GetWallTimeImp(); |
| return imp.Now(); |
| } |
| |
| WallTime ChronoWalltimeNow() { |
| typedef std::chrono::system_clock 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(); |
| } |
| |
| // 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 useWallTime = !CpuScalingEnabled(); |
| if (useWallTime) { |
| return CPUWalltimeNow(); |
| } else { |
| return ChronoWalltimeNow(); |
| } |
| } |
| |
| } // end namespace walltime |
| |
| |
| 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::tm timeinfo; |
| if (local) { |
| std::tm* ret = std::localtime(&now); |
| CHECK(ret != nullptr); |
| timeinfo = *ret; |
| } else { |
| std::tm* ret = std::gmtime(&now); |
| CHECK(ret != nullptr); |
| timeinfo = *ret; |
| } |
| 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); |
| } |
| |
| } // end namespace benchmark |