Rollo62 558 Posted 23 hours ago 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
shineworld 80 Posted 22 hours ago (edited) 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 22 hours ago by shineworld Share this post Link to post
Rollo62 558 Posted 22 hours ago (edited) 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 22 hours ago by Rollo62 Share this post Link to post
shineworld 80 Posted 21 hours ago (edited) 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 21 hours ago by shineworld 1 Share this post Link to post
Rollo62 558 Posted 20 hours ago (edited) 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 20 hours ago by Rollo62 Share this post Link to post
EugeneK 22 Posted 19 hours ago You can check System.Diagnostics.TStopWatch it is cross platform and high-precision Share this post Link to post
Anders Melander 1929 Posted 16 hours ago OP: Unambiguous question about the behavior of X A: Here's some code that does something similar but doesn't answer your question. OP: Okay but what about my question about the behavior of X A: Here's some data that also doesn't answer the question. OP: Thanks but... B: Have you tried <something else> instead? OP: *flips desk* 🙂 Apart from that, https://www.scientificamerican.com/article/time-s-passage-is-probably-an-illusion/ But seriously, ignore the help. It's obviously not up to date. The behavior of Now is never going to change to match the help because that would break a lot of code and nobody needs it to have 1 second resolution. Even in Delphi 1 (which implemented Now as Date (and Time via the DOS INT 21h, function 2Ch (which had 10 mS resolution))) the resolution was better than 1 second. I too would go for TStopWatch - even if you don't need the precision. Share this post Link to post
DelphiUdIT 213 Posted 15 hours ago 4 hours ago, Rollo62 said: hope that someone knows exactly where the 1 Sec. comes from and what conditions will make it to show up. Look this, I think that the information on the NOW() function in Embarcadero page is old and no more accurate.: https://devblogs.microsoft.com/oldnewthing/20170921-00/?p=97057 Share this post Link to post
Rollo62 558 Posted 5 hours ago Thanks, that very helpful. It is what I expected, of course the all should be derived from a deeper hardware clock, with the same resolution. But this is just my assumption, Raymonds article leads to this conclusion too: Quote Another group uses the system timer, which usually means 55ms or 10ms, although the timeBeginPeriod function can be used to run the timer at a higher rate. GetTickCount, GetTickCount64 GetMessageTime GetSystemTime, GetLocalTime, GetSystemTimeAsFileTime QueryInterruptTime, QueryUnbiasedInterruptTime - GetTickCount64 is based it's same name Windows kernel function, and - Now() is based on Windows GetLocalTime() function. which all belongs to that same group and leads to the same timer resolution of 10 to 55ms ( I believe that is more realistic, but I will stay with 15ms the average ). My quick measurements here show tolerances of approx. 1S = 7-8ms, where within 3S = 21-24 ms I should stay within 95% for all time resolution outliers. But its just a rough test on a virtual machine and no scientific result, but it shows that the resolution shall be far below 1 Sec. With a upper/lower limit of 3S I could catch only very few outliers, and within 4S it showed no more outliers at all. Because of that I would safely assume that a tolerance of 4S = 28-32ms is a realistic worst-case scenario for my virtual machine, which matches to Raymonds note. All in all, that is what I expected and good to know that there is at least an official statement from Microsoft too. It may be possible that Windows system timer functions gets massively blocked by various extra loads, which may then have a good reason. I don't expect any realtime accuracy from those kind of timers in that group anyway. Perhaps the 1 Sec. Note from Embarcadero ist just stupid a "Warning", that the Now() Function is not intended for any precise measurements, although it do support ms resolution. 10 hours ago, Anders Melander said: OP: Unambiguous question about the behavior of X Apart from that, https://www.scientificamerican.com/article/time-s-passage-is-probably-an-illusion/ ... I too would go for TStopWatch - even if you don't need the precision. Yes, time is relative ... good that you remind me that. Thats why I can perfectly live with 15ms timers too, in about 99.9% of uncritical cases Regarding TStopwatch.Timestamp(), this is based on the following system functions, whereas I want to avoid the macOS "mach_absolute_time()" function for obvious reasons https://en.delphipraxis.net/topic/13126-apple-gettimertick64-mach_absolute_time-how-to-handle-the-nsprivacyaccessedapitype-privacy/?tab=comments#comment-102072 class function TStopwatch.GetTimeStamp: Int64; //## MSWINDOWS: if FIsHighResolution then QueryPerformanceCounter(Result) else Result := GetTickCount * Int64(TTimeSpan.TicksPerMillisecond); //## MACOS: Result := Int64(AbsoluteToNanoseconds( mach_absolute_time ) div 100); //## POSIX: clock_gettime(CLOCK_MONOTONIC, @res); TStopwatch relies also on GetTickCount as fallback, instead of GetTickCount64, which I hope is only a relict of older Windows versions and would not be seen on any modern ( 10yr ) PC anyway. Share this post Link to post