Windows Sleep precision lazarus / 67-Windows ---------------------------------------------------------------------- 440bx · 2022-07-21 06:37 Hello, Some of you may remember a somewhat recent "discussion" about the accuracy, or lack thereof, of the Sleep() API. Some forum members did a few tests on machines they had available and the results seemed to vary quite a bit.  I decided to investigate the issue. On real hardware, I get very accurate times.  In VMs, the results were "erratic" to put it kindly but, they can be made very close to being as precise as on real hardware.  The "trick" is to set the timer resolution using undocumented ntdll.dll APIs. Here is a little program that queries the timer resolution _and_ "optionally" sets the timer to the maximum resolution.  After setting the timer to the maximum resolution, the Sleep times are very accurate even in a VM. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$APPTYPE CONSOLE} program _NtSetTimerResolution; uses  Windows,  Sysutils  ; type  NTSTATUS = DWORD; const  ntdll = 'ntdll';  function NtSetTimerResolution           (            { _in_  } InDesiredTime   : DWORD;            { _in_  } InSetResolution : boolean;            { _out_ } OutActualTime   : PDWORD           )         : NTSTATUS; stdcall; external ntdll; function NtQueryTimerResolution           (          { _out_ } OutMinimumResolution : PDWORD;            { _out_ } OutMaximumResolution : PDWORD;            { _out_ } OutCurrentResolution : PDWORD           )         : NTSTATUS; stdcall; external ntdll;  const  STATUS_OK = 0; var  MaximumResolution, MinimumResolution, CurrentResolution : DWORD;   Status { NTSTATUS }                                   : DWORD; begin  writeln;   writeln('  NtSetTimerResolution');  writeln;  writeln('  sets the timer resolution to maximum resolution possible');  writeln;   MinimumResolution := 0;  MaximumResolution := 0;  CurrentResolution := 0;   Status := NtQueryTimerResolution(@MinimumResolution,                                 @MaximumResolution,                                 @CurrentResolution);   repeat    if Status <> STATUS_OK then    begin      writeln('  call to NtQueryTimerResolution failed. NTSTATUS: ',              IntToHex(Status, 0));      break;    end;     writeln;    writeln('  Minimum Resolution : ', MinimumResolution);    writeln('  Maximum Resolution : ', MaximumResolution);    writeln('  Current Resolution : ', CurrentResolution);     writeln;    writeln('  press ENTER/RETURN to set the resolution to the maximum');    writeln('  resolution or CTRL-C to terminate without setting it.');  readln;     { for good measure, reset the current resolution variable               }     CurrentResolution := 0;    Status := NtSetTimerResolution(MaximumResolution,                                 TRUE,                                  @CurrentResolution);     if Status <> STATUS_OK then    begin      writeln('  call to NtSetTimerResolution failed. NTSTATUS: ',              IntToHex(Status, 0));      break;    end;     writeln;    writeln('  NtSetTimerResolution returned CurrentResolution : ',            CurrentResolution);    writeln;     { redo the call to NtQueryTimerResolution                               }     MinimumResolution := 0;    MaximumResolution := 0;    CurrentResolution := 0;     Status := NtQueryTimerResolution(@MinimumResolution,                                   @MaximumResolution,                                     @CurrentResolution);     writeln('  NtQueryTimerResolution now reports : ');     writeln;    writeln('  Minimum Resolution : ', MinimumResolution);    writeln('  Maximum Resolution : ', MaximumResolution);    writeln('  Current Resolution : ', CurrentResolution);   until TRUE;    writeln;  writeln('press ENTER/RETURN to end this program');  readln;end.            However, there is an interesting "peculiarity" concerning the bitness of the program. if a 32bit version of the above code is run on a 64bit O/S then the timer resolution seems to apply only to the process, however, if a 64bit version of the above code is run on the same 64bit O/S (real hardware or VM, doesn't matter), then the resolution is set "globally" and persists after the program has ended (even in a VM.) It seems that 32bit programs running on a 64bit O/S are genuine "second class" citizens. Enjoy. PS: personally, I have not experienced _any_ negative side effects from setting the resolution to the maximum.  On the contrary, after setting the resolution, Sleep is quite accurate, which is definitely welcome. PPS: Sysinternals' Clockres utility is implemented using "NtQueryTimerResolution" just as the above program is but, unlike the program above, it does not allow the user to set the timer resolution. ---------------------------------------------------------------------- rvk · 2022-07-21 08:17 Have you tried to put a Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i TimeBeginPeriod(1);in your code instead of the NtSetTimerResolution? (I already mentioned and showed that in the other topic) And what versions of 32 and 64 where you testing for the global issue? It was also discussed in the other topic that Windows 10 < version 2004 this setting was global, later versions not. ---------------------------------------------------------------------- 440bx · 2022-07-21 09:18 Quote from: rvk on July 21, 2022, 10:17:27 amHave you tried to put a Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i TimeBeginPeriod(1);in your code instead of the NtSetTimerResolution? No, I haven't tried that and, disassembly of that function shows it's just a roundabout way of calling NtSetTimerResolution. The disassembly of that function is:Code: ASM  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i .text:000007FF71CCA648 ; MMRESULT __stdcall timeBeginPeriod(UINT uPeriod).text:000007FF71CCA648                 public timeBeginPeriod.text:000007FF71CCA648 timeBeginPeriod proc near               ; CODE XREF: timeSetEventInternal+A7↓p.text:000007FF71CCA648                                       ; midiOutTimerTick+1CA↓p.text:000007FF71CCA648                                       ; DATA XREF: ....text:000007FF71CCA648.text:000007FF71CCA648 ActualResolution= dword ptr  8.text:000007FF71CCA648 arg_8           = qword ptr  10h.text:000007FF71CCA648 arg_10          = qword ptr  18h.text:000007FF71CCA648.text:000007FF71CCA648 ; FUNCTION CHUNK AT .text:000007FF71CCD636 SIZE 0000008A BYTES.text:000007FF71CCA648.text:000007FF71CCA648                 mov   [rsp+arg_8], rbx.text:000007FF71CCA64D                 mov     [rsp+arg_10], rbp.text:000007FF71CCA652                 push    rdi.text:000007FF71CCA653                 push    r12.text:000007FF71CCA655                 push    r13.text:000007FF71CCA657                 sub     rsp, 20h.text:000007FF71CCA65B                 mov     edi, ecx.text:000007FF71CCA65D                 cmp     ecx, cs:TDD_MAXRESOLUTION.text:000007FF71CCA663                 jb      loc_7FF71CCD636.text:000007FF71CCA669                 cmp     ecx, cs:uPeriod.text:000007FF71CCA66F                 jnb     loc_7FF71CCD640.text:000007FF71CCA675                 lea     rcx, ResolutionCritSec ; lpCriticalSection.text:000007FF71CCA67C               call    cs:__imp_EnterCriticalSection.text:000007FF71CCA682               mov     r11d, edi.text:000007FF71CCA685                 lea   rbp, TimerData.text:000007FF71CCA68C                 sub     r11d, cs:TDD_MAXRESOLUTION.text:000007FF71CCA693                 mov     r13d, 0FFFFh.text:000007FF71CCA699                 movzx   eax, word ptr [rbp+r11*2+46h].text:000007FF71CCA69F                 cmp     ax, r13w.text:000007FF71CCA6A3                 jz      loc_7FF71CCD647.text:000007FF71CCA6A9                 inc     ax.text:000007FF71CCA6AC                 mov     [rbp+r11*2+46h], ax.text:000007FF71CCA6B2                 cmp     ax, 1.text:000007FF71CCA6B6                 jnz     loc_7FF71CCD6B9.text:000007FF71CCA6BC                 cmp     edi, cs:dword_7FF71CEE3FC.text:000007FF71CCA6C2                 jnb     loc_7FF71CCD6B9.text:000007FF71CCA6C8                 mov     rcx, cs:WPP_GLOBAL_Control.text:000007FF71CCA6CF                 lea     r12, WPP_GLOBAL_Control.text:000007FF71CCA6D6                 cmp     rcx, r12.text:000007FF71CCA6D9                 jz      short loc_7FF71CCA6EC.text:000007FF71CCA6DB                 bt      dword ptr [rcx+1Ch], 16h.text:000007FF71CCA6E0                 jnb     short loc_7FF71CCA6EC.text:000007FF71CCA6E2                 cmp     byte ptr [rcx+19h], 5.text:000007FF71CCA6E6                 jnb     loc_7FF71CCD656.text:000007FF71CCA6EC.text:000007FF71CCA6EC loc_7FF71CCA6EC:                        ; CODE XREF: timeBeginPeriod+91↑j.text:000007FF71CCA6EC                                       ; timeBeginPeriod+98↑j ....text:000007FF71CCA6EC               mov     eax, cs:MinimumTime.text:000007FF71CCA6F2               mov     ecx, edi.text:000007FF71CCA6F4                 lea     r8, [rsp+38h+ActualResolution] ; ActualResolution.text:000007FF71CCA6F9               imul    ecx, 2710h.text:000007FF71CCA6FF                 cmp     ecx, eax.text:000007FF71CCA701                 mov     dl, 1         ; SetOrUnset.text:000007FF71CCA703                 cmovb   ecx, eax        ; RequestedResolution.text:000007FF71CCA706               mov     [rsp+38h+ActualResolution], ecx.text:000007FF71CCA70A               call    cs:__imp_NtSetTimerResolution.text:000007FF71CCA710                 xor     ebx, ebx.text:000007FF71CCA712                 cmp     eax, ebx.text:000007FF71CCA714                 jl      loc_7FF71CCD674.text:000007FF71CCA71A                 mov     ecx, [rsp+38h+ActualResolution].text:000007FF71CCA71E                 mov   eax, 0D1B71759h.text:000007FF71CCA723                 mov     cs:dword_7FF71CEE3F8, edi.text:000007FF71CCA729                 add   ecx, 26ACh.text:000007FF71CCA72F                 mul     ecx.text:000007FF71CCA731                 shr     edx, 0Dh.text:000007FF71CCA734                 mov     cs:dword_7FF71CEE3FC, edx.text:000007FF71CCA73A                 jmp   short $+2.text:000007FF71CCA73C ; ----------------------------------- ----------------------------------------.text:000007FF71CCA73C.text:00 0007FF71CCA73C loc_7FF71CCA73C:                        ; CODE XREF: timeBeginPeriod+F2↑j.text:000007FF71CCA73C                                       ; timeBeginPeriod+306C↓j ....text:000007FF71CCA73C               lea     rcx, ResolutionCritSec ; lpCriticalSection.text:000007FF71CCA743                 call    cs:__imp_LeaveCriticalSection.text:000007FF71CCA749                 mov     eax, ebx.text:000007FF71CCA74B.text:000007FF71CCA74B loc_7FF71CCA74B:                        ; CODE XREF: timeBeginPeriod+2FF3↓j.text:000007FF71CCA74B                                       ; timeBeginPeriod+2FFA↓j.text:000007FF71CCA74B               mov     rbx, [rsp+38h+arg_8].text:000007FF71CCA750               mov     rbp, [rsp+38h+arg_10].text:000007FF71CCA755                 add     rsp, 20h.text:000007FF71CCA759                 pop     r13.text:000007FF71CCA75B                 pop     r12.text:000007FF71CCA75D                 pop     rdi.text:000007FF71CCA75E                 retn.text:000007FF71CCA75E ; ---------------------------------------------------------------------- -----.text:000007FF71CCA75F                 db 9 dup(90h).text:000007FF71CCA75F timeBeginPeriod endp.text:000007FF71CCA75F.text:000007FF71CCA768 ; Exported entry 138. timeEndPeriod.text:000007FF71CCA768.text:000007FF71CCA768 ; =============== S U B R O U T I N E ================================== =====.text:000007FF71CCA768.text:000007FF71CCA768.text:000007FF71CCA76 8 ; MMRESULT __stdcall timeEndPeriod(UINT uPeriod).text:000007FF71CCA768                 public timeEndPeriod.text:000007FF71CCA768 timeEndPeriod   proc near             ; CODE XREF: timeKillEvent+4D↓p.text:000007FF71CCA768                                       ; timeSetEventInternal+2AA9↓p ....text:000007FF71CCA768.text:000007FF71CCA768 arg_0           = dword ptr  8.text:000007FF71CCA768 ActualResolution= dword ptr  10h.text:000007FF71CCA768 arg_10          = qword ptr  18h.text:000007FF71CCA768.text:000007FF71CCA768 ; FUNCTION CHUNK AT .text:000007FF71CCD6C0 SIZE 000000CE BYTES.text:000007FF71CCA768.text:000007FF71CCA768                 mov   [rsp+arg_10], rbx.text:000007FF71CCA76D                 push    rbp.text:000007FF71CCA76E                 push    rsi.text:000007FF71CCA76F                 push    rdi.text:000007FF71CCA770                 sub     rsp, 20h.text:000007FF71CCA774                 mov     ebx, ecx.text:000007FF71CCA776                 cmp     ecx, cs:TDD_MAXRESOLUTION.text:000007FF71CCA77C                 jb      loc_7FF71CCD6C0.text:000007FF71CCA782                 cmp     ecx, cs:uPeriod.text:000007FF71CCA788                 jnb     loc_7FF71CCD6CA.text:000007FF71CCA78E                 lea     rcx, ResolutionCritSec ; lpCriticalSection.text:000007FF71CCA795               call    cs:__imp_EnterCriticalSection.text:000007FF71CCA79B               mov     r8d, cs:TDD_MAXRESOLUTION.text:000007FF71CCA7A2               mov     r11d, ebx.text:000007FF71CCA7A5                 sub     r11d, r8d.text:000007FF71CCA7A8                 lea     r9, TimerData.text:000007FF71CCA7AF                 xor     edi, edi.text:000007FF71CCA7B1                 movzx   eax, word ptr [r9+r11*2+46h].text:000007FF71CCA7B7                 cmp     ax, di.text:000007FF71CCA7BA                 jz      loc_7FF71CCD6D1.text:000007FF71CCA7C0                 mov     ebp, 1.text:000007FF71CCA7C5                 sub     ax, bp.text:000007FF71CCA7C8                 mov     [r9+r11*2+46h], ax.text:000007FF71CCA7CE                 jnz     short loc_7FF71CCA82B.text:000007FF71CCA7D0                 cmp     ebx, cs:dword_7FF71CEE3F8.text:000007FF71CCA7D6                 jnz     short loc_7FF71CCA82B.text:000007FF71CCA7D8                 mov     edx, cs:uPeriod.text:000007FF71CCA7DE                 jmp     short $+2.text:000007FF71CCA7E0 ; ------------------------------------------ ---------------------------------.text:000007FF71CCA7E0.text:000007FF7 1CCA7E0 loc_7FF71CCA7E0:                        ; CODE XREF: timeEndPeriod+76↑j.text:000007FF71CCA7E0                                       ; timeEndPeriod+8B↓j.text:000007FF71CCA7E0                 cmp     ebx, edx.text:000007FF71CCA7E2                 jnb     short loc_7FF71CCA7F5.text:000007FF71CCA7E4                 mov     eax, ebx.text:000007FF71CCA7E6                 sub     eax, r8d.text:000007FF71CCA7E9                 cmp     [r9+rax*2+46h], di.text:000007FF71CCA7EF                 jnz     short loc_7FF71CCA7F5.text:000007FF71CCA7F1                 add     ebx, ebp.text:000007FF71CCA7F3                 jmp     short loc_7FF71CCA7E0.text:000007FF71CCA7F5 ; ------------------------------ ---------------------------------------------.text:000007FF71CCA7F5.te xt:000007FF71CCA7F5 loc_7FF71CCA7F5:                        ; CODE XREF: timeEndPeriod+7A↑j.text:000007FF71CCA7F5                                       ; timeEndPeriod+87↑j.text:000007FF71CCA7F5               mov     ecx, cs:dword_7FF71CEE3FC.text:000007FF71CCA7FB               lea     r8, [rsp+38h+ActualResolution] ; ActualResolution.text:000007FF71CCA800                 xor     edx, edx        ; SetOrUnset.text:000007FF71CCA802                 imul    ecx, 2710h      ; RequestedResolution.text:000007FF71CCA808               call    cs:__imp_NtSetTimerResolution.text:000007FF71CCA80E               mov     r11d, cs:uPeriod.text:000007FF71CCA815               mov     cs:dword_7FF71CEE3FC, r11d.text:000007FF71CCA81C               mov     cs:dword_7FF71CEE3F8, ebx.text:000007FF71CCA822               cmp     ebx, r11d.text:000007FF71CCA825                 jb      loc_7FF71CCD6DB.text:000007FF71CCA82B.text:000007FF71CCA82B loc_7FF71CCA82B:                        ; CODE XREF: timeEndPeriod+66↑j.text:000007FF71CCA82B                                       ; timeEndPeriod+6E↑j ....text:000007FF71CCA82B               lea     rcx, ResolutionCritSec ; lpCriticalSection.text:000007FF71CCA832                 call    cs:__imp_LeaveCriticalSection.text:000007FF71CCA838                 mov     eax, edi.text:000007FF71CCA83A.text:000007FF71CCA83A loc_7FF71CCA83A:                        ; CODE XREF: timeEndPeriod+2F5D↓j.text:000007FF71CCA83A                                       ; timeEndPeriod+2F64↓j.text:000007FF71CCA83A               mov     rbx, [rsp+38h+arg_10].text:000007FF71CCA83F                 add     rsp, 20h.text:000007FF71CCA843                 pop     rdi.text:000007FF71CCA844                 pop     rsi.text:000007FF71CCA845                 pop     rbp.text:000007FF71CCA846                 retn.text:000007FF71CCA846 ; ---------------------------------------------------------------------- -----.text:000007FF71CCA847                 align 10h.text:000007FF71CCA847 timeEndPeriod   endp  Quote from: rvk on July 21, 2022, 10:17:27 amAnd what versions of 32 and 64 where you testing for the global issue? It was also discussed in the other topic that Windows 10 < version 2004 this setting was global, later versions not. All tests, VM and real machine, were done on Win7 SP1 64bit but, I just tested it on Windows 10 21H2 in a VM and on that VM, Sleep remains inaccurate even after running the program.  I don't have a Win 10 installation on real hardware (and doubt I'll ever have one.) Eventually, I'll modify the Sleep test program to call NtSetTimerResolution just before the call to Sleep.  Maybe that would make a difference in Win 10. ---------------------------------------------------------------------- 440bx · 2022-07-21 09:41 Here is the modified TestSleep program Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$APPTYPE CONSOLE} program TestSleep2;   uses    Windows,    sysutils    ;  type  NTSTATUS = DWORD; const  ntdll = 'ntdll';  function NtSetTimerResolution           (            { _in_  } InDesiredTime   : DWORD;            { _in_  } InSetResolution : boolean;            { _out_ } OutActualTime   : PDWORD           )         : NTSTATUS; stdcall; external ntdll; function NtQueryTimerResolution           (          { _out_ } OutMinimumResolution : PDWORD;            { _out_ } OutMaximumResolution : PDWORD;            { _out_ } OutCurrentResolution : PDWORD           )         : NTSTATUS; stdcall; external ntdll;  const  STATUS_OK = 0; var  MaximumResolution, MinimumResolution, CurrentResolution : DWORD;   Status { NTSTATUS }                                   : DWORD;    procedure SleepLoop(ms : DWORD);  const    COUNTER_LIMIT = 1000;   var    counter : DWORD = 0;   begin    writeln;    writeln('ms : ', ms, ' repeat count ', COUNTER_LIMIT);    for counter := 1 to COUNTER_LIMIT do    begin      Sleep(ms);    end;  end; begin  writeln;   writeln('  NtSetTimerResolution');  writeln;  writeln('  sets the timer resolution to maximum resolution possible');  writeln;   MinimumResolution := 0;  MaximumResolution := 0;  CurrentResolution := 0;   Status := NtQueryTimerResolution(@MinimumResolution,                                 @MaximumResolution,                                 @CurrentResolution);   if Status <> STATUS_OK then  begin    writeln('  call to NtQueryTimerResolution failed. NTSTATUS: ',          IntToHex(Status, 0));    ExitProcess(1);  end;   writeln;  writeln('  Minimum Resolution : ', MinimumResolution);  writeln('  Maximum Resolution : ', MaximumResolution);  writeln('  Current Resolution : ', CurrentResolution);   { for good measure, reset the current resolution variable                 }   CurrentResolution := 0;  Status := NtSetTimerResolution(MaximumResolution,                               TRUE,                                @CurrentResolution);   if Status <> STATUS_OK then  begin    writeln('  call to NtSetTimerResolution failed. NTSTATUS: ',            IntToHex(Status, 0));    ExitProcess(2);  end;   writeln;  writeln('  NtSetTimerResolution returned CurrentResolution : ',          CurrentResolution);  writeln;   { redo the call to NtQueryTimerResolution                               }   MinimumResolution := 0;  MaximumResolution := 0;  CurrentResolution := 0;   Status := NtQueryTimerResolution(@MinimumResolution,                                 @MaximumResolution,                                 @CurrentResolution);   writeln('  NtQueryTimerResolution now reports : ');   writeln;  writeln('  Minimum Resolution : ', MinimumResolution);  writeln('  Maximum Resolution : ', MaximumResolution);  writeln('  Current Resolution : ', CurrentResolution);    SleepLoop(1);  { use values 1, 8 and 16 }end.      That version, in a Windows 10 21H2 VM executes in a time that is close to the expected time (1s).  It usually executes in a hair under 2 seconds while on Win 7 SP1, it executes in 1.1 seconds. Some of the difference in the timings _might_ be due to Windows defender. ---------------------------------------------------------------------- rvk · 2022-07-21 09:47 Quote from: 440bx on July 21, 2022, 11:18:32 amQuote from: rvk on July 21, 2022, 10:17:27 amHave you tried to put a Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i TimeBeginPeriod(1);in your code instead of the NtSetTimerResolution? No, I haven't tried that and, disassembly of that function shows it's just a roundabout way of calling NtSetTimerResolution. The disassembly of that function is:Well, that code/assembler doesn't come from FPC itself. The TimeBeginPeriod (defined in mmsystem) calls the Windows api timeBeginPeriod in winmm.dll directly. So at least that one is documented  (unlike the NtSetTimerResolution). Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Function timeBeginPeriod(x1: UINT): MMRESULT;stdcall; external 'winmm.dll' name 'timeBeginPeriod'; ---------------------------------------------------------------------- 440bx · 2022-07-21 10:03 Quote from: rvk on July 21, 2022, 11:47:36 amWell, that code/assembler doesn't come from FPC itself. You're right about that, that (dis) assembly code is from IDA Pro. Quote from: rvk on July 21, 2022, 11:47:36 amSo at least that one is documented  (unlike the NtSetTimerResolution). AFAIC, if MS can use a function, I can use it too.  (I paid for the O/S and the computer, I want everything I paid for! ) Also, if it's good enough for Bryce Cogswell and Mark Russinovich then, it's good enough for me too. (their ClockRes is implemented using NtQueryTimerResolution) I just tested the modified version of TestSleep in the Win10 VM, the results are: if the loop is supposed to consume one (1) second, it executes in two (2) seconds, 8 seconds consumes 9 seconds and 16 seconds consume 17 seconds.  Since the loop executes 1000 times, it means there is always 1ms lost somewhere. It's definitely an improvement over the "standard" results in Win 10 but still nowhere near as accurate as in Win7 SP1. ---------------------------------------------------------------------- ASerge · 2022-07-21 18:32 Quote from: 440bx on July 21, 2022, 08:37:22 amPS: personally, I have not experienced _any_ negative side effects from setting the resolution to the maximum.  On the contrary, after setting the resolution, Sleep is quite accurate, which is definitely welcome. From the timeBeginPeriod document QuoteSetting a higher resolution can improve the accuracy of time-out intervals in wait functions. However, it can also reduce overall system performance, because the thread scheduler switches tasks more often. High resolutions can also prevent the CPU power management system from entering power-saving modes. Setting a higher resolution does not improve the accuracy of the high-resolution performance counter.I think it's not useful for energy consumption either. ---------------------------------------------------------------------- 440bx · 2022-07-21 18:42 Quote from: ASerge on July 21, 2022, 08:32:22 pmQuoteSetting a higher resolution can improve the accuracy of time-out intervals in wait functions. However, it can also reduce overall system performance, because the thread scheduler switches tasks more often. High resolutions can also prevent the CPU power management system from entering power-saving modes. Setting a higher resolution does not improve the accuracy of the high-resolution performance counter.I think it's not useful for energy consumption either. I read that too but, my personal experience is the opposite of what is stated there (on Win7 and previous O/Ss.) So far, the system runs very smoothly and programs that use Sleep() run better.  That's noticeable in Win 10 (VM) where the Sleep times (without being explicitly set) are erratic. Honestly, I take that statement with a very large grain of salt because, the fact that scheduler code gets to run more often does _not_ mean that switches happen more often.  A task switch is dependent on a lot of things but, very little on the timer resolution since scheduler code runs already runs a lot more often than regular threads.  IOW, just because scheduler code gets to run does _not_ mean a task switch will occur. ---------------------------------------------------------------------- Joanna · 2025-01-29 00:33 Out of curiosity I found some things that use sleep(0) Blocksock unit , line 2303 Synapse unit , lines 1238 & 1878 I’ve also used sleep to slow down my own project that was using too much cpu. Intuitively as a non expert I can say that sleep(0) Seems like a procedure call whose delay would only be the duration of the procedure call itself. It’s seems useless. Why not just use sleep(1) ? thats hardly any time at all. ---------------------------------------------------------------------- rvk · 2025-01-29 00:39 Quote from: Joanna from IRC on January 29, 2025, 01:33:52 amIntuitively as a non expert I can say that sleep(0) Seems like a procedure call whose delay would only be the duration of the procedure call itself. It’s seems useless. See https://stackoverflow.com/a/3727460/1037511 QuoteAccording to MSDN's documentation for Sleep: A value of zero causes the thread to relinquish the remainder of its time slice to any other thread that is ready to run. If there are no other threads ready to run, the function returns immediately, and the thread continues execution.So it's not meant to sleep but to relinquish the remainder of its time slice. ---------------------------------------------------------------------- marcov · 2025-01-29 10:29 Afaik precision greatly increased on real hardware since either Nehalem  (core first generation) or its successor Sandy Bridge (second gen), as the high precision timer was moved from chipset to CPU/SOC. You could test on real older hardware, e.g. some Core2 derivate. ---------------------------------------------------------------------- MarkMLl · 2025-01-29 10:36 Quote from: Joanna from IRC on January 29, 2025, 01:33:52 amIntuitively as a non expert I can say that sleep(0) Seems like a procedure call whose delay would only be the duration of the procedure call itself. It’s seems useless. Why not just use sleep(1) ? thats hardly any time at all. As a non-expert you'd be better saying nothing, or at least looking to see what's been posted in the forum over the last day or so: Quote from: Thaddy on January 28, 2025, 10:46:59 amSince the platform is Windows, use sleep(0). Period. The problem is the thread priorities, which you can set in code. Sleep(0) only relinguishes to threads with the same or higher priorities. That is the essence of the wrong suggestions and they are very wrong. Introducing a real sleep is not warranted nor necessary, it relies on determination - no, even perception - for a particular situation, not fact. That is really bad programming. https://www.home.saxo Some of which uses Sleep(0) for the trades view. (I wrote that, so I know, it is the same code from my Binckbank/Alex days, who payed for my pension, thank you very much, really comfortable) MarkMLl ---------------------------------------------------------------------- ccrause · 2025-01-29 13:03 Quote from: rvk on January 29, 2025, 01:39:40 amQuote from: Joanna from IRC on January 29, 2025, 01:33:52 amIntuitively as a non expert I can say that sleep(0) Seems like a procedure call whose delay would only be the duration of the procedure call itself. It’s seems useless. See https://stackoverflow.com/a/3727460/1037511 QuoteAccording to MSDN's documentation for Sleep: A value of zero causes the thread to relinquish the remainder of its time slice to any other thread that is ready to run. If there are no other threads ready to run, the function returns immediately, and the thread continues execution.So it's not meant to sleep but to relinquish the remainder of its time slice. A link to the (more) up to date documentation for Sleep: https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf- synchapi-sleep ---------------------------------------------------------------------- Joanna · 2025-01-29 14:33 So sleep(0) is the same behavior as that thing that would be put into loops to respond to events? {Unfortunately I don’t remember what is called and everything I try in autocomplete doesn’t work and everyone who I would have asked is gone} ---------------------------------------------------------------------- MarkMLl · 2025-01-29 14:55 Quote from: Joanna from IRC on January 29, 2025, 03:33:32 pmSo sleep(0) is the same behavior as that thing that would be put into loops to respond to events? {Unfortunately I don’t remember what is called and everything I try in autocomplete doesn’t work and everyone who I would have asked is gone} No, you're thinking about Application.ProcessMessages (often referred to as APM). A program is animated by a process, a process comprises one or more threads. One master/foreground thread handles the GUI. If your master thread (in the context of a GUI program) is in a tight loop (e.g. to iterate through a million numbers) then putting in an APM will make sure that that program's GUI remains responsive BUT it's all happening in the context of the main thread. If /any/ thread, irrespective of whether it's associated with a GUI program or not, wants to give up the CPU for a few mSec then it can call Sleep(). That's "polite": it's a hint to the OS that that particular thread isn't in a screaming rush to get things done and some CPU time can be given to other threads in either the same process (program) or others. The alternative would be to wait until the OS preempts the thread at the next tick, but it might or might not realise that giving time to a different one would be the best way of keeping the overall system running smoothly. There's usually better ways to do it rather than interleaving e.g. poll and sleep calls, but just about everybody does that sort of thing now and again even if they don't like to admit it. MarkMLl ---------------------------------------------------------------------- DragoRosso · 2025-01-29 17:55 Quote from: MarkMLl on January 29, 2025, 03:55:18 pmThere's usually better ways to do it rather than interleaving e.g. poll and sleep calls, but just about everybody does that sort of thing now and again even if they don't like to admit it. Well maybe something better than the 80s techniques (polling and sleeping) can be done ... now (for decades now) there are waitfor methods.... THREADs can be put in standby (even infinitely) until a specific event (or more events) occurs in this way polling, sleep or other are not necessary. The use of the sleep() procedure should be considered (like others said), and a well-developed application does not normally need sleep. Threads MUST remain "asleep" until their action is needed. The WAITFOR... in Windows is linked directly to the operating system kernel. I use sleep only deterministically (with timebeginperiod(x)) for old hardware procedures, I discovered years ago (many years ago) that WAITFOR is the best way to use a THREAD. Many years ago, when processor power was still limited, simply using WAITFOR compared to the very old but common "sleep" in the while of a thread I managed to lower the average CPU temperature by over 15 degrees, ensuring greater functionality, duration and reliability. And forget about Application.ProcessMessages, it is useless and can cause several problems that are difficult to debug. If someone feels compelled to use it, then they need to rethink their application. ---------------------------------------------------------------------- 440bx · 2025-01-29 18:26 The purpose of Sleep is to prevent using 100% of a core for an extended (user noticeable) period of time when doing lengthy calculations. As far as waiting  for a thread, the simplest method is to have the thread that is doing some work send a message to the U.I thread informing it when it's done.  This method works in a great number of common cases and it is much simpler, not to mention safer, than waiting for one or more threads which can be a source of deadlocks. Also, polling may sound pedestrian but, it can often be very, very cheap to implement and also very CPU friendly.  For instance, having the U.I check the value of some variable whenever it gets a message.  The variable value check might consume 10 clock cycles which is nothing in the message dispatching loop.  All that said, there are cases when waiting for an  event of one kind or another is the most efficient implementation but, a lot of common cases can be handled nicely and easily by simply using a message to notify the U.I thread some task is done. ---------------------------------------------------------------------- Sander · 2025-05-20 22:46 Here is a view from an old guy who grew up with DOS, and only writes windows console programs because you pretty much have to these days. In my experience (XP32), "sleep" is indeed not very accurate and usually returns late by about 10 to 20 milliseconds. On very rare occasions it returns a little early. This is good enough for a lot of things where you need a delay, and I never thought of improving the accuracy of it. On the laptop that I write my software on, Windows steals time in chunks of about 16 milliseconds, and it can and do that whenever it feels like it. This suspiciously corresponds to 60 Hz, but okay, it is what it is. It all depends on what the "sleep" is for. One can use a delay in case a piece of hardware needs some recovery time, but I don't think that a "delay from now" is a good way to get something to pace at regular constant  intervals. Over time, the intended interval can really start to drift. One of my programs reads a few GPIB instruments at regular intervals for long term (days) monitoring of things. For this, I have used a "target time" approach. The target time for the next instrument loop is set based on the target time of the previous loop. The instrument loop checks the keyboard (yeah folks, it's a terminal app..) in between dealing with the instruments one by one. When the instrument loop is done,  I call a wait loop which fills the remaining time with keyboard checks and other updates until "now" is greater or equal to the target time, after which a new target time is calculated. Although the  individual time stamps of the instrument loops can still be off by a few milliseconds, the long term stability of the time interval is very good. It does the job without delving into threads and event handlers, so it's good enough for me. It gets easy once you realize that a "tdatetime" is a double precision float which counts in days. You can actually do direct math with time without the need of specialized "time manipulation" functions. Sander