09-01-2025, 10:33 PM
(This post was last modified: 09-02-2025, 01:04 PM by David Roberts.
Edit Reason: n to n+1 rather than 1 to 2
)
SleepX()
SleepX() is a high resolution replacement for Sleep.
The principle used has an analogy with HiFi were a class B amplifier is used to get us within a neighbourhood of a desired voltage and then uses a class A amplifier to fine tune. The cost of this approach is much less than using only a class A amplifier, which are expensive.
The class B amplifier analogy uses Sleep and the class A amplifier analogy uses the Performance Counter, which has a resolution of 100ns with Windows 10 and later.
The following code has the SleepX() code and a usage example.
This is a typical output.
The first example looks at a delay of a quarter of a millisecond. I doubt that anyone will have a use for that. The second example looks at a delay of two seconds. The third example looks at a 'silly' delay of 1234ms, The following looks at delays from 1ms to 30ms in steps of 1ms.
All the results have a sub micro accuracy inline with the Performance Counter on Windows 10 and later.
SleepX() has two parts: The first part is used for delays <= to 3ms and only polls the Performance Counter. This is expensive but is a short-lived expense and should not impact on the system performance. The second part uses the construct 'Sleep ( n-3 )' to get us within a neighbourhood of the target delay, and then we poll the Performance Counter to fine tune. 'Sleep ( n-3 )' needs a resolution of 1ms and why we use SetHiRes.
'Sleep ( n-3 )' does not use any CPU load. The CPU load only kicks in when we enter the 'class A' mode, so is an absolute value and should not impact on the system performance.
Why was 'n <= 3'. It is reasonable to expect a 1ms resolution to give a delay of between n ms and n+1 ms. In practice, we can exceed n+1 ms and about one third of delays do just that. It is very rare, but values approaching n+1.5 ms have been seen. To mitigate that issue, 'n <= 3' was chosen.
That is it: A very simple idea to give a high resolution replacement for Sleep with a negligible CPU load.
It is worth noting that
is acceptable, but it should not be used within a loop; for example a graphics application with 60, or so, fps. That will upset the system clock. It is better to use SetHiRes at the beginning of an application and RevokeHiRes when the higher resolution is no longer needed. If you forget to use RevokeHiRes SetHiRes will be cancelled when the process terminates. Microsoft neglects to mention that.
SleepX() is a high resolution replacement for Sleep.
The principle used has an analogy with HiFi were a class B amplifier is used to get us within a neighbourhood of a desired voltage and then uses a class A amplifier to fine tune. The cost of this approach is much less than using only a class A amplifier, which are expensive.
The class B amplifier analogy uses Sleep and the class A amplifier analogy uses the Performance Counter, which has a resolution of 100ns with Windows 10 and later.
The following code has the SleepX() code and a usage example.
This is a typical output.
Code:
Quarter of a millisecond: .2503 ms
2 seconds: 2.0000003 s
Silly: 1234.0003 ms
1 1.0001 ms
2 2.0001 ms
3 3.0002 ms
4 4.0001 ms
5 5.0002 ms
6 6.0002 ms
7 7.0002 ms
8 8.0001 ms
9 9.0001 ms
10 10 ms
11 11.0002 ms
12 12.0002 ms
13 13.0001 ms
14 14.0001 ms
15 15.0001 ms
16 16.0002 ms
17 17.0002 ms
18 18.0002 ms
19 19.0002 ms
20 20.0001 ms
21 21.0003 ms
22 22.0002 ms
23 23.0001 ms
24 24.0001 ms
25 25 ms
26 26.0002 ms
27 27.0002 ms
28 28.0002 ms
29 29.0002 ms
30 30.0002 ms
The first example looks at a delay of a quarter of a millisecond. I doubt that anyone will have a use for that. The second example looks at a delay of two seconds. The third example looks at a 'silly' delay of 1234ms, The following looks at delays from 1ms to 30ms in steps of 1ms.
All the results have a sub micro accuracy inline with the Performance Counter on Windows 10 and later.
SleepX() has two parts: The first part is used for delays <= to 3ms and only polls the Performance Counter. This is expensive but is a short-lived expense and should not impact on the system performance. The second part uses the construct 'Sleep ( n-3 )' to get us within a neighbourhood of the target delay, and then we poll the Performance Counter to fine tune. 'Sleep ( n-3 )' needs a resolution of 1ms and why we use SetHiRes.
'Sleep ( n-3 )' does not use any CPU load. The CPU load only kicks in when we enter the 'class A' mode, so is an absolute value and should not impact on the system performance.
Why was 'n <= 3'. It is reasonable to expect a 1ms resolution to give a delay of between n ms and n+1 ms. In practice, we can exceed n+1 ms and about one third of delays do just that. It is very rare, but values approaching n+1.5 ms have been seen. To mitigate that issue, 'n <= 3' was chosen.
That is it: A very simple idea to give a high resolution replacement for Sleep with a negligible CPU load.
It is worth noting that
Code:
SetHiRes
<Code to time>
RevokeHiRes
Code:
#COMPILE EXE
#DIM ALL
#INCLUDE "win32api.inc"
' SOURCE CODE:
Macro QPC = QueryPerformanceCounter qTimeNow
Macro SetHiRes
MacroTemp Time
Dim Time As TIMECAPS
TimeGetDevCaps( Time, SizeOf(Time) )
TimeBeginPeriod(Time.wPeriodMin)
'Sleep 16 ' Pre Windows 10 - high resolution does not 'byte' until next clock tick.
End Macro
Macro RevokeHiRes
MacroTemp Time
Dim Time As TIMECAPS
TimeGetDevCaps( Time, SizeOf(Time) )
TimeEndPeriod(Time.wPeriodMin)
End Macro
Global qFreq As Quad
Sub SleepX( ByVal n As Double )
Local qTarget, qTimeNow As Quad
QPC
qTarget = qTimeNow + n*qFreq*0.001
If n <= 3 then
Do : QPC : Loop Until qTimeNow >= qTarget ' Class A amplifier analogy
Else
' Class B amplifier analogy followed by Classs A amplifier analogy
Sleep ( n-3 ) : Do : QPC : Loop Until qTimeNow >= qTarget
End If
End Sub
' ====================
' EXAMPLE USAGE:
Function Pbmain () As Long
Local i as Long
Local qStart, qStop As Quad
QueryPerformanceFrequency qFreq
SetHiRes
QueryPerformanceCounter qStart
SleepX(0.25)
QueryPerformanceCounter qStop
Print "Quarter of a millisecond:";(qStop - qStart)*1000/qFreq;"ms"
Print
QueryPerformanceCounter qStart
SleepX(2000)
QueryPerformanceCounter qStop
Print "2 seconds:";(qStop - qStart)/qFreq;"s"
Print
QueryPerformanceCounter qStart
SleepX(1234)
QueryPerformanceCounter qStop
Print "Silly:";(qStop - qStart)*1000/qFreq;"ms"
Print
For i = 1 to 30
QueryPerformanceCounter qStart
SleepX(i)
QueryPerformanceCounter qStop
Print i;" ";(qStop - qStart)*1000/qFreq;"ms"
Next
RevokeHiRes
WaitKey$
End Function