Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

QueryPerformanceCounter or GetSystemTimePreciseAsFileTime When using SNTP?

The following paragraph has confused me:

From the article: Acquiring high-resolution time stamps

When you need time stamps with a resolution of 1 microsecond or better and you don't need the time stamps to be synchronized to an external time reference, choose QueryPerformanceCounter, KeQueryPerformanceCounter, or KeQueryInterruptTimePrecise. When you need UTC-synchronized time stamps with a resolution of 1 microsecond or better, choose GetSystemTimePreciseAsFileTime or KeQuerySystemTimePrecise.

What does the phrase " to be synchronized to an external time reference" exactly means here? What I learn is this:

  1. If your PC is not connected to a GPS ( through serial port or SNTP ) use QueryPerformanceCounter.
  2. If the PC is connected, then use GetSystemTimePreciseAsFileTime.

Is this assumption correct?

like image 740
Behzad Avatar asked Jan 01 '16 15:01

Behzad


1 Answers

The "current" time returned by various Windows functions doesn't always count up at a rate of 60 seconds every minute.

Sometimes Windows intentionally has the clock going a little bit slower, or a little bit faster:

  • If your PC clock is currently behind the current correct time: run slightly faster to catch up
  • If your PC clock is currently ahead of the current corret time: run slightly slower to let the correct time catch up to this

It does this rather than having your clock suddenly JUMP, and now your logfiles have temporal anomalies.

You can use GetSystemTimeAdjustment to see if Windows is currently ticking your clock faster or slower, or at the nominal rate. For example, on my computer right now:

  • System Time Adjustment: Disabled
  • Adding nominal 15,6250 ms each update

But if your clock is too far away from the correct time, Windows will just BONK your time to the correct time.

Time can run fast, slow, jump forward, jump backward. QueryPerformanceCounter won't

So the times returned by:

Function Return type Resolution Timezone
GetLocalTime SYSTEM_TIME (1ms) ~10-15 ms Local
GetSystemTime SYSTEM_TIME (1ms) ~10-15 ms UTC
GetSystemTimeAsFileTime FILE_TIME (0.1us) ~10-15 ms UTC
GetSystemTimePreciseAsFileTime FILE_TIME (0.1us) 0.1 us UTC

Can all run fast, slow, or jump around, as your clock jumps around. While QueryPerformanceCounter will never speed up or slow down. It always counts up at exactly the rate of 60s/min.

Function Return type Resolution Timezone
GetLocalTime SYSTEM_TIME (1ms) ~10-15 ms Local
GetSystemTime SYSTEM_TIME (1ms) ~10-15 ms UTC
GetSystemTimeAsFileTime FILE_TIME (0.1us) ~10-15 ms UTC
GetSystemTimePreciseAsFileTime FILE_TIME (0.1us) 0.1 us UTC
GetTickCount Int32 (1ms) ~10-15 ms n/a
GetTickCount64 Int64 (1ms) ~10-15 ms n/a
QueryPerformanceCounter Int64 (ticks) 0.1 us n/a

So what do you care about

As long as your computer's clock is not currently undergoing SystemTimeAdjustment, the elapsed intervals measured by:

  • GetSytemTimeAsPreciseFileTime
  • QueryPerformanceCounter

will both:

  • be in sync
  • be within a few tenths of a microsecond of each other
  • have an ultimate resolution of 100ns (0.1 us)

For accurate time measurements these are the two functions you should use. The question, which one do you care for the task:

  • GetSystemTimeAsPreciseFileTime: high resolution measurement of "now"

    • good for log files
    • timestamping of events
    • synchronized to UTC for cross-machine agreement of "now"
  • QueryPerformanceCounter: high resolution measurement of "elapsed time"

    • good for bench-marking
    • measuring duration of processes

Bonus Chatter

It should be noted that the SYSTEM_TIME structure returned by GetLocalTime and GetSystemTime are inherently limited to millisecond precision. There's no way for GetSystemTime to give you sub-millisecond resolution, simply because the return type has no place to give you microseconds:

struct SYSTEMTIME {
     WORD wYear;
     WORD wMonth;
     WORD wDay;
     WORD wHour;
     WORD wMinute;
     WORD wSecond;
     WORD wMilliseconds; //<---- that's the limit
}

You should only use SYSTEM_TIME when you want Windows to break a time for you down into years, months, days, hours, minutes, seconds. The FILE_TIME structure on the other hand as an ultimate precision of 100ns (0.1 us).

Precise time functions

In the language i use, we use a DateTime that is a Double precision floating point, where:

  • integer portion represents the number of days since 12/30/1899
  • fractional portion represents a fraction of the 24-hour day

This is the datetime scheme used by COM, OLE, Delphi, VB, Excel, Lotus 123; and similar to the datetime scheme used by SQL Server (although they use 1/1/1900 rather than 12/30/1899)

DateTime UtcNowPrecise()
{
   const UInt64 OA_ZERO_TICKS = 94353120000000000; //12/30/1899 12:00am in ticks
   const UInt64 TICKS_PER_DAY = 864000000000;      //ticks per day

   FILE_TIME ft;
   GetSystemTimePreciseAsFileTime(out ft);

   ULARGE_INTEGER dt; //needed to avoid alignment faults
   dt.LowPart  = ft.dwLowDateTime;
   dt.HighPart = ft.dwHighDateTime;

   return (dt.QuadPart - OA_ZERO_TICKS) / TICKS_PER_DAY;
}

DateTime NowPrecise()
{
   const UInt64 OA_ZERO_TICKS = 94353120000000000; //12/30/1899 12:00am in ticks
   const UInt64 TICKS_PER_DAY = 864000000000;      //ticks per day

   FILE_TIME ft;
   GetSystemTimePreciseAsFileTime(out ft);

   //Convert from UTC to local
   FILE_TIME ftLocal;
   if (!FileTimeToLocalFileTime(ft, ref ftLocal))
      RaiseLastNativeError();

   ULARGE_INTEGER dt; //needed to avoid alignment faults
   dt.LowPart  = ftLocal.dwLowDateTime;
   dt.HighPart = ftLocal.dwHighDateTime;

   return (dt.QuadPart - OA_ZERO_TICKS) / TICKS_PER_DAY;
}
like image 70
Ian Boyd Avatar answered Oct 15 '22 19:10

Ian Boyd