timer problem under vista

Windows specific forum
noot
User
User
Posts: 22
Joined: Wed Sep 12, 2007 8:45 am

Post by noot »

I'm using WaitWindowEvent(), it seems to work better here (on XP)

Well now: I tried to strip the program down to just the event loop - getting the time and playing the sounds - and it sort of works under Vista...

so I took the full length program and commented out everything inside the event loop, and noticed two things - first: it performs very differently depending on whether I make an .exe out of it, or run it in the PB console. (makes no difference whatsoever under XP) second: it seemed to get increasingly sticky (but not as bad as the .exe) when i added more stuff inside the event loop.

so, I know I have very much stuff inside the event loop and it's probably very bad programming, but why am i not having these problems under XP? after all it's only Get/SetGadgetState and setting variables, so it shouldn't be too heavy on the hardware (?)
Derek
Addict
Addict
Posts: 2354
Joined: Wed Apr 07, 2004 12:51 am
Location: England

Post by Derek »

noot wrote:after all it's only Get/SetGadgetState and setting variables, so it shouldn't be too heavy on the hardware (?)
You would think it's that way but I've found Vista itself to be pretty heavy on the hardware whereas XP is a little gentler so the problems of extra stuff in an event loop are partially magnified with Vista, might be time to start optimising any code you can and see if that helps at all.
noot
User
User
Posts: 22
Joined: Wed Sep 12, 2007 8:45 am

Post by noot »

might be time to start optimising any code you can
i don't know if i can, i have about 100 gadgets in this damn thing and i need to monitor them all, and there's really not very much else inside the loop...

but nice to know that it's a Vista problem after all :D apparently anyway
Derek
Addict
Addict
Posts: 2354
Joined: Wed Apr 07, 2004 12:51 am
Location: England

Post by Derek »

Just out of curiosity and without posting all your code, can you just post a small section of your mainloop, maybe you are over doing the checking or something.
noot
User
User
Posts: 22
Joined: Wed Sep 12, 2007 8:45 am

Post by noot »

well i can describe briefly what's inside the loop:

- 4 x soundVolume, soundPan, soundFrequency
- 4 x if...and...and...PlaySound
- some ExamineKeyboard and Select for buttongadgets
- twelve (12) times the following kind of bit (checking trackbar and checkbox values) - this should probably be optimised :roll:

Code: Select all

If GetGadgetState(111)=0 And GetGadgetState(112)=0 And GetGadgetState(113)=0 And GetGadgetState(114)=0 And GetGadgetState(115)=0
bdf=GetGadgetState(11)
EndIf

If GetGadgetState(111)=1
bdf=Random(100000)
EndIf

If GetGadgetState(112)=1
bdf=bdf-100
If GetGadgetState(114)=0 And bdf<=0
 bdf=100000
EndIf
EndIf

If GetGadgetState(113)=1
bdf=bdf+100
If GetGadgetState(114)=0 And bdf>=100000
bdf=0
EndIf
EndIf

If GetGadgetState(114)=1 And bdf>=99999
SetGadgetState(113,0)
SetGadgetState(112,1)
EndIf

If GetGadgetState(114)=1 And bdf<=1
SetGadgetState(113,1)
SetGadgetState(112,0)
EndIf
the reason for having so much of this if-get-then-set crap is that i have three trackbars and a bunch of checkboxes for each of the four sounds...
Derek
Addict
Addict
Posts: 2354
Joined: Wed Apr 07, 2004 12:51 am
Location: England

Post by Derek »

Wow, looks like a busy bit of code.

Just as a quick test I changed your code to use variables instead of constantly checking gadget states and halved the time taken, might be all it takes for Vista to be able to run better.

Code: Select all

OpenWindow(0,0,0,800,600,"",$ca0001)
CreateGadgetList(WindowID(0))
CheckBoxGadget(111,10,10,30,30,"")
CheckBoxGadget(112,10,10,30,30,"")
CheckBoxGadget(113,10,10,30,30,"")
CheckBoxGadget(114,10,10,30,30,"")
CheckBoxGadget(115,10,10,30,30,"")

t=ElapsedMilliseconds()
For n=1 To 100000

If GetGadgetState(111)=0 And GetGadgetState(112)=0 And GetGadgetState(113)=0 And GetGadgetState(114)=0 And GetGadgetState(115)=0 
bdf=GetGadgetState(111) 
EndIf 

If GetGadgetState(111)=1 
bdf=Random(100000) 
EndIf 

If GetGadgetState(112)=1 
bdf=bdf-100 
If GetGadgetState(114)=0 And bdf<=0 
 bdf=100000 
EndIf 
EndIf 

If GetGadgetState(113)=1 
bdf=bdf+100 
If GetGadgetState(114)=0 And bdf>=100000 
bdf=0 
EndIf 
EndIf 

If GetGadgetState(114)=1 And bdf>=99999 
SetGadgetState(113,0) 
SetGadgetState(112,1) 
EndIf 

If GetGadgetState(114)=1 And bdf<=1 
SetGadgetState(113,1) 
SetGadgetState(112,0) 
EndIf

Next
t=ElapsedMilliseconds()-t
Debug t

t=ElapsedMilliseconds()
For n=1 To 100000

g111=GetGadgetState(111)
g112=GetGadgetState(112)
g113=GetGadgetState(113)
g114=GetGadgetState(114)
g115=GetGadgetState(115)

If g111=0 And g112=0 And g113=0 And g114=0 And g115=0 
bdf=g111 
EndIf 

If g111=1 
bdf=Random(100000) 
EndIf 

If g112=1 
bdf=bdf-100 
If g114=0 And bdf<=0 
 bdf=100000 
EndIf 
EndIf 

If g113=1 
bdf=bdf+100 
If g114=0 And bdf>=100000 
bdf=0 
EndIf 
EndIf 

If g114=1 And bdf>=99999 
SetGadgetState(113,0) 
SetGadgetState(112,1) 
EndIf 

If g114=1 And bdf<=1 
SetGadgetState(113,1) 
SetGadgetState(112,0) 
EndIf

Next
t=ElapsedMilliseconds()-t
Debug t
Of course, if you have any delay() or waitwindowevent() commands in your loop then it won't make any difference! :wink:
User avatar
pdwyer
Addict
Addict
Posts: 2813
Joined: Tue May 08, 2007 1:27 pm
Location: Chiba, Japan

Post by pdwyer »

noot wrote:thanks for the tip paul, i'll try it with something else (GetTickCount is it?), and maybe also check the driver updates... i'll report back when i see what happens
Actually I was thinking of "QueryPerformanceCounter" but couldn't remember the name before.

Apparently it's a lot higher resolution than GetTickCount which is "limited to the resolution of the system timer" accoring to MS

QueryPerformanceCounter
http://msdn2.microsoft.com/en-us/library/ms644904.aspx

GetTickCount
http://msdn2.microsoft.com/en-us/library/ms724408.aspx

More info about timers in general from MS is here
http://msdn2.microsoft.com/en-us/library/ms644900.aspx
Paul Dwyer

“In nature, it’s not the strongest nor the most intelligent who survives. It’s the most adaptable to change” - Charles Darwin
“If you can't explain it to a six-year old you really don't understand it yourself.” - Albert Einstein
noot
User
User
Posts: 22
Joined: Wed Sep 12, 2007 8:45 am

Post by noot »

Derek,
that's very interesting - I haven't realized that there could be THAT big a difference, although I normally always do put the states into variables, just for convenience... however, I have to use WaitWindowEvent(1) keeping this whole thing together so I'm afraid it's no help this time :?

Another thing I just thought of - maybe I should put Else Gosub after the first check, because there's not much use checking if any of the flags are 1 when they've all just been checked to be 0, is there :D

Paul,
I replaced the milliseconds/ticks with QueryPerformanceCounter, but no change on either system as to the actual performance, BUT the PerformanceCounter resolution is really pretty insane - if I'm interpreting the results correctly it's something like 3500 to 1 compared to the ticks (!) Wow.
User avatar
pdwyer
Addict
Addict
Posts: 2813
Joined: Tue May 08, 2007 1:27 pm
Location: Chiba, Japan

Post by pdwyer »

Yeah, so you need to findout how many ticks per second your hardware does

Code: Select all


freq.q 
If QueryPerformanceFrequency_(@freq)    
    Debug Str(freq) + " ticks per second" 
EndIf

My system is 3,579,545 ticks per second

No idea how much that changes for different CPUs and vendors, I don't suppose it's relevent provided you are dividing correctly, I guess that the function overhead etc means it's not perfect but the tick count ones and PBs seems to be in 16ms resolution on my PC so atleast this is a lot better.

Since this didn't improve your performance I'm guessing that perhaps your sound drivers or something are not 100% under vista and you are runing overtime on response then catching up again, this would explain what you are seeing with performance too, more than a problem with timers and ticks
Paul Dwyer

“In nature, it’s not the strongest nor the most intelligent who survives. It’s the most adaptable to change” - Charles Darwin
“If you can't explain it to a six-year old you really don't understand it yourself.” - Albert Einstein
noot
User
User
Posts: 22
Joined: Wed Sep 12, 2007 8:45 am

Post by noot »

Same here, so apparently it counts 1/3579545's of seconds regardless of hardware - ie. does exactly what a timer should do, and seems to do it pretty very well too!

But yeah, it must be a compatibility issue - and probably an isolated case at that. I'll update the CPU & soundcard drivers to see if that does anything, and after that it's back to XP for me :)
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

timeBeginPeriod_()
timeEndPeriod_()
timeGetTime_()

Look up timeBeginPeriod and timeEndPeriod and timeGetTime in the PSDK or MSDN, also search these forums, I and several others have posted many examples on these.

timeGetTime is like getTicks only it uses the multimedia code in Windows and you can use timeBegindPeriod to increase the accuracy from um 15ms (on XP I think) down to ~2ms.

You might also consider putting the time stuff in it's own thread with a high priority. Myself in a program I created a timer thread set it to realtime priority and which runs in a loop with a Sleep_(1) in it and it sets a global variable with the value from timeGetTime_() and then I use a macro GetTime() that basically is a macro wrapper for the global variable.
I think I posted a example in Tips and Tricks some time ago.
The benefit of this is that the variable is updated each 1ms (remember to set the period at program start and end) or as close to 1ms as possible but your programs can use the GetTime() macro as much as you want with no additional procedure of API call overhead, only the overhead of reading a variable.

If this is overkill for your program (sounds to me like you might benefit from that particular solution actually) then you might want to redo your loop instead.

In a internet radio player I made all stuff is in the mainloop,
but the gadget checking and gfx vumeter display etc is layed out in a very efficient way.

The loop uses WaitWindowEvent(time) the time is a var that changes depending on wether the program is minimzed or not etc. (the goal is to only use as much cpu/resources as you need when you actually need it).

The code is layed out like this.

*loop start

-Was there an event?
--Yes, was it the main window?
---Yes, which gadget?
----What was the event?
-----Do gui stuff.
--No event timeout was reached, or a 0 event occured.

-Is the GUI visible (not minimized)?
--Yes, is it time yet to updated the graphics? (timer check or var check)
---Yes, draw the gui stuff

*loop end

I'm my case the audio is already in it's own playback thread thanks to BASS library so I won't have to deal with that luckily.
The graphics however will not be updated as long as GUI stuff is being processed (this is intentional to minimize CPU use) but is updated once that is done or if no GUI handling was needed at all.

My advice is get the audio stuff into it's own playback thread if you need to.

Also pay close attention to the gui stuff in the example above,
there is no point reading a trackbar unless an event happen and for that window and for that gadget. So if you are checking the trackbar everytime the loop um loops (pardon the pun) that is horrible coding, and it kinda smells like OOP and Managed coding gone wrong.

I really hope you will not continue to use the trackbar itself to store the value, read it and put it in a variable instead, only read the trackbar if you know the value has changed. As pointed out in the posts above your eating up way more cpu/resources than you truly need due to this.

Only do stuff when you actually need to do it, cache values/data wherever possible thus reducing the overhead of calling functions and procedures all the time, the less code there is inside a loop the better, prepare as much as possible before entering a loop, code inside procedures and functions etc also count as part of the code inside the loop so the less code there are inside those the faster the loop is.

With PureBasic you have the power to do this easily, lean mean code ;)
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

I see in your example snippet that you only set gadget 113 and 112
The others you only read so I assume those are moved/changed by the user?
In that case put all that stuff among the other gadget select stuff
and right after endselect put the SetGadgetState.
That should move all the gadget stuff into the select block and only triggered when the user actually interacts with the GUI.

If done properly you should only have the sound play triggering outside/after the gui stuff, getting all the cpu it needs.
The only issue that still remain is if the user happen to interact with the GUI at the same time as a sound event happen, which is why you should consider using a thread.

A nice way to test this would be to click on the window titlebar and keep grabbing it, most likely the sound will never be played until you let go of the window again. With a thread it will play the sound even if you do this.
noot
User
User
Posts: 22
Joined: Wed Sep 12, 2007 8:45 am

Post by noot »

Thanks Rescator, I think this will be a huge help! I didn't even
know about threads previously, but if they work like I understand,
looks like a playback thread will solve one or two other problems
as well... I was just going to ask if there is any way to keep the
program from halting when moving the window etc. but I guess
this is it :) I definitely have some rewriting to do now...
Post Reply