开发者

What's the best replacement for timeGetTime to avoid wrap-around?

开发者 https://www.devze.com 2022-12-30 17:17 出处:网络
timeGetTime seems to b开发者_运维知识库e quite good to query for system time. However, its return value is 32-bit only, so it wraps around every 49 days approx.

timeGetTime seems to b开发者_运维知识库e quite good to query for system time. However, its return value is 32-bit only, so it wraps around every 49 days approx.

It's not too hard to detect the rollover in calling code, but it adds some complexity, and (worse) requires keeping a state.

Is there some replacement for timeGetTime that would not have this wrap-around problem (probably by returning a 64-bit value), and have roughly the same precision and cost?


Unless you need to time an event that is over 49 days, you can SAFELY ignore the wrap-around. Just always subtract the previous timeGetTime() from the current timeGetTime() and you will always obtain a delta measured time that is accurate, even across wrap-around -- provided that you are timing events whose total duration is under 49 days. This all works due to how unsigned modular math works inside the computer.

// this code ALWAYS works, even with wrap-around!
DWORD dwStart = timeGetTime();
// provided the event timed here has a duration of less than 49 days
DWORD dwDuration = timeGetTime()-dwStart;

TIP: look into TimeBeginPeriod(1L) to increase the accuracy of timeGetTime().

BUT... if you want a 64-bit version of timeGetTime, here it is:

__int64 timeGetTime64() {
    static __int64 time64=0;
    // warning: if multiple threads call this function, protect with a critical section!
    return (time64 += (timeGetTime()-(DWORD)time64));
    }

Please note that if this function is not called at least once every 49 days, that this function will fail to properly detect a wrap-around.


What platform?

You could use GetTickCount64() if you're running on Vista or later, or synthesise your own GetTickCount64() from GetTickCount() and a timer...

I deal with the rollover issue in GetTickCount() and synthesising a GetTickCount64() on platforms that don't support it here on my blog about testing non-trivial code: http://www.lenholgate.com/blog/2008/04/practical-testing-17---a-whole-new-approach.html


Nope, tracking roll-over requires state. It can be as simple as just incrementing your own 64-bit counter on each callback.

It is pretty unusual to want to track time periods to a resolution as low as 1 millisecond for up to 49 days. You'd have to worry that the accuracy is still there after such a long period. The next step is to use the clock, GetTickCount(64), GetSystemTimeAsFileTime have a resolution of 15.625 milliseconds and are kept accurate with a time server.


Have a look at GetSystemTimeAsFileTime(). It fills a FILETIME struct that contains a "64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC)"


How are you trying to use it? I frequently use the Win32 equivalent when checking for durations that I know will be under 49 days. For example the following code will always work.

DWORD start = timeGetTime();
DoSomthingThatTakesLessThen49Days();
DWORD duration = timeGetTime() - start;

Even if timeGetTime rolled over while calling DoSomthingThatTakesLessThen49Days duration will still be correct.

Note the following code could fail on rollover.

DWORD start = timeGetTime();
DoSomthingThatTakesLessThen49Days();
if (now + 5000 < timeGetTime())
{
}

but can easy be re-written to work as follows

DWORD start = timeGetTime();
DoSomthingThatTakesLessThen49Days();
if (timeGetTime() - start < 5000)
{
}


Assuming you can guarantee that this function will called at least once every 49 days, something like this will work:

// Returns current time in milliseconds
uint64_t timeGetTime64()
{
   static uint32_t _prevVal    = 0;
   static uint64_t _wrapOffset = 0;

   uint32_t newVal = (uint32_t) timeGetTime();
   if (newVal < _prevVal) _wrapOffset += (((uint64_t)1)<<32);
   _prevVal = newVal;
   return _wrapOffset+newVal;
}

Note that due to the use of static variables, this function isn't multithread-safe, so if you plan on calling it from multiple threads you should serialize it via a critical section or mutex or similar.


I'm not sure if this fully meets your needs, but

std::chrono::system_clock

might be along the lines of what you're looking for.

http://en.cppreference.com/w/cpp/chrono/system_clock


You could use RDTSC intrinsic. To get time in milliseconds you could get transform coefficient:

double get_rdtsc_coeff() {
  static double coeff = 0.0;
  if ( coeff < 1.0 ) { // count it only once
    unsigned __int64 t00 = __rdtsc();
    Sleep(1000);
    unsigned __int64 t01 = __rdtsc();
    coeff = (t01-t00)/1000.0;
  }

  return coeff; // transformation coefficient
}

Now you could get count of milliseconds from the last reset:

__int64 get_ms_from_start() {
  return static_cast<__int64>(__rdtsc()/get_rdtsc_coeff());
}

If your system uses SpeedStep or similar technologies you could use QueryPerformanceCounter/QueryPerformanceFrequency functions. Windows gives guarantees then the frequency cannot change while the system is running.

0

精彩评论

暂无评论...
验证码 换一张
取 消