Here is another macro to add to the above although it doesn't time an interval but creates one.
A simple analogy of the method employed is to consider amplification in a Hi-Fi system. We could have a powerful Class B amplifier which doesn't sound too good but didn't cost much. On the other hand, we could have a powerful Class A amplifier which sounds great but both our bank manager and partner would like a word with us at our earliest opportunity.
A hybrid, however, would use the Class B amplifier to get us within a neighbourhood of our desired sound and then use a very much less powerful Class A amplifier to fine tune resulting in a sound close to the more powerful Class A amplifier at a very much smaller combined cost.
With a millisecond timer linked to Sleep if we think only of resolution, neglecting access time, then a request for a five millisecond Sleep would see a delay of, roughly, five to six milliseconds. In general, a request for n milliseconds would see a delay of, roughly, n to n+1 milliseconds. A request for n-1 milliseconds would see a delay of, roughly, n-1 to n milliseconds.
The first part of the method does just that: It requests a Sleep of n-1 milliseconds. This is our Class B amplifier.
We then enter a loop polling QueryPerformanceCounter until we reach n milliseconds. This is our Class A amplifier.
In practice the time in the loop will vary between, roughly, no time at all or one millisecond for any value of n and we will never know at what point the cross-over occurred but that doesn't concern us - all that we are interested in is hitting n milliseconds in good time. Whilst in the loop we will have CPU activity but since we are in there for only one millisecond at most then I cannot see this being an issue.
If we do multiple tests it is bad practice to have them done 'back to back' - it is better to insert a 'breathing space' between tests and, preferably a random one.
Here is the macro.
Code: Select all
#Macro VeryHiResSleep( n ) ' n >= 1
Scope
Dim As ULongInt Target, Now
QueryPerformanceCounter Cast( Large_Integer Ptr, @Now )
Target = Now + n*liFreq/1000
Sleep ( n-1, 1 ) ' Class B amplifier
Do
QueryPerformanceCounter Cast( Large_Integer Ptr, @Now ) ' Class A amplifier
Loop Until Now >= Target
End Scope
#EndMacro
We need Sleep linked to a one millisecond timer and this can be done easily via the StartHiResClock macro in the opening post. This should be executed well before employing VeryHiResSleep() as it incurs a delay itself before the change in resolution occurs. The best place would be at the beginning of a console application or in WM_InitDialog.
Here is the test code using the macro timers in the opening post.
Code: Select all
StartHiResClock
'...
'...
'...
Dim as Long n = 10
For i = 1 To 20
StartTimer(0)
VeryHiResSleep( n )
StopTimer(0)
Print sTimetaken(0,3,0);
If i mod 5 = 0 THEN Print
Sleep( Rnd*100+50,1) ' Have a random breather
Next
This is what I get:
Code: Select all
10.003ms 10.008ms 10.036ms 10.008ms 10.002ms
10.002ms 10.003ms 10.073ms 10.052ms 10.003ms
10.021ms 10.005ms 10.003ms 10.003ms 10.002ms
10.003ms 10.002ms 10.002ms 10.04ms 10.004ms
Requesting 10 milliseconds from just Sleep linked to a millisecond timer would see a worst case overshoot of up to, roughly, one millisecond or, to put that another way, 1000 microseconds. The worst case overshoot in the above table is 73 microseconds.
So, it would seem that VeryHiResSleep() is putting us in a very different place.
The worst case overshoot, for some reason, reduces for higher delay requests and are quite remarkable for a delay of 100 milliseconds although it is debatable whether we should be using VeryHiResSleep() for such a 'large' delay.
We should expect a delay of two milliseconds, for example, to give very volatile results but this is not the case. Here is a typical two millisecond delay output.
Code: Select all
2.002ms 2.023ms 2.008ms 2.003ms 2.008ms
2.002ms 2.003ms 2.002ms 2.003ms 2.007ms
2.024ms 2.002ms 2.002ms 2.002ms 2.003ms
2.002ms 2.003ms 2.015ms 2.005ms 2.002ms
It is worth noting that Sleep(0,1) behaves as Microsoft's Sleep at MSDN so with a one millisecond request of VeryHiResSleep() we find ourselves in the performance counter loop pretty quickly. In this case we simply get a millisecond burst of CPU activity.
For me this post is of only academic interest as I have no use for it, <smile>, a standard Sleep linked to a millisecond timer does me just fine. Whilst mentioning that I still find many folk at different forums requesting 5 or 10 milliseconds from their compiler's Sleep ticking over at the Windows default of 64Hz ie a period of 15.625ms. That regime has no idea what 5 or 10 milliseconds is and they could end up with 15 or 16 milliseconds.
Have fun.