Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » High-performance timers

This thread is locked; no one can reply to it. rss feed Print
High-performance timers
Goalie Ca
Member #2,579
July 2002
avatar

I've attached a simple windows/nix header file which uses native OS periodic timers. The windows timer is set to 1ms accuracy and the *nix one uses posix real-time extensions. Lower accuracy = lower cpu usage on windows. They both use the semaphore mechanism to wake other threads up and go to sleep. No rest() needed.

this provides example usage. It is ugly and slow and will use a lot of cpu (3% on nix, 5% on windows) because of the software drawing to screen. Hardware accelerated / opengl will fare a lot better. Without the drawing code both use virtually no CPU.

timer_init(milliseconds);
timer_wait(); //sleeps until timer wakes up
timer_cleanup();

*nix version linked to librt, windows version linked to winmm.lib
I used windows multimedia timers. Anyone who know windows better point me to a better library

Comments/feedback etc????/

1//Note: this code references the attached timer.h
2#include <allegro.h>
3#include "timer.h"
4 
5int main(int argc, char** argv)
6{
7 if(allegro_init())
8 return 1;
9
10 install_keyboard();
11 set_color_depth(24);
12 
13 if (set_gfx_mode(GFX_AUTODETECT, 640, 480, 0, 0))
14 {
15 set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
16 allegro_message("Unable to set graphic mode\n%s\n", allegro_error);
17 return -1;
18 }
19 
20 if(timer_init(100)) /*time in milliseconds */
21 {
22 set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
23 allegro_message("Failed to initialize timer"); //todo: fetch errno on linux, error on windows. make a macro/function
24 return -1;
25 }
26 
27 /* main loop */
28 while(1)
29 {
30 timer_wait();
31 if (key[KEY_ESC])
32 break;
33 rectfill(screen, 1, 1, SCREEN_W, SCREEN_H, makecol(0x30,0x30,0x30) );
34 textprintf_ex(screen, font, 10, 10, makecol(255, 100, 200),-1, "Time: %d", time(NULL) );
35 }
36 timer_cleanup();
37 
38 return 0;
39}
40END_OF_MAIN();

-------------
Bah weep granah weep nini bong!

bamccaig
Member #7,536
July 2006
avatar

Sounds great. I'm too exhausted to really look at it. I will say that it looks like the Windows code is tied to Allegro which seems unnecessary since AFAIK it doesn't actually use Allegro for anything... :-/ If that's the case I think it would be better written for the platform instead of for Allegro on the platform.

Goalie Ca
Member #2,579
July 2002
avatar

Right now the allegro.h include is used to determine if it is windows or not. Also I include winalleg.h. True that nothing else specific to allegro resides in there. It was a quick hack but i'll come up with a better solution soon.

-------------
Bah weep granah weep nini bong!

GullRaDriel
Member #3,861
September 2003
avatar

Depending of the type of program who use your timer code, CPU can be wasted as it never was, because:

msdn said:

This function affects a global Windows setting. Windows uses the lowest value (that is, highest resolution) requested by any process. Setting a higher resolution can improve the accuracy of time-out intervals in wait functions. However, it can also reduce overall system performance, because the thread scheduler switches tasks more often. High resolutions can also prevent the CPU power management system from entering power-saving modes. Setting a higher resolution does not improve the accuracy of the high-resolution performance counter.

This was discussed some times ago. I linked a website in this topic who was about various high precision timer.

But heh, until your program manage a ton of thread, this should have a little impact.

All that shit to say: imho, it seems that you have done the best for the Windows part.

"Code is like shit - it only smells if it is not yours"
Allegro Wiki, full of examples and articles !!

bamccaig
Member #7,536
July 2006
avatar

/offtopic

Goalie Ca said:

if(timer_init(100))

This way of coding seems odd to me. I'm used to using booleans to indicate success or failure and that statement seems to be saying if timer_init() is successful... Whenever I use C-like routines that work like this I explicitly compare against 0 to indicate that success should return 0 and that the routine doesn't return a boolean value.

if(timer_init(100) != 0)
{
    set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
    allegro_message("Failed to initialize timer"); //todo: fetch errno on linux, error on windows. make a macro/function
    return -1;
}

I guess I'm too new school. Returning zero on success seems unorthodox. It makes sense that only one value is needed for success and multiple values can give a better description of an error, but the logic of saying if routine() then an error occurred doesn't really make sense in the same realm as boolean logic.

Hehe, /evilthought:

1#include <stdio.h>
2 
3#include "routine.h"
4 
5#define FAILED != 0
6#define SUCCEEDED == 0
7 
8int main(int argc, char* argv[])
9{
10 if(routine() FAILED)
11 {
12 printf("Error! routine() failed!");
13 exit(-1);
14 }
15 
16 return(0);
17}

/sorry-for-hijacking :-X

gnolam
Member #2,030
March 2002
avatar

Quote:

I guess I'm too new school. Returning zero on success seems unorthodox.

Quote:

int main(int argc, char* argv[])
{
...
    return(0);
}

;)

--
Move to the Democratic People's Republic of Vivendi Universal (formerly known as Sweden) - officially democracy- and privacy-free since 2008-06-18!

bamccaig
Member #7,536
July 2006
avatar

Goalie Ca
Member #2,579
July 2002
avatar

Quote:

Depending of the type of program who use your timer code, CPU can be wasted as it never was, because: ...

All that shit to say: imho, it seems that you have done the best for the Windows part.

Well.. I was thinking about setting the default to 5ms. It is all put in a #define so anyone can change it. 1ms shouldn't tax a modern machine too much. This seemed to be the cleanest way to get high-resolution and multi-core support.

AFAIK, it never clearly explains the details, context switches occur more often. I suppose that it shrinks the timeslices, does more pre-empting, etc.

Quote:

This way of coding seems odd to me.

That style is used everywhere. It is comfortable and natural in systems level programming. I suppose that it makes more sense if you read the c-code as a portable assembly.

psh 100
jsr timer_init
bne error
; next line of code....

Or maybe in plain english:
I read it like "timer_init" returns error. if error then something.

-------------
Bah weep granah weep nini bong!

Evert
Member #794
November 2000
avatar

On a tangent, have you looked into Allegro 5's timers (the 4.9 SVN branch), just to see if there's anything in there that needs improvement?

BAF
Member #2,981
December 2002
avatar

Quote:

I guess I'm too new school. Returning zero on success seems unorthodox. It makes sense that only one value is needed for success and multiple values can give a better description of an error, but the logic of saying if routine() then an error occurred doesn't really make sense in the same realm as boolean logic.

Returning a value to state success/nonsuccess on something required like that is unorthodox. If you really did OOP, you would use exceptions. :P

Goalie Ca
Member #2,579
July 2002
avatar

Quote:

On a tangent, have you looked into Allegro 5's timers (the 4.9 SVN branch), just to see if there's anything in there that needs improvement?

I'll look into it in detail when i get time. Allegro5 appears to use a new timer API (not like i ever looked at the old one anyways). Actually i'm quite interested in getting a public threading/concurrency interface in there. On quick inspection i see a lot of internal stuff already there.

-------------
Bah weep granah weep nini bong!

Thomas Fjellstrom
Member #476
June 2000
avatar

Quote:

On quick inspection i see a lot of internal stuff already there.

One or two of the main devs have something (I'm not sure what it is) against including a public thread api. Though allegro has an internal api that it itself uses.

--
Thomas Fjellstrom - [website] - [email] - [Allegro Wiki] - [Allegro TODO]
"If you can't think of a better solution, don't try to make a better solution." -- weapon_S
"The less evidence we have for what we believe is certain, the more violently we defend beliefs against those who don't agree" -- https://twitter.com/neiltyson/status/592870205409353730

Peter Wang
Member #23
April 2000

The new API timer implementations were based pretty closely on the old timer implementations, so if you have a problem with the old ones....

But, the new API should accomodate different implementations much better than the old one, so definitely submit patches if you can. I'm particularly interested in implementations that make use of the various high precision timer facilities in Linux.

Goalie Ca
Member #2,579
July 2002
avatar

If i understand the code correctly (quick review).

1) Timer thread is spawned
2) Timer Thread goes to sleep
3) Timer thread wakes up
4) If timer has not expired goto 2
5) If timer has expired call user supplied callback

So the user will supply a function to increment a ticker. Then they will spinlock() or rest() in the main loop. This whole process is really just redundant. Both windows and unix will spawn their own threads and handle all of the above automatically. The code I posted above uses these. My code supplies the callback and the delay function for the main loop. The "hack" is that i use a semaphore to put the main loop to sleep and unlock it in the timer callback.

Another possibility is to eliminate those separate thread timers and create a wrapped rest function that keeps track of overflow and underflow. But.. getting the current "time" value is broken on some multiprocessor configurations. Processor affinity is key. This is described in both the windows and unix docs.

Quote:

One or two of the main devs have something (I'm not sure what it is) against including a public thread api. Though allegro has an internal api that it itself uses.

Well.. in this day and age it would be really handy.. and it would provide excellent user testing on thread-safety in the library :-)

-------------
Bah weep granah weep nini bong!

Go to: