Jump to content
Rollo62

The status of "System.SysUtils.Now" timer resolution & accuracy

Recommended Posts

Hi there,

I'm wondering if there have been any changes to the Now() function recently, related to the timer resolution.
The Now() returnd TDateTime with Millisecond resolution, while officially they state the resolution is only 1 Sec.
https://docwiki.embarcadero.com/Libraries/Athens/en/System.SysUtils.Now

Note: Although TDateTime values can represent milliseconds, Now is accurate only to the nearest second.

This topic is quite old, so already here discussed on SO
https://stackoverflow.com/questions/14573617/how-accurate-is-delphis-now-timestamp

 

With GetTimerTick() I can reach 7-16ms timer resolution, basically.

!! And before somebody recommends high performance: I'm perfectly fine with the 15ms resolution and I want to use it.
With Now() I can see similar results on all platforms, according to my tests, which is much better than the 1 Sec. stated in the docs, pretty much like the 7-16ms resolution.

 

So what does the 1 Sec. really mean then, is this the worst-case scenario that might happen, while its perfectly 15ms all day long?

 

Perhaps there have been changed something recently, in Delphi or Windows or other Platforms?

 

Windows:
System.SysUtils.Now(): is based on  GetLocalTime(SystemTime);

https://learn.microsoft.com/de-de/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlocaltime

https://learn.microsoft.com/de-de/windows/win32/api/minwinbase/ns-minwinbase-systemtime

- I cannot find any restrictions like 1 Sec. there, not even a 15ms note.

 TThread.GetTickCount64: is based in GetTickCount64;

https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-gettickcount64

- Which also had no restrictions, aside the known 15ms resolution.

 


Macos/iOS: mach_absolute_time
Android/Linux: clock_gettime(CLOCK_MONOTONIC, @res);

Other Platforms were way beyond these accuracy
- All should reach 1ms without problem.

From the behaviour I can see in my tests, I would assume that Now() reaches the same 15ms resolution meanwhile on my current Win11 PC.

Is this a safe assumpttion, or are there any other known restriction, which officially state the 1 Sec. resolution?

 

This is why I think the following
- Now() and GetTickCount64 both reach about 15ms resolution
- Perhaps Now() could show resolution 1 Sec. in rare cases, e.g. under heavy load.

- If that is true, I would also expect that also GetTickCount64() might break down to 1 Sec. resolution, under heavy load.


Maybe somebody can clarify the situation, under current Windows versions, are there any official statements, aside from Embarcadero's docs?
If there were such insights for the other platforms, I'm happy to see them too, but only Windows seems to be worst here.

 

Share this post


Link to post

I work only on Windows but to get precise timings (below 1ms of accuracy) I use QueryPerformanceFrequency() and QueryPerformanceCounter().
In case precise timings are not available I switch to timeGetTime()

A messy unit is in attachment.

PS:
You can improve Windwos timeGetTime, gettickcount and sleep (DELPHI IDE does that) with:

program XXX;

uses
  System.SysUtils,
  Winapi.MMSystem,
  ...;

{$R *.res}

var
  TimeCaps: TTimeCaps;
  NeedToChangeTimerPrecsion: Boolean;

begin
  // starts high precision timer
  if timeGetDevCaps(@TimeCaps, SizeOf(TTimeCaps)) = TIMERR_NOERROR then
    NeedToChangeTimerPrecsion := timeBeginPeriod(TimeCaps.wPeriodMin) = TIMERR_NOERROR
  else
    NeedToChangeTimerPrecsion := False;

  // initializes and runs application
  Application.Initialize;
  ...;
  Application.Run;

  // stops high precision timer
  if NeedToChangeTimerPrecsion then
    timeEndPeriod(TimeCaps.wPeriodMin);
end.


 

osTimeUtils.pas

Edited by shineworld

Share this post


Link to post

Like said, I know about performance counters, but I want to use Now() and GetTimerTick64() for other tasks, accepting the 15ms tolerances.
The question is about, what the Now() 1 Sec. tolerance really means nowadays, is it gone or is it a valid case?

Edited by Rollo62

Share this post


Link to post

Mmmm,
I've tried a simple console program in Athens 12:
 

program Project4;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  System.DateUtils,
  Winapi.Windows;

var
  I: Integer;
  T1: Int64;
  T2: Int64;
  TD: Double;
  D1: TDateTime;
  D2: TDateTime;
  DiffMs: Int64;
  Frequency: Int64;

function Delay: Integer;
var
  I: Integer;
begin
  Result := 0;
  for I := 0 to 1000000 do
    Inc(Result);
end;

begin
  FormatSettings.DecimalSeparator := '.';
  FormatSettings.ThousandSeparator := ',';
  try
    QueryPerformanceFrequency(Frequency);

    D1 := Now();
    QueryPerformanceCounter(T1);
    Delay;
    D2 := Now();
    QueryPerformanceCounter(T2);

    TD := (T2 - T1) * 1000 / Frequency;
    DiffMs := MilliSecondsBetween(D2, D1);
    Writeln(Format('Now() delta            = %d ms', [DiffMs]));
    Writeln(Format('QueryPerformance delta = %f ms', [TD]));


  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

Result:

Now() delta            = 2 ms
QueryPerformance delta = 2.15 ms

The Delay is empirical, necessary just to have a little delay.
Now() seems to capture 2ms of delta.


Increasing delay of 10:

Now() delta            = 17 ms
QueryPerformance delta = 16.42 ms

I don't know why they report 1 sec of precision in the documentation, but if it can be so, is terribile, because my OPC UA server timings are based on TDateTime and 1ms of resolutions is already 10 time bigger than OPC-UA tick of 100ns.

Edited by shineworld

Share this post


Link to post

Thanks for the proposals, but again, I'm not looking for QueryPerformanceCounter, but about the Now();  behaviour.

Moreover I'm looking for a cross-platform sulution, not Windows alone.

I dont need high performance for this task, the 15ms would be fine.
It could be even OK, if 1 Sec., if this happens only very very rare ( but I would like to avoid that ).

 

I hope that someone knows exactly where the 1 Sec. comes from and what conditions will make it to show up.

Edited by Rollo62

Share this post


Link to post

You can check System.Diagnostics.TStopWatch it is cross platform and high-precision

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×