PDA

View Full Version : TThread vs CPU usage?



noeska
19-02-2008, 09:24 PM
What is the best way to keep the cpu usage of an TThread descendant class low?

I used an sleep(50) to get from 80% cpu usage to 8% cpu usage.

But i am afraid sleep is dependend on the speed of the cpu. So depending of the cpu speed the value of 50 should be higer or lower?

Are there other better ways to do this?

I also thought of using application.processmessages but that one is not available form within my TThread based class.

Thanks for your answers in advance.

cronodragon
19-02-2008, 09:53 PM
Do you mean for a game loop? If so, I think the best is to define the FPS you need, time the work done in inside the loop, and sleep the amount of time you are not using in the frame.

OTOH, I have found that Sleep( 8 ) works ok on Windows, in several machines.

JSoftware
19-02-2008, 09:54 PM
What does your thread do?

Sleep uses milliseconds so it's not CPU frequency dependant. Application.Processmessages should be accesible trough the 'forms' unit(as Application is a global variable in VCL programs).

If your thread does a lot of waiting or synchronizing then you should probably use a mutex instead. If it does timeslice updating then sleep is the most optimal solution

noeska
19-02-2008, 10:04 PM
I use it to stream a mp3 file to openal.
If i set sleep to high sound is interupted by silence. If i set it to low the cpu usage goes up. The mp3 decode part might take longer or shorter dependend on the cpu. So on a slow pc sleep(50) may also take to long.

see also: http://www.pascalgamedevelopment.com/viewtopic.php?p=41863&sid=5c401ca85f8260f684287e7d67557955#41863

JSoftware
19-02-2008, 10:20 PM
As the first thing I would remove your critical section handling. It makes no sense at all. Make it look like this:


while not terminated do
begin
updatebuffer;
sleep(50); //TODO: are there better ways to do this?
end;

I can't think of anything else. You could calculate the time you would need between updates by dividing your bitrate with your buffersize, but I don't think that'll make it better.

8% also sounds like a fine percentage to me

arthurprs
19-02-2008, 11:13 PM
sleep pauses the tread for some time, i think there is no bether way

(i heard that waitforsingleobject and waitformultipleobjects uses sleep internaly, but im not sure, it can be another option)

Mirage
20-02-2008, 05:21 AM
I suggest to make two buffers. One to play from and other to decode to.
Code which plays the buffers will set hBufferPlayedEvent by calling SetEvent(hBufferPlayedEvent) when it ends with one buffer and switches to other (just swapping pointers). Decoding thread will get this event and will start decoding to buffer which was just played. Backbuffer technique.:)

You decoding threads's code will be something like this:

while not terminated do
begin
updatebuffer;
WaitforSingleObject(hBufferPlayedEvent, INFINITE);
end;

Actually it's better to use WaitforMultipleObjects() to correctly exit the loop when program terminates, but it's "premature optimizations" for now.:)

Pyrogine
20-02-2008, 06:03 AM
Mirage is correct.

const
P2D_OGG_BUFSIZE = 1024*8;
P2D_THREAD_DELAY = 5; // milliseconds

procedure TOggPlayer.Update;
var
pos : DWORD;
size: DWORD;
buf : pchar;
pbuf: pchar;
sec : Integer;
ret : Integer;
begin
if FDone then Exit;

if FPaused then Exit;

FDSBuffer.GetCurrentPosition(@pos, nil);

//nCurSection = pos<BUFSIZE?0:1;
if pos<P2D_OGG_BUFSIZE then
FCurSection := 0
else
FCurSection := 1;

// section changed?
if (FCurSection <> FLastSection) then
begin
if (FDone = True) and (FLoop = False) then
begin
Stop();
end;

// gotta use this trick 'cause otherwise there wont be played all bits
if (FAlmostDone = True) and (FLoop = False) then
begin
FDone := True;
Stop();
end;

size := P2D_OGG_BUFSIZE;

// fill the section we just left
buf := nil;
FDSBuffer.Lock(FLastSection*P2D_OGG_BUFSIZE, size, @buf, @size, nil, nil, 0 );

pos := 0;
sec := 0;
ret := 1;

while((ret > 0) and (pos<size> 0) and (pos<size)) do
begin
pbuf := buf + pos;
ret := ov_read(FVorbisFile, pbuf^, size-pos, 0, 2, 1, @sec);
inc(pos, ret);
end;
end
else if ((ret = 0) and (FLoop = False)) then
begin
// not looping so fill the rest with 0
while(pos<size) do
begin
pbuf := buf + pos;
pbuf^ := #0;
inc(pos);
end;

// and say that after the current section no other sectin follows
FAlmostDone := True;
end;

FDSBuffer.Unlock( buf, size, nil, 0);

FLastSection := FCurSection;

end;
end;

procedure TOggAudioThread.Execute;
begin
repeat
CriticalSection_Enter;
if Assigned(Audio) then
begin
Audio.Update;
end;
Sleep(P2D_THREAD_DELAY);
CriticalSection_Leave;
until Terminated;
end;

JSoftware
20-02-2008, 06:52 AM
Mirage, the problem is that there's no callback or anything which is called when the buffer has been finished. You have to poll it yourself. Calls to the OpenAL API are nonblocking to my knowledge.

Doublebuffering will help against studdering and silences between updates, but you still have to do the polling with regular timesteps

noeska
20-02-2008, 05:19 PM
i already use two buffers. whilst one is playing the other one is filled. with unque and que buffer the order of the buffers is altered.

But as jrsoftware says i have to poll the bufferstate for the source. there is no event for that in openal.

But as i found out sleep(1) works aswell. Just do not use sleep(0) as that locks up your pc.

@pyrogine you also use sleep. also i do not see you using an event as pointed out by mirage.

Pyrogine
20-02-2008, 05:51 PM
Yea, I was trying to say he was right to use a double buffer. Something happen (error message of some sort) and I then was not able to edit the post.... arrrg. I am doing polling too. I think you can enable a callback in DirectSound, but I've not yet had a chance to look into it. The polling has been working fine me. The Sleep seems to work good. The value that I came up with works well based on the buffer size. Smaller then this for the buffer size that I use then sound starts to overlap, larger then too much latency.

alexione
09-03-2008, 09:22 PM
Actually, if you use OpenAL, there IS a way to check for buffer's state. Here is very good lesson how to do streaming in C++:

http://www.devmaster.net/articles/openal-tutorials/lesson8.php

Pay special attention to function ogg_stream::update()

arthurprs
09-03-2008, 10:09 PM
sleep is good, don't worry ;)

Andreaz
10-03-2008, 06:38 AM
Sleep(Time > 0) may cause the thread to sleep for to long due to the window times of the scheduler (16ms on win if i'm not misstaken), a way that quake used that works really well even for unthreaded applications is:


UpdateTime:= GetCurrentTime;
repeat
Sleep(0);

CurentTime:= GetCurrentTime;
until CurentTime- UpdateTime >= UpdateTime;
[/pascal]

arthurprs
10-03-2008, 10:21 PM
Sleep(Time > 0) may cause the thread to sleep for to long due to the window times of the scheduler (16ms on win if i'm not misstaken), a way that quake used that works really well even for unthreaded applications is:


UpdateTime:= GetCurrentTime;
repeat
Sleep(0);

CurentTime:= GetCurrentTime;
until CurentTime- UpdateTime >= UpdateTime;
[/pascal]

it's precise but eats the cpu instead