FreeBasic's Timer
Re: FreeBasic's Timer
No.
If 't1' is not modified, then at the next loop, 't2 < t1' will still be checked and therefore 't3' will still be decremented by one day and so on !
If 't1' is not modified, then at the next loop, 't2 < t1' will still be checked and therefore 't3' will still be decremented by one day and so on !
-
- Posts: 4310
- Joined: Jan 02, 2017 0:34
- Location: UK
- Contact:
Re: FreeBasic's Timer
Agreed.
-
- Posts: 4310
- Joined: Jan 02, 2017 0:34
- Location: UK
- Contact:
Re: FreeBasic's Timer
I reckoned that fxm spent sometime before settling on 'If t > 300' and did not question it. However, I decided to do just that.
With t <= 300 we don't invoke Sleep and the onus of the delay is solely with the loop; which could mean 'looking for' up to 300ms.
With t = 500, for example, we request a Sleep for 200ms (500 - 300). Because of Sleep's resolution, we actually sleep for Int(200/15.625 + 1) * 15.625 = 203.125. The loop now has the job of 'finding' 296.875ms (500 - 203.125).
With t = 1000 the loop, again, has the job of 'finding' 296.875ms.
Polling Timer is CPU intensive, so for t > 300 one of the CPU's cores will be going flat out for nearly 300ms which is not good for the system.
We need to reduce the amount of work done in the loop.
I looked at 16, instead of 300, but this was too close to Sleep's resolution of 15.625ms and some 'rogue' values appeared in the delay. This was easily resolved by using 32.
We now have the loop only having to 'find' no more than 32ms.
Going flat out for 32ms is a lot better than going flat out for 300ms.
We won't be doing this, but is a worst-case scenario.
The first test used 'If t > 300' and the second test used 'If t > 32'.
Here are a couple of snapshots of Task Manager.
As you can see, the system has a definite preference for 32.
With t <= 300 we don't invoke Sleep and the onus of the delay is solely with the loop; which could mean 'looking for' up to 300ms.
With t = 500, for example, we request a Sleep for 200ms (500 - 300). Because of Sleep's resolution, we actually sleep for Int(200/15.625 + 1) * 15.625 = 203.125. The loop now has the job of 'finding' 296.875ms (500 - 203.125).
With t = 1000 the loop, again, has the job of 'finding' 296.875ms.
Polling Timer is CPU intensive, so for t > 300 one of the CPU's cores will be going flat out for nearly 300ms which is not good for the system.
We need to reduce the amount of work done in the loop.
I looked at 16, instead of 300, but this was too close to Sleep's resolution of 15.625ms and some 'rogue' values appeared in the delay. This was easily resolved by using 32.
We now have the loop only having to 'find' no more than 32ms.
Going flat out for 32ms is a lot better than going flat out for 300ms.
We won't be doing this, but is a worst-case scenario.
Code: Select all
For i As Ulong = 1 to 20
delay(1000)
Next
Here are a couple of snapshots of Task Manager.
As you can see, the system has a definite preference for 32.
Re: FreeBasic's Timer
I agree that the 32ms threshold seems like a good compromise between accuracy and CPU load.
In the case of my 2 procedures 'delay()' and 'regulate()':
- 'delay()' for me has rather a use of a one shoot from time to time, but with a good accuracy, so well that I did not spare the CPU load too much with the threshold of 300ms (I could have as well take 100ms).
- 'regulate()' on the contrary is rather adapted to insert a delay in a loop to often adjust its FPS, and there with the threshold of 20ms, I rather wanted to favor a little the limitation of the CPU load compared to the accuracy.
In conclusion, I agree to use the same single threshold of 32ms (trade-off between accuracy and CPU load) for both procedures.
All my previous posts have been updated accordingly.
In the case of my 2 procedures 'delay()' and 'regulate()':
- 'delay()' for me has rather a use of a one shoot from time to time, but with a good accuracy, so well that I did not spare the CPU load too much with the threshold of 300ms (I could have as well take 100ms).
- 'regulate()' on the contrary is rather adapted to insert a delay in a loop to often adjust its FPS, and there with the threshold of 20ms, I rather wanted to favor a little the limitation of the CPU load compared to the accuracy.
In conclusion, I agree to use the same single threshold of 32ms (trade-off between accuracy and CPU load) for both procedures.
All my previous posts have been updated accordingly.
-
- Posts: 4310
- Joined: Jan 02, 2017 0:34
- Location: UK
- Contact:
Re: FreeBasic's Timer
With regard delay() there is no compromise between accuracy and CPU load.
With a delay of 1000ms, for example, previously we had, roughly, 700ms Sleep and 300ms loop. With the 32ms threshold we now have, roughly, 968ms Sleep and 32ms loop.
There is a dramatic reduction in CPU load but no change in accuracy.
With a delay of 1000ms, for example, previously we had, roughly, 700ms Sleep and 300ms loop. With the 32ms threshold we now have, roughly, 968ms Sleep and 32ms loop.
There is a dramatic reduction in CPU load but no change in accuracy.
Re: FreeBasic's Timer
You are right, I do not find any noticeable degradation in accuracy between a threshold at 300 ms and the threshold at 32 ms, but on the other hand I observe a clear degradation between the threshold at 32 ms and a threshold at 20 ms.
-
- Posts: 4310
- Joined: Jan 02, 2017 0:34
- Location: UK
- Contact:
Re: FreeBasic's Timer
Yes, doubling Sleep's 'out of the box' resolution keeps us out of trouble. Playing it safe, so to speak.fxm wrote:but on the other hand I observe a clear degradation between the threshold at 32 ms and a threshold at 20 ms.
And there is more.
With my VeryHiResSleep( n ) macro I had an issue with Sleep's association with the system clock's frequency of 64Hz, so I associated it with a multimedia timer with a frequency of 1000Hz. That helped.
Linked to a 1ms resolution timer, a request for 1ms will give a Sleep of between 1ms and 2ms. Again, playing it safe, I chose 4ms.
We can now use:
Code: Select all
If t > 4 Then Sleep t - 4, 1
Here is a Task Manager snapshot using
Code: Select all
For i As Ulong = 1 to 20
delay(1000)
Next
That spike is the exe loading, as always, but the CPU load has fallen to the pre-execution level. The CPU load is now negligible.
With a delay of 1000ms, for example, a 4ms threshold gives, roughly, a 996ms Sleep and a 4ms loop. The system will not bat an eyelid with 4ms of intense activity.
OK, so how do we get Sleep to use a 1ms timer?
At the head of our code we simply add:
Code: Select all
Declare Function _setTimer Lib "winmm" Alias "timeBeginPeriod"(As Ulong=1) As Long
_setTimer
I don't use _setTimer inside of delay() as that would affect the accuracy of the delay – albeit only slightly.
For Windows 10 and later, _setTimer no longer affects a global Windows setting. For more details, see timeBeginPeriod function
I have given the new delay() a hammering using fxm's test code, and the new threshold of 4ms appears to be robust. I did not expect an issue because 4ms really is playing it safe.
New delay()
Code: Select all
Declare Function _setTimer Lib "winmm" Alias "timeBeginPeriod"(As Ulong=1) As Long
_setTimer
Sub delay(Byval t As Single) '' delay 't' expressed in milliseconds
Dim As Double t1 = Timer
Dim As Double t2
Dim As Double t3 = t1 + t / 1000
If t > 4 Then Sleep t - 4, 1
Do
#ifndef __FB_WIN32__
t2 = Timer
If t2 < t1 Then t1 -= 24 * 60 * 60 : t3 -= 24 * 60 * 60
Loop Until t2 >= t3
#else
Loop Until Timer >= t3
#endif
End Sub
So with the above, fxm's delay() is blindingly accurate with an acceptable delay down to 0.1ms with a CPU load which is, quite frankly, not worth mentioning.
-
- Posts: 4310
- Joined: Jan 02, 2017 0:34
- Location: UK
- Contact:
Re: FreeBasic's Timer
On May 22, 2023 15:43 fxm published 'Example of use ('framecounter()' from dodicat):'
I executed that reducing the fps to 60 and used the given:
I then added:
and edited:
On my desktop I have several metrics displayed, with one as 'CPU Usage'.
This is what I got:
The top usage uses a 32ms threshold and the bottom a 4ms threshold.
Task Manager refers to the 32ms threshold as 'Very high' power usage. 15.8% is expensive. That is in line with my system backup application, which completes in just over four minutes.
The 4ms threshold varied from low to moderate power usage, said Task Manager. 4.1% is not exactly cheap, but it is not bad.
So associating Sleep with a 1000Hz timer and using a threshold of 4ms dramatically reduces the CPU usage.
Added: The 1ms association is cancelled by Windows when the application closes. MSDN does not mention that.
I executed that reducing the fps to 60 and used the given:
Code: Select all
If dt >= 32 Then Sleep dt - 32, 1
Code: Select all
Declare Function _setTimer Lib "winmm" Alias "timeBeginPeriod"(As Ulong=1) As Long
_setTimer
Code: Select all
If dt >= 4 Then Sleep dt - 4, 1
This is what I got:
The top usage uses a 32ms threshold and the bottom a 4ms threshold.
Task Manager refers to the 32ms threshold as 'Very high' power usage. 15.8% is expensive. That is in line with my system backup application, which completes in just over four minutes.
The 4ms threshold varied from low to moderate power usage, said Task Manager. 4.1% is not exactly cheap, but it is not bad.
So associating Sleep with a 1000Hz timer and using a threshold of 4ms dramatically reduces the CPU usage.
Added: The 1ms association is cancelled by Windows when the application closes. MSDN does not mention that.
Re: FreeBasic's Timer
The date/time depends on the country, so it is more efficient to provide a link to the post.deltarho[1859] wrote: ↑May 25, 2023 3:30 On May 22, 2023 15:43 fxm published 'Example of use ('framecounter()' from dodicat):'
Otherwise, I find proportional results (23% and 6%).
-
- Posts: 4310
- Joined: Jan 02, 2017 0:34
- Location: UK
- Contact:
Re: FreeBasic's Timer
We live and learn.fxm wrote:The date/time depends on the country, so it is more efficient to provide a link to the post.
23% Flaming heck – your CPU is being throttled.
6% is still high. I looked at a threshold of 2ms and that worked, but I think we are pushing our luck with that.
I am fairly confident that 3ms should be OK and that may help with your machine.
Re: FreeBasic's Timer
For debugging purposes, it would be interesting if 'regulate()' could return the delay applied (the one added in the initial loop).
For this, 'regulate()' could be defined as a function (which we could also call as a Sub if we do not want to use this information).
Our previous example with 60 FPS and 3 ms as threshold (for Windows):
For this, 'regulate()' could be defined as a function (which we could also call as a Sub if we do not want to use this information).
Our previous example with 60 FPS and 3 ms as threshold (for Windows):
Code: Select all
Declare Function _setTimer Lib "winmm" Alias "timeBeginPeriod"(Byval As Ulong = 1) As Long
_setTimer()
Function regulate(Byval MyFps As Long) As Double '' return delay applied in milliseconds
Static As Double timervalue
Dim As Single tt = 1 / MyFps
Dim As Double tf = timervalue + tt
Dim As Double t = Timer
If t < timervalue Then timervalue -= 24 * 60 * 60 : tf -= 24 * 60 * 60
Dim As Single dt = (tt - (t - timervalue)) * 1000
If dt >= 3 Then Sleep dt - 3, 1
Do
t = Timer
If t < timervalue Then timervalue -= 24 * 60 * 60 : tf -= 24 * 60 * 60
Loop Until t >= tf
timerValue = Timer
Return dt
End Function
Function framecounter() As Ulong
Dim As Double t2 = Timer
' Static As Ulongint t3, frames, answer
Static As Double t3
Static As Ulongint frames, answer
frames += 1
If (t2 - t3) >= 1 Then
t3 = t2
answer = frames
frames = 0
End If
Return answer
End Function
Screen 12
Dim As Ulong MyFps = 60
Do
Static As Ulongint l
Static As Single dt
Screenlock
Cls
Color 11
Print "Requested FPS : "; MyFps
Print "Measured FPS : "; framecounter()
Print "Applied delay :"; dt; " ms"
Print
Print
Print
Color 14
Print "<+> : Increase FPS"
Print "<-> : Decrease FPS"
Print "<Escape> : Quit"
Line (0, 64)-(l, 80), 15, BF
Screenunlock
l = (l + 1) Mod 640
Dim As String s = Inkey
Select Case s
Case "+"
If MyFps < 1000 Then MyFps += 1
Case "-"
If MyFps > 10 Then MyFps -= 1
Case Chr(27)
Exit Do
End Select
dt = regulate(MyFps)
Loop
Last edited by fxm on May 26, 2023 12:16, edited 1 time in total.
Reason: Corrected 'framecounter()' procedure.
Reason: Corrected 'framecounter()' procedure.
-
- Posts: 4310
- Joined: Jan 02, 2017 0:34
- Location: UK
- Contact:
Re: FreeBasic's Timer
Nice.
3ms is a walk in the park on my machine.
Just for the record, here are the Windows APIs affected by TimeBeginPeriod according to a PowerBASIC post of mine in 2014. Yep, I have been using it for a few years now.
Sleep
SleepEx
timeGetTime
timeGetSystemTime
timeSetEvent
All of which have a resolution of 15.625ms without TimeBeginPeriod.
The system revoking TimeBeginPeriod with a process termination is from a comment by Mark Russinovich of SysInternals fame; noted in the PowerBASIC post mentioned above.
3ms is a walk in the park on my machine.
Just for the record, here are the Windows APIs affected by TimeBeginPeriod according to a PowerBASIC post of mine in 2014. Yep, I have been using it for a few years now.
Sleep
SleepEx
timeGetTime
timeGetSystemTime
timeSetEvent
All of which have a resolution of 15.625ms without TimeBeginPeriod.
The system revoking TimeBeginPeriod with a process termination is from a comment by Mark Russinovich of SysInternals fame; noted in the PowerBASIC post mentioned above.
-
- Posts: 4310
- Joined: Jan 02, 2017 0:34
- Location: UK
- Contact:
Re: FreeBasic's Timer
32ms System clock versus 3ms 1000Hz timer.
The top image is, obviously, 32ms looking like WWIII and the bottom image, 3ms, looks like my machine is on tranquillizers.
With the top image, Windows is doing its best to stop the third core being hammered, but we are talking about a single thread of execution, so it cannot do much. With the bottom image, Windows has its feet up enjoying a cup of tea saying: Thanks boss – much better than that horrible PractRand you were running the other week which makes my liquid cooler sweat."
I know – I can stop the cursor being captured, but I keep forgetting. (Senile decay)
Added: Task Manager is reporting the 3ms 1000Hz timer 'Power usage' as being low.
The top image is, obviously, 32ms looking like WWIII and the bottom image, 3ms, looks like my machine is on tranquillizers.
With the top image, Windows is doing its best to stop the third core being hammered, but we are talking about a single thread of execution, so it cannot do much. With the bottom image, Windows has its feet up enjoying a cup of tea saying: Thanks boss – much better than that horrible PractRand you were running the other week which makes my liquid cooler sweat."
I know – I can stop the cursor being captured, but I keep forgetting. (Senile decay)
Added: Task Manager is reporting the 3ms 1000Hz timer 'Power usage' as being low.
Re: FreeBasic's Timer
About the 'framecounter()' procedure:
An instantaneous measurement (by measuring the frame time) could be provided through a simpler 'framerate()' procedure as follows:
but this last procedure uses a division which would add an extra delay in the loop.
But now, about the 'framecounter()' procedure, I am surprised that nobody reacted to it on the use of a 'Ulongint' variable (careless mistake) to memorize the Timer value ('t3') ?
Depending on the requested FPS value, the measurement is either very good or completely wrong for certain values.
Change its type to 'Double' and you will see the difference on the measured FPS value ! (for example on code at: viewtopic.php?p=298857#p298857), but the measurement is now a little less stable.
To have a reliable measurement, one must use a corrected 'framecouner()' procedure or the 'framerate()' procedure.
Code: Select all
Function framecounter() As Ulong Dim As Double t2 = Timer Static As Ulongint t3, frames, answer frames += 1 If (t2 - t3) >= 1 Then t3 = t2 answer = frames frames = 0 End If Return answer End Function
An instantaneous measurement (by measuring the frame time) could be provided through a simpler 'framerate()' procedure as follows:
Code: Select all
Function framerate() As Ulong
Dim As Double t2 = Timer
Static As Double t1
Dim As Ulong dt = 1 / (t2 - t1)
t1 = t2
Return dt
End Function
But now, about the 'framecounter()' procedure, I am surprised that nobody reacted to it on the use of a 'Ulongint' variable (careless mistake) to memorize the Timer value ('t3') ?
Depending on the requested FPS value, the measurement is either very good or completely wrong for certain values.
Change its type to 'Double' and you will see the difference on the measured FPS value ! (for example on code at: viewtopic.php?p=298857#p298857), but the measurement is now a little less stable.
To have a reliable measurement, one must use a corrected 'framecouner()' procedure or the 'framerate()' procedure.
-
- Posts: 4310
- Joined: Jan 02, 2017 0:34
- Location: UK
- Contact:
Re: FreeBasic's Timer
That is true and would have an effect on the 'Applied delay' but 'framerate()' is a simpler procedure so gets some compensation for that. On testing framecounter() versus framerate() the 'Applied delay' differs only in the microsecond range, so the effect on the loop is negligible. The 'Measured FPS' is now immediate.fxm wrote:but this last procedure uses a division which would add an extra delay in the loop.
Added: Of course, if we use regulate() as a Sub we wouldn't use framerate() anyway.
------------------------------------------
We should not categorize nations as having a particular personality trait as we are all individuals, but there appears to be some merit in doing just that. So, it is with peer support forums. It was no surprise to me that nobody reacted to your 'careless mistake'. This thread is like many others where they eventually become a discussion between two members. This can happen at PowerBASIC, but there we usually have eight or nine members participating until the thread's conclusion.I am surprised that nobody reacted to it on the use of a 'Ulongint' variable (careless mistake) to memorize the Timer value ('t3')
When I came here in 2017, PowerBASIC had about 250 active members – it now has, today, 139. However, those 139 are very active. Here the active membership, it seems to me, is very much less and there appears to be a reluctance for many members to participate. Why? I have no idea.
One day, I will write a book entitled "On the psychology of peer support forums".
I should add that I missed the UlongInt 'mistake' as I only gave framecounter() a cursory glance; concentrating on the CPU Load.
It looks like my 4ms threshold was playing it too safe. On further analysis, the 3ms threshold should never be an issue.
Added: I hadn't thought about this, but the 3ms threshold relies upon a 1000Hz timer – two thirds of the forum?
I should point out that before Windows 10 'timeBeginPeriod' was a global setting, and we should think about using 'timeEndPeriod' immediately after we are finished using the timer services rather than wait for the process to close. To that end, we need 'Declare Function _freeTimer Lib "winmm" Alias "timeEndPeriod"(As Ulong=1) As Long'.
-----------------------------------------
Requests
For me, your delay() and regulate() procedures are now the definitive procedures for delaying and regulating.
At some point, this thread will disappear into the ether. That should not be the fate of delay() and regulate() so I propose that their code be put into the manual. Since you are the manual's editor-in-chief, I will leave it up to you as to where they should be placed.
Now how about this as a radical suggestion. I firmly believe that your two procedures are so powerful they should be included as keywords in the fbc; delay( amount ) and regulate( amount ). The onus now is on coderJeff.
Added: We are, of course, only talking about Windows. I have no idea if Linux, or anybody else for that matter, has a 1000Hz timer, and they will not have time<whatever>Period. coderJeff may baulk at that, and I would not disagree.