Allegro.cc - Online Community

Allegro.cc Forums » Allegro Development » New yield behaviour?

This thread is locked; no one can reply to it. rss feed Print
 1   2 
New yield behaviour?
Kitty Cat
Member #2,815
October 2002
avatar

I just saw this as listed for the 4.1.13 changes:

Quote:

Changed the behavior of yield_timeslice() to sleeping instead of just yielding.

This is a bad idea, IMO. This forces your program to rest even though it may not need to. For instance, in games where you're trying to get the most FPS possible, you'll still want to "play nice with multitasking" by yielding the CPU, but you still want as much of the CPU as possible (this is important when you're timing things). But now, you're forced to wait even if you don't need to, which introduces lag in programs that want precise timing (as precise as possible, anyway). It's been mentioned that you don't use yield_timeslice to give away the CPU, you use it to yield the CPU while you're waiting.

As an alternative.. what about changing yield_timeslice back how it was and making a new function that's like Sleep() in Windows? Where passing 0 would behave like 4.1.12 yield_timeslice, and passing 1 would behave as 4.1.13 yield_timeslice, ect?

PS: I'm posting this here because I'm not on the A4 dev list, and I don't really have time right now to join up just to say this. Besides, posting it here usually gets it through there anyway. And also, I'd like you guys' thoughts too. I personally think this was a bad move (making it force a wait, instead of only when needed), what about you?

--
"Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

Bob
Free Market Evangelist
September 2000
avatar

From the mailing list:

Quote:

Eric:
>>Apply your initial patch to mainline. The current yield_timeslice is pretty
>> useless for the average Allegro programmer and we strive for ease-of-use.
>> Moreover, there is a precedent with BeOS, QNX and MacOS X.
>>

Elias:
Exactly what I was thinking. (And we can always later add the al_sleep
anyway, with al_sleep(0) doing what the current yield_timeslice does,
for the small amount of users who actually need it.)

--
- Bob
[ -- All my signature links are 404 -- ]

A J
Member #3,025
December 2002
avatar

if its sleep its not yield, so the name is misleading. why would anyone want a function that does not perform its name ?

___________________________
The more you talk, the more AJ is right. - ML

Elias
Member #358
May 2000

You can see the discussion on the dev list here: [url http://alleg.sf.net/maillist.html] - click on [AD], then search for "al_sleep" (if it is working for you - else go to December 2003). I posted a patch which added a new function al_sleep(), leaving yield_timeslice untouched. But the part Bob quoted is why we then preferred to just change yield_timeslice.. most existing code already using yield_timeslice improves by the change, and on a couple of platforms it already worked like it does now anyway. It's purpose is also explained in the docs, so the name yield_timeslice is fully valid.

To get the old behavior back is of course tricky now - you have to do it in a system dependent way. I don't think there are many circumstances where you'd want that though - a vast majority of Allegro programs currently isn't using yield_timeslice at all. And in most of the ones using it, it got added to not waste unnecessary CPU power - what it never did, but does now.

--
"Either help out or stop whining" - Evert

A J
Member #3,025
December 2002
avatar

so we let the past dictate the future ?

Quote:

Moreover, there is a precedent with BeOS, QNX and MacOS X.

so a minority of systems, implemented incorrectly (probably due to someone just deciding sleep was 'good enough') can change the meaning of words?

Again i repeat "why would anyone want a function that does not perform its name ?"

if something has to be broken to get it right, then it should be done ASAP.

___________________________
The more you talk, the more AJ is right. - ML

Kitty Cat
Member #2,815
October 2002
avatar

Okay, I can understand this reasoning, that the majority of games designed that use yield_timeslice do so in a fashion to limit CPU usage, not to have the CPU just play nice.

However, I think this should've been handled by adding a new function to give up (as opposed to yielding) the CPU, instead of changing the current function (who's usage has been debated before, so it only adds to the confusion), and then adding another one later on to get the old functionality back.

BTW, wouldn't it have been simpler to just update the rest() function so that it maps directly to Sleep() in Windows, and the equivilants for other platforms? Though I don't know if Sleep(), and it's alikes, affect the whole process or just the thread it's called on.. for the former, I could see some problems if rest() was used in timers (which it shouldn't be), and interfering other threads..

But shouldn't something like this have been better to add, instead of changing yield_timeslice()'s behavior?

void al_sleep(int msecs)
{
#ifdef ALLEGRO_WINDOWS
  Sleep(msecs);
#elif defined ALLEGRO_UNIX // maybe just Linux?
  struct timeval tv = { 0, msecs*1000 };
  select(0, NULL, NULL, NULL, &tv);
#else
  // don't know how to handle it on this platform
  // just use
  rest(msecs);
#endif
}

--
"Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

Elias
Member #358
May 2000

Quote:

so we let the past dictate the future ?

Well, it just showed that the exact behavior of yield_timeslice wasn't clear at all. I see the 4.1.13 solution oriented more towards users (i.e. already written code keeps working or improves), than to enforce the original technical specification - which wasn't really clear anyway and didn't make sense to most. The implementation on some platforms just shows that it didn't make sense - users complained about the high CPU usage e.g. for the BeOS port, so Angelo adjusted yield_timeslice to make Allegro play nicely and only use CPU when it is needed. (So e.g. fans make less noise, or batteries last longer. Just yielding wouldn't do that, especially since yielding is done automatically by the preemtying scheduler in some OSes I think.)

Quote:

void al_sleep(int msecs)

That's exactly what my al_sleep was doing. And al_sleep(0) would have done what the yield_timeslice() in 4.1.13 does: Sleep the shortest possible time to have the CPU released. yield_timeslice wouldn't have changed on the main platforms.

--
"Either help out or stop whining" - Evert

A J
Member #3,025
December 2002
avatar

Quote:

exact behavior of yield_timeslice wasn't clear at all

void yield_timeslice(); On systems that support this, gives up the rest of the current scheduler timeslice. Also known as the "play nice with multitasking" option.

i found this in alg 4.1.9 and think it explains itself well. it doesn't mention CPU usage, as its not a CPU related function, its a scheduling related function. It explains exactly what the function name describes. its perfect!

Quote:

or improves

sorry, but a sleep does not improve performace at all, instead it now gives you less CPU.
as apps that use allegro tend to be games, they typically want as much CPU as possible.

i think al_sleep() needs to be implemented ASAP.
and yield_timeslice() should do exactly what its name says and not something else.

___________________________
The more you talk, the more AJ is right. - ML

X-G
Member #856
December 2000
avatar

A J, you do realize that on most if not all platforms Allegro supports, al_sleep(0) and yield_timeslice() would do the same thing?

--
Since 2008-Jun-18, democracy in Sweden is dead. | 悪霊退散!悪霊退散!怨霊、物の怪、困った時は ドーマン!セーマン!ドーマン!セーマン! 直ぐに呼びましょう陰陽師レッツゴー!

gillius
Member #119
April 2000

I agree strongly with AJ. yield_timeslice should not sleep for any amount of time. It should perform a yield and nothing more than that. If the current platform does not support yielding, then yield_timeslice should do nothing.

To make it sleep for a period of time makes it go against not only its old behaviour but to go against its name, that any programmer familiar with multithreading will instantly know what a "yield" function will do, and that yield is most definitely different from sleep.

Edit: I'm confused.

In Windows, Sleep(0) yields your timeslice, but doesn't actually sleep. In Windows, does yield_timeslice do a Sleep(0) or a Sleep(x) where x > 0?

Gillius
Gillius's Programming -- https://gillius.org/

Elias
Member #358
May 2000

Previously, it was Sleep(0), now it is Sleep(1) in 4.1.13.

The advantage is, code previously sitting in a loop executing millions of yield_timeslice() calls will now not eat up all the CPU. In fact, it will only call it once per timeslice in Windows and most Linuxes, using effectively no CPU at all. So this is very good to have.

On the other side, code who wants to use as much CPU as possible, and still wants other apps to run, needs yield_timeslice. (Actually - I don't quite understand why.. some schedulers obviously can do this automatically. In fact, I always thought preemptying does just that..)

Anyway, just providing an al_sleep() isn't a solution.. since there's no standard minimum time you have to sleep to drop CPU usage. OTOH, Allegro has no way to wait for events - so we need a way to sit in a loop, and have callsto wait for this system specific minimum time. In 4.1.13, yield_timeslice() was modified to be the function to do this, since it was already used like that in most code using it. My original idea was to have al_sleep, and make al_sleep(0) do this.

--
"Either help out or stop whining" - Evert

Kitty Cat
Member #2,815
October 2002
avatar

Forget it. My g.d. browser crashed two times while I was trying to write a lengthy reply. >:(>:(

Basically, I agree with what A J is saying. yield_timeslice says nothing about CPU usage. And yielding to the timeslice scheduler is different than resting the program (although resting the program implies a timeslice yield, it does more than that). IMHO, al_sleep should map directly to Sleep (al_sleep(0) -> Sleep(0)), or equivilant for the platform. Yeah, it'll be more work for the developers having to change yield_timeslice() to al_sleep(x) and d_yield_proc to d_sleep_proc, but the focus should be on end-user programmers. IMHO.

--
"Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

CGamesPlay
Member #2,559
July 2002
avatar

Also with 4.1.13, there is a disabling of screensavers? Can you, like, take that out until you can make an API call to turn it off? I'm trying to make a utility right now, not to mention many other apps I've written using Allegro.

[edit]
Also

docs/build/msvc.txt said:

When using a statically linked library, you must define the preprocessor
symbol ALLEGRO_STATICLINK before including any of the Allegro headers and
link your program against Allegro and the main Win32/DirectX libraries
in that order

As it's MSVC, the order isn't necessary. And unless something changed, you don't need to do the that at all (link in those libs).

--
Tomasu: Every time you read this: hugging!

Ryan Patterson - <http://cgamesplay.com/>

A J
Member #3,025
December 2002
avatar

what topic are you on CGamesPlay ?

back to the yield issue..

Quote:

he advantage is, code previously sitting in a loop executing millions of yield_timeslice() calls will now not eat up all the CPU.

lets say :

while ( game_loop )
{
   do_game();
   yeild_timeslice();
}

the old way (Sleep(0)/real yeilding) would work just fine, giving the OS a chance to do its thang if it needed. however the new way, Sleep(>0), would give you less CPU. lets say your do_game() takes 30ms to process a frame, thats 1000/30=33fps, which is good! but the yeild now sleeps(1) on Win32 which actually sleeps closer to 10ms.. you have 30ms/frame + 10ms sleep = 40ms/frame = 25fps.. so now your app has just lost 8fps. thats like 25% performace decrease.

___________________________
The more you talk, the more AJ is right. - ML

Bob
Free Market Evangelist
September 2000
avatar

Quote:

so now your app has just lost 8fps. thats like 25% performace decrease.

And if the OS actually had things to do in the background, then you'd still run slower. Point is - if you don't want to let go of the CPU because you're in some time-critical portion of the code, then don't call yield_timeslice()!

--
- Bob
[ -- All my signature links are 404 -- ]

A J
Member #3,025
December 2002
avatar

on win32, a search on the allegro src code failed to find timeBeginPeriod() so i presume a Sleep(1) actually does a sleep(10).
if this is the case, we have another problem.

bob, i think your avatars frown is warranted. ;)

___________________________
The more you talk, the more AJ is right. - ML

Kitty Cat
Member #2,815
October 2002
avatar

Quote:

if you don't want to let go of the CPU because you're in some time-critical portion of the code, then don't call yield_timeslice()!

I agree with this. However, yield_timeslice is letting go of the CPU for too long, now. If you're in a waiting loop, you have some flexibility. If it's alright that you don't get back immediately, then sleeping is fine, but when things are a bit more critical in a waiting loop, where you'll want to return asap in case you need to break, you'd use yield_timeslice.

while(playing)
{
  while(waiting_for_frame_tic)
    yield_timeslice();

  do_frame();
}

Originally, this would return immediately if nothing is waiting, or it would let the other programs run, and then it would get back. But now, this sleeps a minimum of ~10ms regardless of what else is going on.

It just seems to me that making yield_timeslice() sleep and relinquish the CPU is just a hack to lower CPU usage. But with proper usage of yield_timeslice, other programs wouldn't suffer any less than if you force giving the CPU away. And the only real reason to lower CPU usage is for laptops and power consumption, and if that was the case, why not make Allegro startup a thread that periodically calls Sleep/al_sleep(1) to slow the process? Then it wouldn't rely on the user structuring his program so that it would have to loop a call to anything.

Although, even if this change should've been done, I think al_sleep() should've been implemented first, or at least at the same time. Then the dev team could've told us to change our programs to match the new behavior (so we could get the old behavior back). But for now, we're stuck with how it is, until al_sleep is implemented.

--
"Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

Elias
Member #358
May 2000

Quote:

It just seems to me that making yield_timeslice() sleep and relinquish the CPU is just a hack to lower CPU usage. But with proper usage of yield_timeslice, other programs wouldn't suffer any less than if you force giving the CPU away. And the only real reason to lower CPU usage is for laptops and power consumption, and if that was the case, why not make Allegro startup a thread that periodically calls Sleep/al_sleep(1) to slow the process? Then it wouldn't rely on the user structuring his program so that it would have to loop a call to anything.

It is a bit hackish, yes - ideally your code example would tell the OS to get back to the program in the moment the timer is changed. But with Allegro's architecture, this doesn't seem easy. And I assume, such a waiting function would have the same 10ms accuracy problem, at least in Windows and some Linux. Running another thread to call Sleep() would just make that thread sleep, so wouldn't help at all. And slowing down Allegro in any way is not what we want - just the possibility to give up CPU time, but only if the user wants to do so.

About the power consumption.. I expect Doom 3 to take 100% CPU. But if I start lxdoom here, it takes only a few % of CPU - which makes sense, given it could run on a <100MHz computer with full framerate. My platformer who runs on a Pentium-200 with its maximum 60 FPS is the same.. it simply should only take about 1/5th of the CPU on a processor 5 times as fast. And any application made with Allegro taking CPU while idle just feels wrong all the same. The CPU heating up also can have ugly side effects, like fans rotating faster and causing too much noise to hear the sound in the game :P

So, I don't see a way around a function which does what the 4.1.13 yield_timeslice does. al_sleep(1) alone wouldn't be so good, since we don't know how the scheduler works on the current platfoem, and what exactly it does in response to a 1ms sleep. (But al_sleep(0) could work.) The question is just, if you have code which is so time critical, why would you call yield_timeslice() at all? Just run your game which takes 100% CPU.. there's no OS which will stop responding to user input because of that (I hope). And for some special uses, you'd need a platform specific solution.

In any case, I don't mind al_sleep(), with al_sleep(0) doing what 4.1.13 yield_timeslice() does. And al_sleep(x) just calling Sleep(x).

--
"Either help out or stop whining" - Evert

Kitty Cat
Member #2,815
October 2002
avatar

Quote:

In any case, I don't mind al_sleep(), with al_sleep(0) doing what 4.1.13 yield_timeslice() does. And al_sleep(x) just calling Sleep(x).

But again, this feels hackish. As you said, you don't know how the scheduler works, and that would make the behavior of al_sleep(0) indeterminate. What about something like this, then:

yield_timeslice() -> Yield to the scheduler, and return ASAP. Don't rest.
al_sleep(x) -> rest the thread at OS level (if possible), else just call rest(x).
al_idle() -> return the CPU to the OS (hopefully reducing CPU usage), and wait.
             Time spent idling would depend on the preciseness of the OS, current
             time in the slice, other processes' CPU usage, ect.

--
"Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

Elias
Member #358
May 2000

Yes, I meant, al_sleep(0) would do what your al_idle() does. Maybe we don't need al_sleep at all, and just al_idle.

EDIT: rest() can already be used to wait a speceific amount of time. And rest() than would call al_idle() instead of yield_timeslice() as it does currently.

--
"Either help out or stop whining" - Evert

Kitty Cat
Member #2,815
October 2002
avatar

Quote:

rest() can already be used to wait a speceific amount of time. And rest() than would call al_idle() instead of yield_timeslice() as it does currently.

That sounds good to me. Though then rest() would need a way of timing al_idle to make sure enough time has passed, since there'd be no gaurantee of how long al_idle will wait. I timing interrupt/thread would be a Very Bad Idea for this, IMO.

EDIT:
Using clock as an example (OS-specific calls should be used.. (u)clock isn't reliable in DOS once install_timer() is called):

void rest(int msecs)
{
  clock_t start = clock();

  while((start - clock()) / (CLOCKS_PER_SEC / 1000) < msecs)
    al_idle();
}

Would also be a good incentive for a al_get_msecs() function..

--
"Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

Elias
Member #358
May 2000

Yes, I meant just that, rest() could use al_idle() to not do busy waiting.

And yeah, in order to not use a timer "interrupt" for timing, we'd need al_get_msecs() - which has been on my wishlist for a while as well. Except for DOS, al_get_msecs() could simply call the corresponding OS function. In DOS, we'd of course keep using the timer, since it's 100% accurate there anyway.

--
"Either help out or stop whining" - Evert

A J
Member #3,025
December 2002
avatar

lets not have a function with an abstract meaning.
al_idle() could mean anything.. what is idle ?
its too abstract; and there is the problem.

lets just have yield_timeslice() that does exactly what its name implies.

___________________________
The more you talk, the more AJ is right. - ML

CGamesPlay
Member #2,559
July 2002
avatar

The second part of my post is just something I didn't notice before in the docs. The first part is something I know is new to 4.1.13, and since this is a thread about 4.1.13, I thought this would be a good place for it.

--
Tomasu: Every time you read this: hugging!

Ryan Patterson - <http://cgamesplay.com/>

Kitty Cat
Member #2,815
October 2002
avatar

Quote:

lets not have a function with an abstract meaning.
al_idle() could mean anything.. what is idle ?

It idles the Allegro program.. where as yield_timeslice would, correctly, only yield the current timeslice if needed, al_idle would idle the program so all other processes (including those with idle priority, like the system idle process in Windows) get some time, whether they need it or not.

--
"Do not meddle in the affairs of cats, for they are subtle and will pee on your computer." -- Bruce Graham

 1   2 


Go to: