Delay(0)

Share your advanced PureBasic knowledge/code with the community.
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Delay(0)

Post by Rescator »

Code updated For 5.20+

Many uses Delay(1) to avoid hugging the cpu!
This is very nice and ok with most utility programs!

But how about more heavy programs, or even various games.
Or games in windowed mode!

Using Delay(1) would cause a delay of 1 (if you lucky) to 20+ microseconds.
Using no delay at all would hog the cpu, and trying to start another program
or doing anything at all would be extreemly sluggish.

So how do you get a very fast looping program that doesn't,
cause the entire multitasking of windows to choke?

The answer is (on Windows at least) to use Delay(0)

Which Delay() actualy uses the windows API Sleep() function.
The Win API documentation states:
A value of zero causes the thread to relinquish the remainder of its time slice to any other thread of equal priority that is ready to run.
If there are no other threads of equal priority ready to run, the function returns immediately, and the thread continues execution.
So if you use Delay(0) instead of the more common Delay(1)
to avoid eating up CPU,
you will notice cpu usage go from ~1% cpu load to whatever
the idle cpu of the system is.
In other words. if you have 95% free cpu and you start the program
and it uses Delay(0) instead of 1. it will use 95% cpu.
The advantage should be obvious.

A. Windows itself (which should have one of the highest priorities) will not become sluggish!
B. Starting of quitting programs may be a bit slower, but not annoying!
C. You avoid hogging the cpu, yet able to use a optimal loop!
D. Changing the program priority will actually be even more usefull than before!

Below is the source for a test program of mine.
Run it and note down the number displayed when the test is done!
Change the Delay(0) on line 56 to a Delay(1)
and watch the programs performance drop!
(Warning! You could try commenting out the Delay(0) but be aware
your system will be sluggish as hell for 60 seconds).

With the delay set at Delay(0) my system (Athlon 1.85Ghz)
shows a rating of: 1817

This test program simply meassures the number of loops that occur
over the time of 60 seconds.
But calculates the result from the microseconds for a more accurate and sensible result.
My rating above of 1817 means that 1817 loops occured on average each 1 microsecond.
That means 1817000 loops per second, and 109020000 loops during 60 seconds.

So remember, 1817 in this case is how many loops in one microsecond.
If you change the delay to 1 you won't even get 1 loop,
more likely to get 0.1 or less, as a Delay(1) can give a delay of 1ms to 20+ ms depending on your system.
Delay(0) does not delay at all, it simply enforces a task switch.
(or in other words "multitasking")

The higher the rating the faster your system is able to multitask,
so this is your chance to brag. (we all love benchmark programs right? hehe)

But seriously, this example (yet usefull) program shows how to use Delay(0)
so you can get maximum loopability/response in your program,
while avoding hogging the system.
Essentially what happens is that with Delay(0) on Windows
is that your program will use whatever cpu the "system" gives it,
versus no delay where it would even steal from Windows itself.

Obviously as you start throwing more program code into the loop
and other stuff and procedures, the number of loops will be less,
yet way more loops/response than if you had used a delay of 1ms.

Have fun with the code peeps! It's freeware, no commercial use permitted,
however feel free to borrow the code and use it in your own programs.

I've begun using a Delay(1) on all non-critical programs I make.
(simple tools, or programs that usually wait purely for user input)
and Delay(0) on programs that are autonomous, or in other words
do things on their own. like showing graphics on the screen etc.

Imagine this:
Using Delay(0) on a system similar to mine you are able to do over 1800 loops per microsecond.
That is over 1.8 million loops per second. (multitasking loops)
That means, while the graphics update say, 85 times per second. (85Hz)
the game itself would be able to do over a million calculations per second,
without making the system sluggish.

If you were to use no loop, you would be able to do way more loops.
But the system would be sluggish or non-responsive.
And if you were to use a delay of 1 ms then you could end up with as much as 20+ ms delay.
and 1000ms (1 sec) divided by 85 (Hz) is 11,764 ms (per Hz).
Meaning, there is no way you could have 85 loops/updates per sec to match the refresh rate of a system (or the graphics).
At 85Hz you actually need 85000 "loops" to sync the game data with the refresh rate.

However if you use Delay(0) you are able to do over a million updates per second.
I have no idea if Delay(0) behave the same on Linux and Mac and Amiga, but hopefully they. (if anyone know, or Fred you have the time) it would be nice if someone mentioned that here!

Here's the code, have fun!

Code: Select all

; (c) Roger Hågensen, EmSai 2005
title$="Multitask Loop v0.1"
text$="This test will take about 60 seconds."+#CRLF$+#CRLF$+"To ensure an accurate test, please do not use the computer during the test!"+#CRLF$+#CRLF$

Enumeration
  #Window
EndEnumeration

Enumeration
  #Gadget_Info
  #Gadget_Start
  #Gadget_Abort
EndEnumeration

If OpenWindow(#Window,0,0,200,185,title$,#PB_Window_SystemMenu|#PB_Window_TitleBar|#PB_Window_ScreenCentered)
  
  EditorGadget(#Gadget_Info,10,10,180,130,#PB_String_ReadOnly|#PB_String_BorderLess|#ESB_DISABLE_LEFT|#ESB_DISABLE_RIGHT)
  ButtonGadget(#Gadget_Start,15,140,70,35,"S T A R T")
  ButtonGadget(#Gadget_Abort,115,140,70,35,"A B O R T")
  DisableGadget(#Gadget_Start,0)
  DisableGadget(#Gadget_Abort,1)
  
Else
  MessageRequester(title$,"Unable to open window!")
  End
EndIf

Repeat
  Event=WindowEvent()
  If Event=#PB_Event_Gadget
    gadget=EventGadget()
    If gadget=#Gadget_Start
      DisableGadget(#Gadget_Start,1)
      DisableGadget(#Gadget_Abort,0)
      SetGadgetText(#Gadget_Info,text$+"Starting...")
      While WindowEvent()
      Wend
      gadget=0
      loopcount=0
      Delay(1000)
      SetGadgetText(#Gadget_Info,text$+"Please wait...")
      Delay(1000)
      Started=ElapsedMilliseconds()
      StartTime=Started
      Repeat
        Event=WindowEvent()
        If Event=#PB_Event_Gadget
          gadget=EventGadget()
        EndIf
        ; do some test code here
        
        ; end test code
        Delay(0) ;no delay, just tell the system to go to next task in cpu queue!
        CurrentTime=ElapsedMilliseconds()
        ElapsedTime=CurrentTime-StartTime
        loopcount+1
        ended=ElapsedMilliseconds()-Started
      Until gadget=#Gadget_Abort Or ended>59999
      Event=0
      If gadget=#Gadget_Abort
        SetGadgetText(#Gadget_Info,text$)
      Else
        SetGadgetText(#Gadget_Info,text$+"Rating: "+StrF(loopcount/ended,3)+" (loops/ms)")
      EndIf
      DisableGadget(#Gadget_Start,0)
      DisableGadget(#Gadget_Abort,1)
    EndIf
  EndIf
  Delay(1)
Until Event=#PB_Event_CloseWindow

While WindowEvent()
Wend
CloseWindow(#Window)
End
El_Choni
TailBite Expert
TailBite Expert
Posts: 1007
Joined: Fri Apr 25, 2003 6:09 pm
Location: Spain

Post by El_Choni »

This is a cool tip, thanks!
El_Choni
benny
Enthusiast
Enthusiast
Posts: 465
Joined: Fri Apr 25, 2003 7:44 pm
Location: end of www
Contact:

Post by benny »

@Rescator:

Sounds like a nice hint :!: Thank you for the nice explanation ...
...
However if you use Delay(0) you are able to do over a million updates per
second.
I have no idea if Delay(0) behave the same on Linux and Mac and Amiga, but
hopefully they. (if anyone know, or Fred you have the time) it would be nice
if someone mentioned that here!
Indeed, that would be interesting. Unfortunately I am on Windows only atm.
So, would be nice if a MAC/Linux enthusiast could report about it.
regards,
benny!
-
pe0ple ar3 str4nge!!!
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post by Trond »

This is really not a good tip. Sure, it makes it so that other applications can react. The problem is that it still use the whole CPU, which means it gets warm, and my fan start blowing and making A LOT immedeately.
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

Yeah! That is the issue, with performance vs cpu load.
But remember, it doesn't hog the cpu as not using a delay at all.

So if you want good performane, fast response, and high update/fps etc.
This is a good middle ground. There is another thread here somewhere where people struggle to come up with a good high quality timer.
Once they find a good solution there, then you may find a routine that let
you only use as much cpu as needed.

Please note that load balancing code can be used in addition to a Delay(0)
load balancing wouldn't work so great with Delay(1) due to the innacuracy
of the normal system timers, and the cpu scheduling overhead.

Delay(0) basicaly says to the system "Give me what you got...please!"
whereas no delay at all is like "gimme all you got and NOW!"
And Delay(1) is like "I don't really wanna intrude or anything, exuse me I'll leave now"

*laughs*


But seriously!
Remember that Delay(0) is only part of the solution!
You may have the main program thread, then a graphics thread.
The graphics thread should only refresh when ther actually is anything to refresh.
And the main loop/thread would hopefully have some load balancing,
only calculating data when there actually is a need to calculate.
You could easily use a Delay(1) elsewhere (in the load balancing code may be?) that is used when the program/game is "idle".


PS! About the cpu kicking into "full" speed mode as you seemed to describe.
That is a downside to a Delay(0)
but the benfit is twofold. (or threefold depending on how you look at it).
The cpu is kept at full speed so you won't have issues where the cpu goes "low" and then has to step it up suddenly.
and as I said, using load balancing you can enfore a Delay(1) when "you" want, rather than having it as a static "speed".
Also, windows and other programs will not get any penalty obviously, things will stay responsive.
And the game/program will have more cpu power available than if Delay(1) was used all the time.

In fact, you gave me an idea.
In combo with a load balancing I guess one could use a variable as the value.
I.e: Delay(pause)
where the load balance code changes pause to 0 or 1 depending on how much it estimate it will need.
So during slow/calm parts where a hgh respons is not needed.
i.e pause screen, credits, score, options menu, or when the player is not moving at all and nothing is going on within the players visual range
and in the cases where there is so little stuff going on that it isn't that time critial. The load balancer would set pause to 1
And then when stuff is happing and the player is moving etc etc. pause could be set to 0 and the game/program will be able to grab whatever cpu slice time that is free to use, but without hogging the cpu or causing a system slowdown.

hmm. You gave me some cool ideas for a load balancing system now :)
dell_jockey
Enthusiast
Enthusiast
Posts: 767
Joined: Sat Jan 24, 2004 6:56 pm

Post by dell_jockey »

thanks for this well researched/documented tip!
cheers,
dell_jockey
________
http://blog.forex-trading-ideas.com
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Post by blueznl »

resc, that was just what i was thinking about :-)

got some stuff to work on... think i know a solution....
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
dmoc
Enthusiast
Enthusiast
Posts: 739
Joined: Sat Apr 26, 2003 12:40 am

Post by dmoc »

Anyone try a variable number of calls of Delay(0) instead of Delay(n)? Just a thought but can't check myself atm because I'm not on win-puter.
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post by Trond »

Yeah! That is the issue, with performance vs cpu load.
But remember, it doesn't hog the cpu as not using a delay at all.
The total load on the CPU will be exactly the same. This is just a way of letting other programs react. Taking more CPU than necessary is totally unacceptable in a multi-tasking environment. Whatsoever.

Listen to this: (ogg, 226 kb) What you made my computer do
thefool
Always Here
Always Here
Posts: 5875
Joined: Sat Aug 30, 2003 5:58 pm
Location: Denmark

Post by thefool »

is that a vacum cleaner? :D

nah really the big games, they use 100% cpu, dont they?
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post by Trond »

Yes. So I can't play many games on this computer. I don't want a whole rush of new applications causing problems too.
Team100
New User
New User
Posts: 4
Joined: Fri Feb 18, 2005 10:28 pm

Post by Team100 »

.... I am using this code in some business applications

Code: Select all


Repeat

; .... Your code here

dynamic_delay_set = 50

; set dynamic delay individual in your code


loopcounter + 1

If loopcounter >= dynamic_delay_set
   Delay(1)
   loopcounter = 0
Endif

until quit = 1

With this code You will call delay after the number
of loops you set with the dynamic_delay_set variable.

You can set it to 1 for delay(1) or you can set it to 1000 or
more for full CPU load (don't forget to set loopcounter = 0
within your code when starting a boost)

The advantage is the possibility of setting this variable
within your code depending the need of CPU-power.

I didn't test this with game and graphics programing,
but it works well for applications using WindowEvent()

With a set to 50 the CPU load is less ( like delay(1)) but the speed of
the program is much more higher.

It would be interesting to know if this will work for
games too.

Cu

Team100
Anubis
New User
New User
Posts: 6
Joined: Fri Jun 03, 2005 11:32 am
Location: California - USA

Post by Anubis »

Trond, it sounds like it is time to get a new computer that has a 'silent' fan on it or replace the fan you have. :wink:

I pratcially fell over with laughter when I heard your sound bite... (I agree that it certainly sounds like a vacuum cleaner) it reminds me of my servers at work! :shock:

Theory: Using 95%+ of your cpu is a good thing generally speaking. Unless of course it creates slowdowns and other obvious issues. I hate nothing more than to have a powerful computer that sits at 1-5% processing power, wasting resources.

I also know some laptops have some very noisy fans, and they love to heat up, even without pushing the cpu. Who develops this stuff? :evil:
/\NUBIS
Post Reply