• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 Google Inc. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "benchmark/macros.h"
16 #include "internal_macros.h"
17 #include "walltime.h"
18 
19 #if defined(BENCHMARK_OS_WINDOWS)
20 #include <time.h>
21 #include <winsock.h> // for timeval
22 #else
23 #include <sys/time.h>
24 #endif
25 
26 #include <cstdio>
27 #include <cstdint>
28 #include <cstring>
29 #include <ctime>
30 
31 #include <atomic>
32 #include <chrono>
33 #include <limits>
34 
35 #include "arraysize.h"
36 #include "check.h"
37 #include "cycleclock.h"
38 #include "log.h"
39 #include "sysinfo.h"
40 
41 namespace benchmark {
42 namespace walltime {
43 
44 namespace {
45 
46 #if defined(HAVE_STEADY_CLOCK)
47 template <bool HighResIsSteady = std::chrono::high_resolution_clock::is_steady>
48 struct ChooseSteadyClock {
49     typedef std::chrono::high_resolution_clock type;
50 };
51 
52 template <>
53 struct ChooseSteadyClock<false> {
54     typedef std::chrono::steady_clock type;
55 };
56 #endif
57 
58 struct ChooseClockType {
59 #if defined(HAVE_STEADY_CLOCK)
60   typedef ChooseSteadyClock<>::type type;
61 #else
62   typedef std::chrono::high_resolution_clock type;
63 #endif
64 };
65 
66 class WallTimeImp
67 {
68 public:
69   WallTime Now();
70 
GetWallTimeImp()71   static WallTimeImp& GetWallTimeImp() {
72     static WallTimeImp* imp = new WallTimeImp();
73     return *imp;
74   }
75 
76 private:
77   WallTimeImp();
78   // Helper routines to load/store a float from an AtomicWord. Required because
79   // g++ < 4.7 doesn't support std::atomic<float> correctly. I cannot wait to
80   // get rid of this horror show.
SetDrift(float f)81   void SetDrift(float f) {
82     int32_t w;
83     memcpy(&w, &f, sizeof(f));
84     std::atomic_store(&drift_adjust_, w);
85   }
86 
GetDrift() const87   float GetDrift() const {
88     float f;
89     int32_t w = std::atomic_load(&drift_adjust_);
90     memcpy(&f, &w, sizeof(f));
91     return f;
92   }
93 
Slow() const94   WallTime Slow() const {
95     struct timeval tv;
96 #if defined(BENCHMARK_OS_WINDOWS)
97     FILETIME    file_time;
98     SYSTEMTIME  system_time;
99     ULARGE_INTEGER ularge;
100     const unsigned __int64 epoch = 116444736000000000LL;
101 
102     GetSystemTime(&system_time);
103     SystemTimeToFileTime(&system_time, &file_time);
104     ularge.LowPart = file_time.dwLowDateTime;
105     ularge.HighPart = file_time.dwHighDateTime;
106 
107     tv.tv_sec = (long)((ularge.QuadPart - epoch) / (10L * 1000 * 1000));
108     tv.tv_usec = (long)(system_time.wMilliseconds * 1000);
109 #else
110     gettimeofday(&tv, nullptr);
111 #endif
112     return tv.tv_sec + tv.tv_usec * 1e-6;
113   }
114 
115 private:
116   static_assert(sizeof(float) <= sizeof(int32_t),
117                "type sizes don't allow the drift_adjust hack");
118 
119   WallTime base_walltime_;
120   int64_t base_cycletime_;
121   int64_t cycles_per_second_;
122   double seconds_per_cycle_;
123   uint32_t last_adjust_time_;
124   std::atomic<int32_t> drift_adjust_;
125   int64_t max_interval_cycles_;
126 
127   BENCHMARK_DISALLOW_COPY_AND_ASSIGN(WallTimeImp);
128 };
129 
130 
Now()131 WallTime WallTimeImp::Now() {
132   WallTime now = 0.0;
133   WallTime result = 0.0;
134   int64_t ct = 0;
135   uint32_t top_bits = 0;
136   do {
137     ct = cycleclock::Now();
138     int64_t cycle_delta = ct - base_cycletime_;
139     result = base_walltime_ + cycle_delta * seconds_per_cycle_;
140 
141     top_bits = static_cast<uint32_t>(uint64_t(ct) >> 32);
142     // Recompute drift no more often than every 2^32 cycles.
143     // I.e., @2GHz, ~ every two seconds
144     if (top_bits == last_adjust_time_) {  // don't need to recompute drift
145       return result + GetDrift();
146     }
147 
148     now = Slow();
149   } while (cycleclock::Now() - ct > max_interval_cycles_);
150   // We are now sure that "now" and "result" were produced within
151   // kMaxErrorInterval of one another.
152 
153   SetDrift(static_cast<float>(now - result));
154   last_adjust_time_ = top_bits;
155   return now;
156 }
157 
158 
WallTimeImp()159 WallTimeImp::WallTimeImp()
160     : base_walltime_(0.0), base_cycletime_(0),
161       cycles_per_second_(0), seconds_per_cycle_(0.0),
162       last_adjust_time_(0), drift_adjust_(0),
163       max_interval_cycles_(0) {
164   const double kMaxErrorInterval = 100e-6;
165   cycles_per_second_ = static_cast<int64_t>(CyclesPerSecond());
166   CHECK(cycles_per_second_ != 0);
167   seconds_per_cycle_ = 1.0 / cycles_per_second_;
168   max_interval_cycles_ =
169       static_cast<int64_t>(cycles_per_second_ * kMaxErrorInterval);
170   do {
171     base_cycletime_ = cycleclock::Now();
172     base_walltime_ = Slow();
173   } while (cycleclock::Now() - base_cycletime_ > max_interval_cycles_);
174   // We are now sure that "base_walltime" and "base_cycletime" were produced
175   // within kMaxErrorInterval of one another.
176 
177   SetDrift(0.0);
178   last_adjust_time_ = static_cast<uint32_t>(uint64_t(base_cycletime_) >> 32);
179 }
180 
CPUWalltimeNow()181 WallTime CPUWalltimeNow() {
182   static WallTimeImp& imp = WallTimeImp::GetWallTimeImp();
183   return imp.Now();
184 }
185 
ChronoWalltimeNow()186 WallTime ChronoWalltimeNow() {
187   typedef ChooseClockType::type Clock;
188   typedef std::chrono::duration<WallTime, std::chrono::seconds::period>
189           FPSeconds;
190   static_assert(std::chrono::treat_as_floating_point<WallTime>::value,
191                 "This type must be treated as a floating point type.");
192   auto now = Clock::now().time_since_epoch();
193   return std::chrono::duration_cast<FPSeconds>(now).count();
194 }
195 
UseCpuCycleClock()196 bool UseCpuCycleClock() {
197     bool useWallTime = !CpuScalingEnabled();
198     if (useWallTime) {
199         VLOG(1) << "Using the CPU cycle clock to provide walltime::Now().\n";
200     } else {
201         VLOG(1) << "Using std::chrono to provide walltime::Now().\n";
202     }
203     return useWallTime;
204 }
205 
206 
207 } // end anonymous namespace
208 
209 // WallTimeImp doesn't work when CPU Scaling is enabled. If CPU Scaling is
210 // enabled at the start of the program then std::chrono::system_clock is used
211 // instead.
Now()212 WallTime Now()
213 {
214   static bool useCPUClock = UseCpuCycleClock();
215   if (useCPUClock) {
216     return CPUWalltimeNow();
217   } else {
218     return ChronoWalltimeNow();
219   }
220 }
221 
222 }  // end namespace walltime
223 
224 
225 namespace {
226 
DateTimeString(bool local)227 std::string DateTimeString(bool local) {
228   typedef std::chrono::system_clock Clock;
229   std::time_t now = Clock::to_time_t(Clock::now());
230   char storage[128];
231   std::size_t written;
232 
233   if (local) {
234 #if defined(BENCHMARK_OS_WINDOWS)
235     written = std::strftime(storage, sizeof(storage), "%x %X", ::localtime(&now));
236 #else
237     std::tm timeinfo;
238     std::memset(&timeinfo, 0, sizeof(std::tm));
239     ::localtime_r(&now, &timeinfo);
240     written = std::strftime(storage, sizeof(storage), "%F %T", &timeinfo);
241 #endif
242   } else {
243 #if defined(BENCHMARK_OS_WINDOWS)
244     written = std::strftime(storage, sizeof(storage), "%x %X", ::gmtime(&now));
245 #else
246     std::tm timeinfo;
247     std::memset(&timeinfo, 0, sizeof(std::tm));
248     ::gmtime_r(&now, &timeinfo);
249     written = std::strftime(storage, sizeof(storage), "%F %T", &timeinfo);
250 #endif
251   }
252   CHECK(written < arraysize(storage));
253   ((void)written); // prevent unused variable in optimized mode.
254   return std::string(storage);
255 }
256 
257 } // end namespace
258 
LocalDateTimeString()259 std::string LocalDateTimeString() {
260   return DateTimeString(true);
261 }
262 
263 }  // end namespace benchmark
264