Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(72)

Side by Side Diff: base/time_win.cc

Issue 4092: Change to Hi Res timers on Windows.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 12 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « base/time_unittest_win.cc ('k') | base/trace_event.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5
6 // Windows Timer Primer
7 //
8 // A good article: http://www.ddj.com/windows/184416651
9 // A good mozilla bug: http://bugzilla.mozilla.org/show_bug.cgi?id=363258
10 //
11 // The default windows timer, GetSystemTimeAsFileTime is not very precise.
12 // It is only good to ~15.5ms.
13 //
14 // QueryPerformanceCounter is the logical choice for a high-precision timer.
15 // However, it is known to be buggy on some hardware. Specifically, it can
16 // sometimes "jump". On laptops, QPC can also be very expensive to call.
17 // It's 3-4x slower than timeGetTime() on desktops, but can be 10x slower
18 // on laptops. A unittest exists which will show the relative cost of various
19 // timers on any system.
20 //
21 // The next logical choice is timeGetTime(). timeGetTime has a precision of
22 // 1ms, but only if you call APIs (timeBeginPeriod()) which affect all other
23 // applications on the system. By default, precision is only 15.5ms.
24 // Unfortunately, we don't want to call timeBeginPeriod because we don't
25 // want to affect other applications. Further, on mobile platforms, use of
26 // faster multimedia timers can hurt battery life. See the intel
27 // article about this here:
28 // http://softwarecommunity.intel.com/articles/eng/1086.htm
29 //
30 // To work around all this, we're going to generally use timeGetTime(). We
31 // will only increase the system-wide timer if we're not running on battery
32 // power. Using timeBeginPeriod(1) is a requirement in order to make our
33 // message loop waits have the same resolution that our time measurements
34 // do. Otherwise, WaitForSingleObject(..., 1) will no less than 15ms when
35 // there is nothing else to waken the Wait.
36
5 #include "base/time.h" 37 #include "base/time.h"
6 38
7 #pragma comment(lib, "winmm.lib") 39 #pragma comment(lib, "winmm.lib")
8 #include <windows.h> 40 #include <windows.h>
9 #include <mmsystem.h> 41 #include <mmsystem.h>
10 42
11 #include "base/basictypes.h" 43 #include "base/basictypes.h"
12 #include "base/lock.h" 44 #include "base/lock.h"
13 #include "base/logging.h" 45 #include "base/logging.h"
46 #include "base/cpu.h"
14 #include "base/singleton.h" 47 #include "base/singleton.h"
48 #include "base/system_monitor.h"
15 49
16 namespace { 50 namespace {
17 51
18 // From MSDN, FILETIME "Contains a 64-bit value representing the number of 52 // From MSDN, FILETIME "Contains a 64-bit value representing the number of
19 // 100-nanosecond intervals since January 1, 1601 (UTC)." 53 // 100-nanosecond intervals since January 1, 1601 (UTC)."
20 int64 FileTimeToMicroseconds(const FILETIME& ft) { 54 int64 FileTimeToMicroseconds(const FILETIME& ft) {
21 // Need to bit_cast to fix alignment, then divide by 10 to convert 55 // Need to bit_cast to fix alignment, then divide by 10 to convert
22 // 100-nanoseconds to milliseconds. This only works on little-endian 56 // 100-nanoseconds to milliseconds. This only works on little-endian
23 // machines. 57 // machines.
24 return bit_cast<int64, FILETIME>(ft) / 10; 58 return bit_cast<int64, FILETIME>(ft) / 10;
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after
167 // TimeTicks ------------------------------------------------------------------ 201 // TimeTicks ------------------------------------------------------------------
168 namespace { 202 namespace {
169 203
170 // We define a wrapper to adapt between the __stdcall and __cdecl call of the 204 // We define a wrapper to adapt between the __stdcall and __cdecl call of the
171 // mock function, and to avoid a static constructor. Assigning an import to a 205 // mock function, and to avoid a static constructor. Assigning an import to a
172 // function pointer directly would require setup code to fetch from the IAT. 206 // function pointer directly would require setup code to fetch from the IAT.
173 DWORD timeGetTimeWrapper() { 207 DWORD timeGetTimeWrapper() {
174 return timeGetTime(); 208 return timeGetTime();
175 } 209 }
176 210
211
177 DWORD (*tick_function)(void) = &timeGetTimeWrapper; 212 DWORD (*tick_function)(void) = &timeGetTimeWrapper;
178 213
179 // We use timeGetTime() to implement TimeTicks::Now(). This can be problematic 214 // We use timeGetTime() to implement TimeTicks::Now(). This can be problematic
180 // because it returns the number of milliseconds since Windows has started, 215 // because it returns the number of milliseconds since Windows has started,
181 // which will roll over the 32-bit value every ~49 days. We try to track 216 // which will roll over the 32-bit value every ~49 days. We try to track
182 // rollover ourselves, which works if TimeTicks::Now() is called at least every 217 // rollover ourselves, which works if TimeTicks::Now() is called at least every
183 // 49 days. 218 // 49 days.
184 class NowSingleton { 219 class NowSingleton : public base::SystemMonitor::PowerObserver {
185 public: 220 public:
186 NowSingleton() 221 NowSingleton()
187 : rollover_(TimeDelta::FromMilliseconds(0)), last_seen_(0) { 222 : rollover_(TimeDelta::FromMilliseconds(0)),
188 // Request a resolution of 1ms from timeGetTime(). This can have some 223 last_seen_(0),
189 // consequences on other applications (see MSDN), but we've found that it 224 hi_res_clock_enabled_(false) {
190 // is very common in other applications (Flash, Media Player, etc), so it 225 base::SystemMonitor* system = base::SystemMonitor::Get();
191 // is not really a dangerous thing to do. We need this because the default 226 system->AddObserver(this);
192 // resolution of ~15ms is much too low for accurate timing and timers. 227 UseHiResClock(!system->BatteryPower());
193 ::timeBeginPeriod(1);
194 } 228 }
195 229
196 ~NowSingleton() { 230 ~NowSingleton() {
197 ::timeEndPeriod(1); 231 UseHiResClock(false);
232 base::SystemMonitor::Get()->RemoveObserver(this);
198 } 233 }
199 234
200 TimeDelta Now() { 235 TimeDelta Now() {
201 AutoLock locked(lock_); 236 AutoLock locked(lock_);
202 // We should hold the lock while calling tick_function to make sure that 237 // We should hold the lock while calling tick_function to make sure that
203 // we keep our last_seen_ stay correctly in sync. 238 // we keep our last_seen_ stay correctly in sync.
204 DWORD now = tick_function(); 239 DWORD now = tick_function();
205 if (now < last_seen_) 240 if (now < last_seen_)
206 rollover_ += TimeDelta::FromMilliseconds(0x100000000I64); // ~49.7 days. 241 rollover_ += TimeDelta::FromMilliseconds(0x100000000I64); // ~49.7 days.
207 last_seen_ = now; 242 last_seen_ = now;
208 return TimeDelta::FromMilliseconds(now) + rollover_; 243 return TimeDelta::FromMilliseconds(now) + rollover_;
209 } 244 }
210 245
246 // Interfaces for monitoring Power changes.
247 void OnPowerStateChange(base::SystemMonitor* system) {
248 UseHiResClock(!system->BatteryPower());
249 }
250
251 void OnSuspend(base::SystemMonitor* system) {}
252 void OnResume(base::SystemMonitor* system) {}
253
211 private: 254 private:
255 // Enable or disable the faster multimedia timer.
256 void UseHiResClock(bool enabled) {
257 if (enabled == hi_res_clock_enabled_)
258 return;
259 if (enabled)
260 timeBeginPeriod(1);
261 else
262 timeEndPeriod(1);
263 hi_res_clock_enabled_ = enabled;
264 }
265
212 Lock lock_; // To protected last_seen_ and rollover_. 266 Lock lock_; // To protected last_seen_ and rollover_.
213 TimeDelta rollover_; // Accumulation of time lost due to rollover. 267 TimeDelta rollover_; // Accumulation of time lost due to rollover.
214 DWORD last_seen_; // The last timeGetTime value we saw, to detect rollover. 268 DWORD last_seen_; // The last timeGetTime value we saw, to detect rollover.
269 bool hi_res_clock_enabled_;
215 270
216 DISALLOW_COPY_AND_ASSIGN(NowSingleton); 271 DISALLOW_COPY_AND_ASSIGN(NowSingleton);
217 }; 272 };
218 273
219 // Overview of time counters: 274 // Overview of time counters:
220 // (1) CPU cycle counter. (Retrieved via RDTSC) 275 // (1) CPU cycle counter. (Retrieved via RDTSC)
221 // The CPU counter provides the highest resolution time stamp and is the least 276 // The CPU counter provides the highest resolution time stamp and is the least
222 // expensive to retrieve. However, the CPU counter is unreliable and should not 277 // expensive to retrieve. However, the CPU counter is unreliable and should not
223 // be used in production. Its biggest issue is that it is per processor and it 278 // be used in production. Its biggest issue is that it is per processor and it
224 // is not synchronized between processors. Also, on some computers, the counters 279 // is not synchronized between processors. Also, on some computers, the counters
(...skipping 12 matching lines...) Expand all
237 // RDTSC on each processor are consistent with each other, and apply a handful 292 // RDTSC on each processor are consistent with each other, and apply a handful
238 // of workarounds for known buggy hardware. In other words, QPC is supposed to 293 // of workarounds for known buggy hardware. In other words, QPC is supposed to
239 // give consistent result on a multiprocessor computer, but it is unreliable in 294 // give consistent result on a multiprocessor computer, but it is unreliable in
240 // reality due to bugs in BIOS or HAL on some, especially old computers. 295 // reality due to bugs in BIOS or HAL on some, especially old computers.
241 // With recent updates on HAL and newer BIOS, QPC is getting more reliable but 296 // With recent updates on HAL and newer BIOS, QPC is getting more reliable but
242 // it should be used with caution. 297 // it should be used with caution.
243 // 298 //
244 // (3) System time. The system time provides a low-resolution (typically 10ms 299 // (3) System time. The system time provides a low-resolution (typically 10ms
245 // to 55 milliseconds) time stamp but is comparatively less expensive to 300 // to 55 milliseconds) time stamp but is comparatively less expensive to
246 // retrieve and more reliable. 301 // retrieve and more reliable.
247 class UnreliableHighResNowSingleton { 302 class HighResNowSingleton {
248 public: 303 public:
249 UnreliableHighResNowSingleton() : ticks_per_microsecond_(0) { 304 HighResNowSingleton()
305 : ticks_per_microsecond_(0.0),
306 skew_(0) {
307 InitializeClock();
308
309 // On Athlon X2 CPUs (e.g. model 15) QueryPerformanceCounter is
310 // unreliable. Fallback to low-res clock.
311 base::CPU cpu;
312 if (cpu.vendor_name() == "AuthenticAMD" && cpu.family() == 15)
313 DisableHighResClock();
314 }
315
316 bool IsUsingHighResClock() {
317 return ticks_per_microsecond_ != 0.0;
318 }
319
320 void DisableHighResClock() {
321 ticks_per_microsecond_ = 0.0;
322 }
323
324 TimeDelta Now() {
325 // Our maximum tolerance for QPC drifting.
326 const int kMaxTimeDrift = 50 * Time::kMicrosecondsPerMillisecond;
327
328 if (IsUsingHighResClock()) {
329 int64 now = UnreliableNow();
330
331 // Verify that QPC does not seem to drift.
332 DCHECK(now - ReliableNow() - skew_ < kMaxTimeDrift);
333
334 return TimeDelta::FromMicroseconds(now);
335 }
336
337 // Just fallback to the slower clock.
338 return Singleton<NowSingleton>::get()->Now();
339 }
340
341 private:
342 // Synchronize the QPC clock with GetSystemTimeAsFileTime.
343 void InitializeClock() {
250 LARGE_INTEGER ticks_per_sec = {0}; 344 LARGE_INTEGER ticks_per_sec = {0};
251 if (!QueryPerformanceFrequency(&ticks_per_sec)) 345 if (!QueryPerformanceFrequency(&ticks_per_sec))
252 return; // Broken, we don't guarantee this function works. 346 return; // Broken, we don't guarantee this function works.
253 ticks_per_microsecond_ = 347 ticks_per_microsecond_ = static_cast<float>(ticks_per_sec.QuadPart) /
254 ticks_per_sec.QuadPart / Time::kMicrosecondsPerSecond; 348 static_cast<float>(Time::kMicrosecondsPerSecond);
349
350 skew_ = UnreliableNow() - ReliableNow();
255 } 351 }
256 352
257 bool IsBroken() { 353 // Get the number of microseconds since boot in a reliable fashion
258 return ticks_per_microsecond_ == 0; 354 int64 UnreliableNow() {
355 LARGE_INTEGER now;
356 QueryPerformanceCounter(&now);
357 return static_cast<int64>(now.QuadPart / ticks_per_microsecond_);
259 } 358 }
260 359
261 TimeDelta Now() { 360 // Get the number of microseconds since boot in a reliable fashion
262 LARGE_INTEGER now; 361 int64 ReliableNow() {
263 QueryPerformanceCounter(&now); 362 return Singleton<NowSingleton>::get()->Now().InMicroseconds();
264 return TimeDelta::FromMicroseconds(now.QuadPart / ticks_per_microsecond_);
265 } 363 }
266 364
267 private:
268 // Cached clock frequency -> microseconds. This assumes that the clock 365 // Cached clock frequency -> microseconds. This assumes that the clock
269 // frequency is faster than one microsecond (which is 1MHz, should be OK). 366 // frequency is faster than one microsecond (which is 1MHz, should be OK).
270 int64 ticks_per_microsecond_; // 0 indicates QPF failed and we're broken. 367 float ticks_per_microsecond_; // 0 indicates QPF failed and we're broken.
368 int64 skew_; // Skew between lo-res and hi-res clocks (for debugging).
271 369
272 DISALLOW_COPY_AND_ASSIGN(UnreliableHighResNowSingleton); 370 DISALLOW_COPY_AND_ASSIGN(HighResNowSingleton);
273 }; 371 };
274 372
275 } // namespace 373 } // namespace
276 374
277 // static 375 // static
278 TimeTicks::TickFunctionType TimeTicks::SetMockTickFunction( 376 TimeTicks::TickFunctionType TimeTicks::SetMockTickFunction(
279 TickFunctionType ticker) { 377 TickFunctionType ticker) {
280 TickFunctionType old = tick_function; 378 TickFunctionType old = tick_function;
281 tick_function = ticker; 379 tick_function = ticker;
282 return old; 380 return old;
283 } 381 }
284 382
285 // static 383 // static
286 TimeTicks TimeTicks::Now() { 384 TimeTicks TimeTicks::Now() {
287 return TimeTicks() + Singleton<NowSingleton>::get()->Now(); 385 return TimeTicks() + Singleton<NowSingleton>::get()->Now();
288 } 386 }
289 387
290 // static 388 // static
291 TimeTicks TimeTicks::UnreliableHighResNow() { 389 TimeTicks TimeTicks::HighResNow() {
292 UnreliableHighResNowSingleton* now = 390 return TimeTicks() + Singleton<HighResNowSingleton>::get()->Now();
293 Singleton<UnreliableHighResNowSingleton>::get();
294
295 if (now->IsBroken()) {
296 NOTREACHED() << "QueryPerformanceCounter is broken.";
297 return TimeTicks(0);
298 }
299
300 return TimeTicks() + now->Now();
301 } 391 }
OLDNEW
« no previous file with comments | « base/time_unittest_win.cc ('k') | base/trace_event.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698