Recent

Author Topic: Windows Sleep precision  (Read 1418 times)

440bx

  • Hero Member
  • *****
  • Posts: 4014
Windows Sleep precision
« on: July 21, 2022, 08:37:22 am »
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][+][-]
  1. {$APPTYPE CONSOLE}
  2.  
  3. program _NtSetTimerResolution;
  4.  
  5. uses
  6.   Windows,
  7.   Sysutils
  8.   ;
  9.  
  10. type
  11.   NTSTATUS = DWORD;
  12.  
  13. const
  14.   ntdll = 'ntdll';
  15.  
  16.  
  17. function NtSetTimerResolution
  18.            (
  19.             { _in_  } InDesiredTime   : DWORD;
  20.             { _in_  } InSetResolution : boolean;
  21.             { _out_ } OutActualTime   : PDWORD
  22.            )
  23.          : NTSTATUS; stdcall; external ntdll;
  24.  
  25. function NtQueryTimerResolution
  26.            (
  27.             { _out_ } OutMinimumResolution : PDWORD;
  28.             { _out_ } OutMaximumResolution : PDWORD;
  29.             { _out_ } OutCurrentResolution : PDWORD
  30.            )
  31.          : NTSTATUS; stdcall; external ntdll;
  32.  
  33.  
  34. const
  35.   STATUS_OK = 0;
  36.  
  37. var
  38.   MaximumResolution, MinimumResolution, CurrentResolution : DWORD;
  39.  
  40.   Status { NTSTATUS }                                     : DWORD;
  41.  
  42. begin
  43.   writeln;
  44.  
  45.   writeln('  NtSetTimerResolution');
  46.   writeln;
  47.   writeln('  sets the timer resolution to maximum resolution possible');
  48.   writeln;
  49.  
  50.   MinimumResolution := 0;
  51.   MaximumResolution := 0;
  52.   CurrentResolution := 0;
  53.  
  54.   Status := NtQueryTimerResolution(@MinimumResolution,
  55.                                    @MaximumResolution,
  56.                                    @CurrentResolution);
  57.  
  58.   repeat
  59.     if Status <> STATUS_OK then
  60.     begin
  61.       writeln('  call to NtQueryTimerResolution failed. NTSTATUS: ',
  62.               IntToHex(Status, 0));
  63.       break;
  64.     end;
  65.  
  66.     writeln;
  67.     writeln('  Minimum Resolution : ', MinimumResolution);
  68.     writeln('  Maximum Resolution : ', MaximumResolution);
  69.     writeln('  Current Resolution : ', CurrentResolution);
  70.  
  71.     writeln;
  72.     writeln('  press ENTER/RETURN to set the resolution to the maximum');
  73.     writeln('  resolution or CTRL-C to terminate without setting it.');
  74.     readln;
  75.  
  76.     { for good measure, reset the current resolution variable                 }
  77.  
  78.     CurrentResolution := 0;
  79.     Status := NtSetTimerResolution(MaximumResolution,
  80.                                    TRUE,
  81.                                   @CurrentResolution);
  82.  
  83.     if Status <> STATUS_OK then
  84.     begin
  85.       writeln('  call to NtSetTimerResolution failed. NTSTATUS: ',
  86.               IntToHex(Status, 0));
  87.       break;
  88.     end;
  89.  
  90.     writeln;
  91.     writeln('  NtSetTimerResolution returned CurrentResolution : ',
  92.             CurrentResolution);
  93.     writeln;
  94.  
  95.     { redo the call to NtQueryTimerResolution                               }
  96.  
  97.     MinimumResolution := 0;
  98.     MaximumResolution := 0;
  99.     CurrentResolution := 0;
  100.  
  101.     Status := NtQueryTimerResolution(@MinimumResolution,
  102.                                      @MaximumResolution,
  103.                                      @CurrentResolution);
  104.  
  105.     writeln('  NtQueryTimerResolution now reports : ');
  106.  
  107.     writeln;
  108.     writeln('  Minimum Resolution : ', MinimumResolution);
  109.     writeln('  Maximum Resolution : ', MaximumResolution);
  110.     writeln('  Current Resolution : ', CurrentResolution);
  111.  
  112.   until TRUE;
  113.  
  114.  
  115.   writeln;
  116.   writeln('press ENTER/RETURN to end this program');
  117.   readln;
  118. 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.
« Last Edit: July 21, 2022, 08:47:02 am by 440bx »
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

rvk

  • Hero Member
  • *****
  • Posts: 6162
Re: Windows Sleep precision
« Reply #1 on: July 21, 2022, 10:17:27 am »
Have you tried to put a
Code: Pascal  [Select][+][-]
  1. 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

  • Hero Member
  • *****
  • Posts: 4014
Re: Windows Sleep precision
« Reply #2 on: July 21, 2022, 11:18:32 am »
Have you tried to put a
Code: Pascal  [Select][+][-]
  1. 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][+][-]
  1. .text:000007FF71CCA648 ; MMRESULT __stdcall timeBeginPeriod(UINT uPeriod)
  2. .text:000007FF71CCA648                 public timeBeginPeriod
  3. .text:000007FF71CCA648 timeBeginPeriod proc near               ; CODE XREF: timeSetEventInternal+A7↓p
  4. .text:000007FF71CCA648                                         ; midiOutTimerTick+1CA↓p
  5. .text:000007FF71CCA648                                         ; DATA XREF: ...
  6. .text:000007FF71CCA648
  7. .text:000007FF71CCA648 ActualResolution= dword ptr  8
  8. .text:000007FF71CCA648 arg_8           = qword ptr  10h
  9. .text:000007FF71CCA648 arg_10          = qword ptr  18h
  10. .text:000007FF71CCA648
  11. .text:000007FF71CCA648 ; FUNCTION CHUNK AT .text:000007FF71CCD636 SIZE 0000008A BYTES
  12. .text:000007FF71CCA648
  13. .text:000007FF71CCA648                 mov     [rsp+arg_8], rbx
  14. .text:000007FF71CCA64D                 mov     [rsp+arg_10], rbp
  15. .text:000007FF71CCA652                 push    rdi
  16. .text:000007FF71CCA653                 push    r12
  17. .text:000007FF71CCA655                 push    r13
  18. .text:000007FF71CCA657                 sub     rsp, 20h
  19. .text:000007FF71CCA65B                 mov     edi, ecx
  20. .text:000007FF71CCA65D                 cmp     ecx, cs:TDD_MAXRESOLUTION
  21. .text:000007FF71CCA663                 jb      loc_7FF71CCD636
  22. .text:000007FF71CCA669                 cmp     ecx, cs:uPeriod
  23. .text:000007FF71CCA66F                 jnb     loc_7FF71CCD640
  24. .text:000007FF71CCA675                 lea     rcx, ResolutionCritSec ; lpCriticalSection
  25. .text:000007FF71CCA67C                 call    cs:__imp_EnterCriticalSection
  26. .text:000007FF71CCA682                 mov     r11d, edi
  27. .text:000007FF71CCA685                 lea     rbp, TimerData
  28. .text:000007FF71CCA68C                 sub     r11d, cs:TDD_MAXRESOLUTION
  29. .text:000007FF71CCA693                 mov     r13d, 0FFFFh
  30. .text:000007FF71CCA699                 movzx   eax, word ptr [rbp+r11*2+46h]
  31. .text:000007FF71CCA69F                 cmp     ax, r13w
  32. .text:000007FF71CCA6A3                 jz      loc_7FF71CCD647
  33. .text:000007FF71CCA6A9                 inc     ax
  34. .text:000007FF71CCA6AC                 mov     [rbp+r11*2+46h], ax
  35. .text:000007FF71CCA6B2                 cmp     ax, 1
  36. .text:000007FF71CCA6B6                 jnz     loc_7FF71CCD6B9
  37. .text:000007FF71CCA6BC                 cmp     edi, cs:dword_7FF71CEE3FC
  38. .text:000007FF71CCA6C2                 jnb     loc_7FF71CCD6B9
  39. .text:000007FF71CCA6C8                 mov     rcx, cs:WPP_GLOBAL_Control
  40. .text:000007FF71CCA6CF                 lea     r12, WPP_GLOBAL_Control
  41. .text:000007FF71CCA6D6                 cmp     rcx, r12
  42. .text:000007FF71CCA6D9                 jz      short loc_7FF71CCA6EC
  43. .text:000007FF71CCA6DB                 bt      dword ptr [rcx+1Ch], 16h
  44. .text:000007FF71CCA6E0                 jnb     short loc_7FF71CCA6EC
  45. .text:000007FF71CCA6E2                 cmp     byte ptr [rcx+19h], 5
  46. .text:000007FF71CCA6E6                 jnb     loc_7FF71CCD656
  47. .text:000007FF71CCA6EC
  48. .text:000007FF71CCA6EC loc_7FF71CCA6EC:                        ; CODE XREF: timeBeginPeriod+91↑j
  49. .text:000007FF71CCA6EC                                         ; timeBeginPeriod+98↑j ...
  50. .text:000007FF71CCA6EC                 mov     eax, cs:MinimumTime
  51. .text:000007FF71CCA6F2                 mov     ecx, edi
  52. .text:000007FF71CCA6F4                 lea     r8, [rsp+38h+ActualResolution] ; ActualResolution
  53. .text:000007FF71CCA6F9                 imul    ecx, 2710h
  54. .text:000007FF71CCA6FF                 cmp     ecx, eax
  55. .text:000007FF71CCA701                 mov     dl, 1           ; SetOrUnset
  56. .text:000007FF71CCA703                 cmovb   ecx, eax        ; RequestedResolution
  57. .text:000007FF71CCA706                 mov     [rsp+38h+ActualResolution], ecx
  58. .text:000007FF71CCA70A                 call    cs:__imp_NtSetTimerResolution
  59. .text:000007FF71CCA710                 xor     ebx, ebx
  60. .text:000007FF71CCA712                 cmp     eax, ebx
  61. .text:000007FF71CCA714                 jl      loc_7FF71CCD674
  62. .text:000007FF71CCA71A                 mov     ecx, [rsp+38h+ActualResolution]
  63. .text:000007FF71CCA71E                 mov     eax, 0D1B71759h
  64. .text:000007FF71CCA723                 mov     cs:dword_7FF71CEE3F8, edi
  65. .text:000007FF71CCA729                 add     ecx, 26ACh
  66. .text:000007FF71CCA72F                 mul     ecx
  67. .text:000007FF71CCA731                 shr     edx, 0Dh
  68. .text:000007FF71CCA734                 mov     cs:dword_7FF71CEE3FC, edx
  69. .text:000007FF71CCA73A                 jmp     short $+2
  70. .text:000007FF71CCA73C ; ---------------------------------------------------------------------------
  71. .text:000007FF71CCA73C
  72. .text:000007FF71CCA73C loc_7FF71CCA73C:                        ; CODE XREF: timeBeginPeriod+F2↑j
  73. .text:000007FF71CCA73C                                         ; timeBeginPeriod+306C↓j ...
  74. .text:000007FF71CCA73C                 lea     rcx, ResolutionCritSec ; lpCriticalSection
  75. .text:000007FF71CCA743                 call    cs:__imp_LeaveCriticalSection
  76. .text:000007FF71CCA749                 mov     eax, ebx
  77. .text:000007FF71CCA74B
  78. .text:000007FF71CCA74B loc_7FF71CCA74B:                        ; CODE XREF: timeBeginPeriod+2FF3↓j
  79. .text:000007FF71CCA74B                                         ; timeBeginPeriod+2FFA↓j
  80. .text:000007FF71CCA74B                 mov     rbx, [rsp+38h+arg_8]
  81. .text:000007FF71CCA750                 mov     rbp, [rsp+38h+arg_10]
  82. .text:000007FF71CCA755                 add     rsp, 20h
  83. .text:000007FF71CCA759                 pop     r13
  84. .text:000007FF71CCA75B                 pop     r12
  85. .text:000007FF71CCA75D                 pop     rdi
  86. .text:000007FF71CCA75E                 retn
  87. .text:000007FF71CCA75E ; ---------------------------------------------------------------------------
  88. .text:000007FF71CCA75F                 db 9 dup(90h)
  89. .text:000007FF71CCA75F timeBeginPeriod endp
  90. .text:000007FF71CCA75F
  91. .text:000007FF71CCA768 ; Exported entry 138. timeEndPeriod
  92. .text:000007FF71CCA768
  93. .text:000007FF71CCA768 ; =============== S U B R O U T I N E =======================================
  94. .text:000007FF71CCA768
  95. .text:000007FF71CCA768
  96. .text:000007FF71CCA768 ; MMRESULT __stdcall timeEndPeriod(UINT uPeriod)
  97. .text:000007FF71CCA768                 public timeEndPeriod
  98. .text:000007FF71CCA768 timeEndPeriod   proc near               ; CODE XREF: timeKillEvent+4D↓p
  99. .text:000007FF71CCA768                                         ; timeSetEventInternal+2AA9↓p ...
  100. .text:000007FF71CCA768
  101. .text:000007FF71CCA768 arg_0           = dword ptr  8
  102. .text:000007FF71CCA768 ActualResolution= dword ptr  10h
  103. .text:000007FF71CCA768 arg_10          = qword ptr  18h
  104. .text:000007FF71CCA768
  105. .text:000007FF71CCA768 ; FUNCTION CHUNK AT .text:000007FF71CCD6C0 SIZE 000000CE BYTES
  106. .text:000007FF71CCA768
  107. .text:000007FF71CCA768                 mov     [rsp+arg_10], rbx
  108. .text:000007FF71CCA76D                 push    rbp
  109. .text:000007FF71CCA76E                 push    rsi
  110. .text:000007FF71CCA76F                 push    rdi
  111. .text:000007FF71CCA770                 sub     rsp, 20h
  112. .text:000007FF71CCA774                 mov     ebx, ecx
  113. .text:000007FF71CCA776                 cmp     ecx, cs:TDD_MAXRESOLUTION
  114. .text:000007FF71CCA77C                 jb      loc_7FF71CCD6C0
  115. .text:000007FF71CCA782                 cmp     ecx, cs:uPeriod
  116. .text:000007FF71CCA788                 jnb     loc_7FF71CCD6CA
  117. .text:000007FF71CCA78E                 lea     rcx, ResolutionCritSec ; lpCriticalSection
  118. .text:000007FF71CCA795                 call    cs:__imp_EnterCriticalSection
  119. .text:000007FF71CCA79B                 mov     r8d, cs:TDD_MAXRESOLUTION
  120. .text:000007FF71CCA7A2                 mov     r11d, ebx
  121. .text:000007FF71CCA7A5                 sub     r11d, r8d
  122. .text:000007FF71CCA7A8                 lea     r9, TimerData
  123. .text:000007FF71CCA7AF                 xor     edi, edi
  124. .text:000007FF71CCA7B1                 movzx   eax, word ptr [r9+r11*2+46h]
  125. .text:000007FF71CCA7B7                 cmp     ax, di
  126. .text:000007FF71CCA7BA                 jz      loc_7FF71CCD6D1
  127. .text:000007FF71CCA7C0                 mov     ebp, 1
  128. .text:000007FF71CCA7C5                 sub     ax, bp
  129. .text:000007FF71CCA7C8                 mov     [r9+r11*2+46h], ax
  130. .text:000007FF71CCA7CE                 jnz     short loc_7FF71CCA82B
  131. .text:000007FF71CCA7D0                 cmp     ebx, cs:dword_7FF71CEE3F8
  132. .text:000007FF71CCA7D6                 jnz     short loc_7FF71CCA82B
  133. .text:000007FF71CCA7D8                 mov     edx, cs:uPeriod
  134. .text:000007FF71CCA7DE                 jmp     short $+2
  135. .text:000007FF71CCA7E0 ; ---------------------------------------------------------------------------
  136. .text:000007FF71CCA7E0
  137. .text:000007FF71CCA7E0 loc_7FF71CCA7E0:                        ; CODE XREF: timeEndPeriod+76↑j
  138. .text:000007FF71CCA7E0                                         ; timeEndPeriod+8B↓j
  139. .text:000007FF71CCA7E0                 cmp     ebx, edx
  140. .text:000007FF71CCA7E2                 jnb     short loc_7FF71CCA7F5
  141. .text:000007FF71CCA7E4                 mov     eax, ebx
  142. .text:000007FF71CCA7E6                 sub     eax, r8d
  143. .text:000007FF71CCA7E9                 cmp     [r9+rax*2+46h], di
  144. .text:000007FF71CCA7EF                 jnz     short loc_7FF71CCA7F5
  145. .text:000007FF71CCA7F1                 add     ebx, ebp
  146. .text:000007FF71CCA7F3                 jmp     short loc_7FF71CCA7E0
  147. .text:000007FF71CCA7F5 ; ---------------------------------------------------------------------------
  148. .text:000007FF71CCA7F5
  149. .text:000007FF71CCA7F5 loc_7FF71CCA7F5:                        ; CODE XREF: timeEndPeriod+7A↑j
  150. .text:000007FF71CCA7F5                                         ; timeEndPeriod+87↑j
  151. .text:000007FF71CCA7F5                 mov     ecx, cs:dword_7FF71CEE3FC
  152. .text:000007FF71CCA7FB                 lea     r8, [rsp+38h+ActualResolution] ; ActualResolution
  153. .text:000007FF71CCA800                 xor     edx, edx        ; SetOrUnset
  154. .text:000007FF71CCA802                 imul    ecx, 2710h      ; RequestedResolution
  155. .text:000007FF71CCA808                 call    cs:__imp_NtSetTimerResolution
  156. .text:000007FF71CCA80E                 mov     r11d, cs:uPeriod
  157. .text:000007FF71CCA815                 mov     cs:dword_7FF71CEE3FC, r11d
  158. .text:000007FF71CCA81C                 mov     cs:dword_7FF71CEE3F8, ebx
  159. .text:000007FF71CCA822                 cmp     ebx, r11d
  160. .text:000007FF71CCA825                 jb      loc_7FF71CCD6DB
  161. .text:000007FF71CCA82B
  162. .text:000007FF71CCA82B loc_7FF71CCA82B:                        ; CODE XREF: timeEndPeriod+66↑j
  163. .text:000007FF71CCA82B                                         ; timeEndPeriod+6E↑j ...
  164. .text:000007FF71CCA82B                 lea     rcx, ResolutionCritSec ; lpCriticalSection
  165. .text:000007FF71CCA832                 call    cs:__imp_LeaveCriticalSection
  166. .text:000007FF71CCA838                 mov     eax, edi
  167. .text:000007FF71CCA83A
  168. .text:000007FF71CCA83A loc_7FF71CCA83A:                        ; CODE XREF: timeEndPeriod+2F5D↓j
  169. .text:000007FF71CCA83A                                         ; timeEndPeriod+2F64↓j
  170. .text:000007FF71CCA83A                 mov     rbx, [rsp+38h+arg_10]
  171. .text:000007FF71CCA83F                 add     rsp, 20h
  172. .text:000007FF71CCA843                 pop     rdi
  173. .text:000007FF71CCA844                 pop     rsi
  174. .text:000007FF71CCA845                 pop     rbp
  175. .text:000007FF71CCA846                 retn
  176. .text:000007FF71CCA846 ; ---------------------------------------------------------------------------
  177. .text:000007FF71CCA847                 align 10h
  178. .text:000007FF71CCA847 timeEndPeriod   endp
  179.  


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.
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.



(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

440bx

  • Hero Member
  • *****
  • Posts: 4014
Re: Windows Sleep precision
« Reply #3 on: July 21, 2022, 11:41:39 am »
Here is the modified TestSleep program
Code: Pascal  [Select][+][-]
  1. {$APPTYPE CONSOLE}
  2.  
  3. program TestSleep2;
  4.  
  5.   uses
  6.     Windows,
  7.     sysutils
  8.     ;
  9.  
  10.  
  11. type
  12.   NTSTATUS = DWORD;
  13.  
  14. const
  15.   ntdll = 'ntdll';
  16.  
  17.  
  18. function NtSetTimerResolution
  19.            (
  20.             { _in_  } InDesiredTime   : DWORD;
  21.             { _in_  } InSetResolution : boolean;
  22.             { _out_ } OutActualTime   : PDWORD
  23.            )
  24.          : NTSTATUS; stdcall; external ntdll;
  25.  
  26. function NtQueryTimerResolution
  27.            (
  28.             { _out_ } OutMinimumResolution : PDWORD;
  29.             { _out_ } OutMaximumResolution : PDWORD;
  30.             { _out_ } OutCurrentResolution : PDWORD
  31.            )
  32.          : NTSTATUS; stdcall; external ntdll;
  33.  
  34.  
  35. const
  36.   STATUS_OK = 0;
  37.  
  38. var
  39.   MaximumResolution, MinimumResolution, CurrentResolution : DWORD;
  40.  
  41.   Status { NTSTATUS }                                     : DWORD;
  42.  
  43.  
  44.   procedure SleepLoop(ms : DWORD);
  45.   const
  46.     COUNTER_LIMIT = 1000;
  47.  
  48.   var
  49.     counter : DWORD = 0;
  50.  
  51.   begin
  52.     writeln;
  53.     writeln('ms : ', ms, ' repeat count ', COUNTER_LIMIT);
  54.     for counter := 1 to COUNTER_LIMIT do
  55.     begin
  56.       Sleep(ms);
  57.     end;
  58.   end;
  59.  
  60. begin
  61.   writeln;
  62.  
  63.   writeln('  NtSetTimerResolution');
  64.   writeln;
  65.   writeln('  sets the timer resolution to maximum resolution possible');
  66.   writeln;
  67.  
  68.   MinimumResolution := 0;
  69.   MaximumResolution := 0;
  70.   CurrentResolution := 0;
  71.  
  72.   Status := NtQueryTimerResolution(@MinimumResolution,
  73.                                    @MaximumResolution,
  74.                                    @CurrentResolution);
  75.  
  76.   if Status <> STATUS_OK then
  77.   begin
  78.     writeln('  call to NtQueryTimerResolution failed. NTSTATUS: ',
  79.             IntToHex(Status, 0));
  80.     ExitProcess(1);
  81.   end;
  82.  
  83.   writeln;
  84.   writeln('  Minimum Resolution : ', MinimumResolution);
  85.   writeln('  Maximum Resolution : ', MaximumResolution);
  86.   writeln('  Current Resolution : ', CurrentResolution);
  87.  
  88.   { for good measure, reset the current resolution variable                 }
  89.  
  90.   CurrentResolution := 0;
  91.   Status := NtSetTimerResolution(MaximumResolution,
  92.                                  TRUE,
  93.                                 @CurrentResolution);
  94.  
  95.   if Status <> STATUS_OK then
  96.   begin
  97.     writeln('  call to NtSetTimerResolution failed. NTSTATUS: ',
  98.             IntToHex(Status, 0));
  99.     ExitProcess(2);
  100.   end;
  101.  
  102.   writeln;
  103.   writeln('  NtSetTimerResolution returned CurrentResolution : ',
  104.           CurrentResolution);
  105.   writeln;
  106.  
  107.   { redo the call to NtQueryTimerResolution                               }
  108.  
  109.   MinimumResolution := 0;
  110.   MaximumResolution := 0;
  111.   CurrentResolution := 0;
  112.  
  113.   Status := NtQueryTimerResolution(@MinimumResolution,
  114.                                    @MaximumResolution,
  115.                                    @CurrentResolution);
  116.  
  117.   writeln('  NtQueryTimerResolution now reports : ');
  118.  
  119.   writeln;
  120.   writeln('  Minimum Resolution : ', MinimumResolution);
  121.   writeln('  Maximum Resolution : ', MaximumResolution);
  122.   writeln('  Current Resolution : ', CurrentResolution);
  123.  
  124.  
  125.   SleepLoop(1);  { use values 1, 8 and 16 }
  126. 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.

(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

rvk

  • Hero Member
  • *****
  • Posts: 6162
Re: Windows Sleep precision
« Reply #4 on: July 21, 2022, 11:47:36 am »
Have you tried to put a
Code: Pascal  [Select][+][-]
  1. 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  :D (unlike the NtSetTimerResolution).

Code: Pascal  [Select][+][-]
  1. Function timeBeginPeriod(x1: UINT): MMRESULT;stdcall; external 'winmm.dll' name 'timeBeginPeriod';

440bx

  • Hero Member
  • *****
  • Posts: 4014
Re: Windows Sleep precision
« Reply #5 on: July 21, 2022, 12:03:16 pm »
Well, that code/assembler doesn't come from FPC itself.
You're right about that, that (dis) assembly code is from IDA Pro.


So at least that one is documented  :D (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.
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

ASerge

  • Hero Member
  • *****
  • Posts: 2240
Re: Windows Sleep precision
« Reply #6 on: July 21, 2022, 08:32:22 pm »
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.
From the timeBeginPeriod document
Quote
Setting 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

  • Hero Member
  • *****
  • Posts: 4014
Re: Windows Sleep precision
« Reply #7 on: July 21, 2022, 08:42:27 pm »
Quote
Setting 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.

(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

 

TinyPortal © 2005-2018