Drawing to the screen uses too much CPU

I have this tiny little program:

1#include <allegro.h>
3volatile int counter;
4void update_counter()
6 counter++;
10int main()
12 allegro_init();
13 install_keyboard();
14 install_timer();
15 LOCK_VARIABLE(counter);
16 LOCK_FUNCTION(update_counter);
18 set_color_depth(16); //same problem when using 24 and 32
19 if (set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0) != 0)
20 {
21 allegro_message("Couldn't set up a screen mode!");
22 return 1;
23 }
25 install_int_ex(update_counter, BPS_TO_TIMER(50));
26 counter = 0;
28 while (!key[KEY_ESC])
29 {
30 while (counter > 0)
31 {
32 clear_to_color(screen, makecol(0, 0, 0));
33 //or rectfill(screen, 0, 0, 640, 480, makecol(0, 0, 0));
34 //or create a black bitmap and blit it to the screen
35 counter--;
36 }
37 rest(1);
38 }
40 allegro_exit();
41 return 0;

My problem is that this takes up 100% of my CPU time. Can anyone figure out what's going wrong? It seems that anything that draws to the scren takes way too much processor time.


Unless you relinqusish CPU time to the operating system, with software drawing you will always use 100% on any machine, regardless of its speed. All you will do is spin your loop faster.

You need a framerate reguluator that either sleeps when it is going too fast, or use something that blocks, like vsync (does Allegro's vsync do true blocking on Windows?), to limit your framerate and reduce your CPU use below 100%.

I see you are using rest method, but maybe it is not working? 1ms may be too small of a sleeping time.

EDIT: I guess I assumed you were working with Windows. What OS are you using, and is rest implemented properly there?


It might not help you, but this is what my game cycle looks like:

1 BITMAP *bmp = create_bitmap(SCREEN_W, SCREEN_H);
3 while (!key[KEY_ESC])
4 {
5 while(counter <= 0) {
6 rest(1);
7 }
8 while (counter > 0)
9 {
10 // do some logic
11 counter--;
12 }
13 clear(bmp);
14 draw_graphics(bmp);
15 blit(bmp, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
16 }
18 destroy_bitmap(bmp);

Last night this code allowed me to watch DVD's while testing my framework.


Can someone try the code I posted and see if they get the same result?


Just wondering, why are you using rest(1) ?
My game loop is also like yours rampage just without the rest bit :P
Sorry, off your topic but I'm just curious.


I use rest(1) so that the game takes as little CPU time as possible. Anyway, has anyone tried my code and seen whether they have the same problem?

Edit: Come to think of it, all the allegro examples I've tried are taking 100% CPU as well. I'm going to try recompiling Allegro...

Edit #2: Huh. Trying exaccel gives me this:


Clear: software (urgh, this is not good!)
Blit: software (urgh, this program will run too sloooooowly without hardware acceleration!)

Could that be what's wrong?


You are using clear_to_color(screen, makecol(0, 0, 0)); within your logic loop. Move it outside of that loop and it should be just fine.


As was said, your game will always use 100% CPU unless it's event driven. It's a fact. Sleeping for 1ms won't help at all because sleep(1) ends up sleeping for 16ms, giving you a terrible framerate. This is due to timer innaccuracy.
It's not a bad thing unless your game is to be used in parallel with another program.


Ah, right.

Still, all OpenLayer games I've tried on the laptop barely go above 30% CPU, so it's a bit troubling when a plain Allegro game takes up 100%. Is there a more accurate sleep function or something else that'll help me lower the CPU usage?


Remove the counter:

  while (!key[KEY_ESC])
      clear_to_color(screen, makecol(0, 0, 0));
      //or rectfill(screen, 0, 0, 640, 480, makecol(0, 0, 0));
      //or create a black bitmap and blit it to the screen

If your drawing is fast enough (~1/60th of a millisecond) you'll run at 60fps. The slower your drawing the slower your framerate, but this should keep CPU usage pretty low. (if it doesn't work, try sleep instead of rest).

rest(1) will sleep for ~16ms (timer innaccuracy), so if you can draw really fast it will ...
draw: 0.1ms
rest: 16ms
in 1 second that's roughly 60 frames.


vsync may reduce CPU usage. If it doesn't use any CPU while waiting on the screen, then you'll see a drop in CPU usage. That may be what's going on with OpenLayer (if it's vsync-ing automatically).


By the way, you can still use a counter if you want, but only to keep track of time. I wouldn't use it to control FPS in the this case, because sleep is going to mess it up.

Richard Phipps

Try different amounts of rest, i.e. rest(2) rest(3) and see what effect this has on your CPU usage.


Richard: It should have none. As I said, the timer resolution is roughly 16ms. So anything < 16ms is going to end up near 16ms.

As a side note, timer resolution can be changed on most operating systems, but it is done manually, doesn't usually get below 1ms, and obviously you can't count on it being so (nor ask users to do it).

Goalie Ca

rest(1) will sleep for ~16ms (timer innaccuracy), so if you can draw really fast it will ...

DO NOT RELY ON OS SCHEDULING FOR TIMING. Your program can be put in at any time basically.

James Stanley

rest(0) waits until the operating system says it can go, AFAIK.

The reason all the examples use 100% is because they are effectively on a continuous loop. Write a continuous loop without allegro, and it will still use 100% unless you let the operating system know you're willing to let it go and do it's thing, usually be rest(0)-ing.


rest(0) will still use 100% of the CPU, but it tells the OS to let other processes have a turn, if needed. So, rest(0) basically just uses up whatever extra CPU time it can. Rest(1) will actually decrease CPU usage, but is inaccurate and may have adverse effects on your program. Games are very time critical, so I wouldn't worry about about how much CPU usage it shows that you are using, but rather performance.


Games are very time critical, so I wouldn't worry about about how much CPU usage it shows that you are using, but rather performance.

unless you are runnign on batteries ;)


Yes, this is critically important if you are running on batteries, or with variable cooling systems (esp when using those massive wattage Pentium D processors). On my work box (thankfully I don't play games or peg the CPU a lot), when something takes 100% CPU it goes from dead silent to a flippin' jet that needs FAA clearance to take off. Of course it's a small form-factor Dell machine.

Even on my AMD machine, the same happens, except no where near as loud. So there's no reason to burn CPU if you're just going to spin and can't display any more frames.

On a laptop you can cut your battery life in half or more by burning up the CPU like that in a loop.

If you are using proper triple buffering with waits or another similar system, the OS will block for you if your game runs too fast, so you can use a "standard" limiting/timing system but still get the blocking advantage, without any framerate stutter since you render 2 frames ahead you get that buffer.


gillius is correct, triple buffering and other methods that use show_video_bitmap do indeed reduce CPU usage. show_video_bitmap locks the current thread until vsync (unlike vysnc() itself, mind you). Which means that if your program can draw faster than the refresh rate, your CPU usage will go under 100%.

Note, though, that this only works on systems with triple buffering, and only in fullscreen.

I have developed a better solution. It is by no means an end all, but it will work in both fullscreen and windowed.

1#include "pthread.h"
2#include <allegro.h>
4void *timer(void *ptr)
6 // DO NOT sleep for less than 16ms
7 // Most OS timers are not accurate under 16ms
8 sleep(16);
9 // You may change the sleep length if you want a different FPS < 60
10 // sleep(1000/FPS);
11 // DO NOT use any FPS > 60
14void wait_for_timeslice(pthread_t *thread)
16 pthread_join(*thread, NULL);
17 pthread_create(thread, NULL, timer, NULL);
20int main()
22 allegro_init();
23 install_keyboard();
24 install_timer();
26 set_color_depth(16); //same problem when using 24 and 32
27 if (set_gfx_mode(GFX_AUTODETECT_WINDOWED, 640, 480, 0, 0) != 0)
28 {
29 allegro_message("Couldn't set up a screen mode!");
30 return 1;
31 }
33 pthread_t thread;
34 pthread_create(&thread, NULL, timer, NULL);
35 while(!key[KEY_ESC])
36 {
37 clear_to_color(screen, makecol(255, 255, 255));
38 line(screen, 0, 0, rand() % SCREEN_W, rand() % SCREEN_H, makecol(0,0,0));
40 wait_for_timeslice(&thread);
41 }
43 allegro_exit();
44 exit(0);

It sets up a thread that runs in the background for 16ms. After drawing is done it does "pthread_join" on the sleeper thread. This will lock the main thread until the sleeper thread is done sleeping, during which time no CPU is used. After that it recreates the sleeper thread so it can be used again for the next frame.

This method will draw at most 62.5 frames per second. If your program can run faster than that the CPU usage will drop while continuing at 60FPS.
In the case where the host machine is too slow, your program will run at under 60FPS and 100% CPU. This is an unfortunate case, but what do you expect when the host machine is slow?

I tested the above code on my machine and got 10% CPU usage (0 without the line call). What do others get? (You'll need pthreads to compile) Attached is an EXE in case you can't compile, as well as the pthreads DLL (for windows).


// Most OS timers are not accurate under 16ms

Ugh, both Windows and Linux have 10 Hz timer frequencies by default. It's 10 ms, not 16 :P

kdevil: Yes, the problem is probably that you are using the GDI driver. I don't know why that would be, but I suggest starting from scratch with Allegro, and make sure you have the proper DirectX framework (from Allegro's SF page) installed, and DirectX installed, and graphics drivers and all that. If that doesn't help, try posting your specs.


I don't think there is a cause to use pthreads for this. I developed a timer that would sleep appropriately and would have different (automatic) modes of operation, including frameskip modes. So it would go (slower than real-time with max frameskip) -> (real-time with some frameskip) -> (real-time with sleeps when needed).

The way I did it was to keep a game time counter and a real time counter and to do different actions based on where the game time was in relation to the real time -- sleep or skip frames, or slow the logic down (skip logic frames).

The original thread is at http://www.allegro.cc/forums/thread/305773, but I think I attached the code and it is only on my machine now. I can post it if people want. The original thread has much much more detail on what I did.

After I wrote the code, I realized that you could do something simpler by just sleeping if your gametime was ahead. This is fine if you don't care about the game running ahead about 8ms on average.


Ugh, both Windows and Linux have 10 Hz timer frequencies by default. It's 10 ms, not 16 :P

Did you mean 100Hz?
And no matter what timer it may be using I've always seen timer function have an accuracy of 16ms, not 10ms.


Did you mean 100Hz?
And no matter what timer it may be using I've always seen timer function have an accuracy of 16ms, not 10ms.

Yes :P
I suggest you run more tests... We've had several threads on this topic, agreeing on the 100 Hz timer...

Jakub Wasilewski

I suggest you run more tests... We've had several threads on this topic, agreeing on the 100 Hz timer...

I'm going to bet my left sock that William is using 60hz as his refresh rate, and the vsync caps his timer rate (100/60 = 16.666...).

Also, under Windows it is possible to change the resolution of the scheduler - see timeBeginPeriod() and timeEndPeriod(). IIRC, they're not guaranteed to work, but they're worth trying if you really can't stand sleeping for 10ms instead of 1ms. And I'm quite certain that the default under W2K and WXP is 10.


I'm going to bet my left sock that William is using 60hz as his refresh rate, and the vsync caps his timer rate (100/60 = 16.666...).

Yeah, this was my thought also. I didn't say anything because I hope he wasn't using a graphical program to test that.

Kitty Cat

Also, under Windows it is possible to change the resolution of the scheduler - see timeBeginPeriod() and timeEndPeriod().

That's rather dangerous, as it can have adverse affects on the rest of the system (especially older systems) and you'll be in for problems if your program dies without calling timeEndPeriod. Under Linux, the timer is configurable (250hz, 100hz, 1000hz, IIRC), so you should not assume any kind of granularity when resting. If you rest(0), it will get back to you ASAP after checking other threads. If you rest more, it will get back to you ASAP after the time is over. That's all you can gaurantee.


// DO NOT sleep for less than 16ms
// Most OS timers are not accurate under 16ms

Why shouldn't you sleep for less than 16ms, even if that's the granularity you get? What's the difference if you rest for 1ms and get back in 16ms, or rest for 16ms and get back in 16ms? In fact, trying to match it like that can cause you more problems if it checks your process just ahead of schedule, sees that you want to sleep a bit more, then goes another round without you.

Your granularity is determined b y a number of factors, not the least of which is how much CPU time the other processes take. Even with a 10ms timeslice, if you rest for 1ms, and after 5ms, no process wants to use the CPU, the OS will see your thread and wake you up (thereby causing you to sleep for 5ms, instead of 10 or 16).

There is another issue, though. Allegro's timers themselves are very innaccurate, so you shouldn't use them to time your logic if you can help it. Every single running timer is run on the same thread, so after resting and being woken up to run your callback (incurring the 5~10ms delay), it'll check all other timers to see if they want to run. And if one of them happens to want to, you'll get a longer delay.

Tobias Dammers

Why are you using the unreliable and unpredictable (in terms of accuracy) multithread-timing facilities at all? Here's what I usually do:
- I use a high-resolution timer like QueryPerformanceCounter (Linux has gettimeofday() which is supposed to be as exact; since I'm on a windows box, I haven't investigated this to any extent though). Wrote a nice wrapper (once, for all my future projects) around the various interfaces, falling back on allegro timers if all else fails.
- In my main loop, I first read the timer delta and accumulate it. I decide independently if I need to update logic (if (timer) > 0 for variable-delta, while (timer >= timeslice) for fixed-delta) and graphics (cap to, say, 60 Hz or whatever you like, or don't cap and just update only if logic has changed). After that, I sleep (with a user option to turn this off).
Works beautifully.


Would you mind posting your framework?

Tobias Dammers

I might release a little library soon. In the meantime, here's my timer api.

#ifndef TIMER_H
#define TIMER_H

void init_timer(bool use_qpc = false);
void stop_timer();
void start_timer(int timer_ms);
float get_timer_delta();
const char* get_timing_method_string();
float get_timer_accuracy();



1#include <allegro.h>
3 #include <winalleg.h>
5#include "timer.h"
7bool initialized = false;
9int timer = 0;
10int timer_ms = 0;
13LARGE_INTEGER last_perf_count_li;
14LARGE_INTEGER perf_count_li;
15LARGE_INTEGER perf_freq_li;
16unsigned long long last_perf_count;
17unsigned long long perf_count;
18unsigned long long perf_freq;
20unsigned long long li_to_ll(LARGE_INTEGER li) {
21 unsigned long long ll = li.HighPart;
22 ll = ll << 32;
23 ll |= li.LowPart;
24 return ll;
28bool qpc_mode = false;
30void timer_proc() {
31 ++timer;
35void init_timer(bool use_qpc) {
37 if (use_qpc)
38 qpc_mode = QueryPerformanceFrequency(&perf_freq_li);
39 else
41 qpc_mode = false;
43 if (qpc_mode)
44 perf_freq = li_to_ll(perf_freq_li);
46 if (!qpc_mode) {
47 LOCK_VARIABLE(timer);
48 LOCK_FUNCTION(timer_proc);
49 install_timer();
50 }
52 initialized = true;
55void stop_timer() {
56 if (!qpc_mode)
57 remove_int(timer_proc);
60void start_timer(int _timer_ms) {
61 if (qpc_mode) {
63 QueryPerformanceCounter(&last_perf_count_li);
64 last_perf_count = li_to_ll(last_perf_count_li);
66 }
67 else {
68 stop_timer();
69 timer_ms = _timer_ms;
70 install_int(timer_proc, timer_ms);
71 }
74float get_timer_delta() {
75 if (qpc_mode) {
77 QueryPerformanceCounter(&perf_count_li);
78 perf_count = li_to_ll(perf_count_li);
79 float result = (float)((double)(perf_count - last_perf_count) / (double)perf_freq);
80 last_perf_count = perf_count;
81 return result;
83 return NULL;
85 }
86 else {
87 float result = (float)timer * (float)timer_ms * 0.001f;
88 timer = 0;
89 return result;
90 }
93const char* get_timing_method_string() {
94 if (!initialized)
95 return "Not initialized!";
96 if (qpc_mode)
98 return "QueryPerformanceCounter";
100 return "Something's terribly wrong";
102 return "Allegro timer routines";
105float get_timer_accuracy() {
106 if (qpc_mode)
108 return (float)perf_freq;
110 return 0.0f;
112 if (timer_ms)
113 return 1000.0f / (float)timer_ms;
114 return 0.0f;

And here's what the main loop does:
(snip from scene.cpp)

1bool SCENE::run() {
2 bool looping = true;
3 float dt = 0.0f;
4 float paintt = 0.0f;
5 float min_paintt = 0.5f;
6 fps = 0.0f;
7 logic_frametime = 0.0f;
8 render_frametime = 0.0f;
9 logic_frames = 0;
10 display_info = false;
12 get_timer_delta();
14 while (looping) {
15 while (keypressed()) {
16 switch (readkey() >> 8) {
17// removed irrelevant code here
18 case KEY_ESC:
19 looping = false;
20 break;
21 }
22 }
24 dt = get_timer_delta();
25 ++logic_frames;
26 tick(dt);
28 paint();
30 paintt += dt;
32 if (paintt > min_paintt) {
33 fps = (float)logic_frames / paintt;
34 logic_frames = 0;
35 paintt = 0.0f;
36 render_frametime = 1.0f / fps;
37 logic_frametime = render_frametime;
38 }
40 if (level_completed)
41 looping = false;
42 rest(1);
43 }
44 return level_completed;

Makes sense?

Thread #586855. Printed from Allegro.cc