Sign in to follow this  
beebs1

Windows Fibers

Recommended Posts

Hiya, I've been looking into Windows fibers, and thinking about how they could be used for tasks like AI and scripting in a game. It all seems pretty cool, each AI agent could run from it's own fiber, or perhaps each script. I've never used fibers before though, so I'm just wondering what you folks think about them? Are they suitable for this? I'm also wondering if there are any performance increases to be gained from using fibers? Anyhow, I'd like to implement a fiber-based system component for my project, mostly because I've not used them before, but that's not really a good enough reason on it's own... Any opinions are very welcome, thanks [smile] James.

Share this post


Link to post
Share on other sites
Quote:
Original post by beebs1
Hiya,

I've been looking into Windows fibers, and thinking about how they could be used for tasks like AI and scripting in a game. It all seems pretty cool, each AI agent could run from it's own fiber, or perhaps each script. I've never used fibers before though, so I'm just wondering what you folks think about them? Are they suitable for this?

I'm also wondering if there are any performance increases to be gained from using fibers?

Anyhow, I'd like to implement a fiber-based system component for my project, mostly because I've not used them before, but that's not really a good enough reason on it's own...

Any opinions are very welcome, thanks [smile]
James.

Fibers are a thing of the past. They are emulated threads that were nearly useful at the time of Windows 3.0. They cost a lot to manage (meaning that you'll get no performance gains), and are inferior to threads in many ways. The 11th commandment is "Thou shall not use fibers".

Use plain threads instead (or give a look to Intel TBB 2.0).

Share this post


Link to post
Share on other sites
Quote:
Original post by Emmanuel Deloget
Fibers are a thing of the past. They are emulated threads that were nearly useful at the time of Windows 3.0. They cost a lot to manage (meaning that you'll get no performance gains), and are inferior to threads in many ways. The 11th commandment is "Thou shall not use fibers".


Aren't they basically coroutines? From what I've read about coroutines, they're used for different purposes than threads - when you want logically distinct control flows but don't want them to run in parallel. It's a pity they're not a portable part of most programming languages.


Share this post


Link to post
Share on other sites
Quote:
Original post by Emmanuel Deloget
Fibers are a thing of the past. They are emulated threads that were nearly useful at the time of Windows 3.0. They cost a lot to manage (meaning that you'll get no performance gains), and are inferior to threads in many ways. The 11th commandment is "Thou shall not use fibers".

Use plain threads instead (or give a look to Intel TBB 2.0).

I agree with most of what you say but according to the MSDN, fibers have only been around since Windows 98 and NT 3.51 SP3.

Also, Raymond Chen wrote a small series which used fibers a few years back. In part 3 he mentions some of the hazards of fibers if used improperly (and why they are difficult to use properly).

Share this post


Link to post
Share on other sites
Also, always remember that a thread does not necessarily mean parallel execution. You can only execute 1 thread simultaneously per core in your CPU (most have 1 core, the Core2 and other dual-core machines have 2, and some have 4). That's it for mainstream consumer CPUs.

That means that you'll never get a performance benefit from using more threads than your have cores. It costs performance to switch between threads. So the idea of each unit on it's own thread is an understandable idea (and a common one) but in practice is a very bad one.

You should certainly endeavor to make use of all existing cores. And threads can be useful to give the illusion to the player that things are happening at the same time (i.e. run audio in it's own thread, as well as networking). It just means that to a human things appear to happen simultaneously; to the computer it's just doing one thing for a while then switching context and doing another thing for a while.

-me

Share this post


Link to post
Share on other sites
Quote:
Original post by Emmanuel Deloget
Fibers are a thing of the past. They are emulated threads that were nearly useful at the time of Windows 3.0. They cost a lot to manage (meaning that you'll get no performance gains), and are inferior to threads in many ways.
That's so wrong in so many ways! Fibers/coroutines are not "emulated" threads and they are not inferior to threads; fibers address a completely different need. Fibers are cooperative, threads are preemptive. In that they're cooperative, fibers are arguably the most lightweight mechanism for multitasking: you save/restore registers, switching to a different stack inbetween. Threads typically need to issue kernel/OS calls to switch, and are much more heavyweight. Fibers are very much not a thing of the past!

Quote:
Original post by beebs1
I've never used fibers before though, so I'm just wondering what you folks think about them? [...] I'm also wondering if there are any performance increases to be gained from using fibers?
Fibers are cool and yes there are performance increases to be had, potentially. Consider a system with a single execution unit (i.e. no hardware threading, or just one of several hardware threads). In situations where you would normally issue a synchronous call that you would have to wait for, you can instead issue the call asynchronously, switch to another fiber, and allow the system to continue executing. You can later Yield() back to the routine where you issed the asynchronous call, when the call has completed. No cycles went to waste, other than the fiber switch. If the cost of switching fibers is less than the cost of waiting synchronously, you're ahead.

You could of course issue the call asynchronously and poll for its completion manually in your code, but a fiber system is much more elegant and more user-friendly.

Threads and fibers can with benefit be used simultaneously. For instance, you start up as many threads as you have multiple hardware execution units. Then, within each thread and hardware unit you manage tasks with fibers rather than with threads. With the threads you make sure to utilize all hardware units, with the fibers you make sure to utilize "all" cycles within that hardware unit (instead of stalling for synchronous calls, RPC calls, etc).

Share this post


Link to post
Share on other sites
I looked into fibers a while back, but found them redundant (thread stack per fiber).

I implemented my own cooperative multi-tasking model through the use of threads and async message passing (or non-blocking method invocation). I have micro-threads, which are just normal objects, and a thread pool, where each thread serves a subset of objects.

There are a few gotchas to look out for - infinite loop in one micro-thread will cause that entire worker thread to stall. But, this is only possible with a for/while loop, since all communication between threads is asynchronous, so an infinite loop made this way is quite common to ensure periodic processing.

One thing it does require though, and where the most complexity comes from, is lock-less queue for passing function calls between threads. Locking in this case would kill performance.

The resulting model has little to no performance penalty (in single thread, performance is just a tad slower than usual function pointers, like boost::signal), cross process communication is limited by characteristics of the queue, usually 10 times slower.

But - there are just about no problems running 1 million such micro-threads, and the whole system scales arbitrarily, just define a number of worker threads.

Share this post


Link to post
Share on other sites
Quote:
Original post by Palidine
[...]That means that you'll never get a performance benefit from using more threads than your have cores.[...]
There is an important distinction to be made: you'll never get a performance benefit from using many more active threads than you have cores. There are many situations where using more threads than cores is a good thing, such as i/o threads (especially I/O completion ports) that will block and not actually do anything on the cpu until the system finishes some significant task. It can make a great deal of sense to have something like N+2 threads for blocking tasks where N is the number that can actually run simultaneously. Some book on windows gives a complete argument and explanation for the fact, but I can't remeber which.

Share this post


Link to post
Share on other sites
Also, even if your 4-thread system is a little slower than optimal on a single core system, it scales to a 2/4 core system whereas your single-threaded version doesn't.
And of course, using threads makes some things just a lot easier. In applications programming we use loads of threads for things like DB access, remote calls, network stuff, when the main system must be highly responsive.

Share this post


Link to post
Share on other sites
Bit of a shame perhaps, but I think you'd have to look far and wide to find a single person who has ever used fibers in a game.
As great as they may seem, good luck finding people to help you when you run into trouble. The coolness factor just doesn't cut it for me.

Share this post


Link to post
Share on other sites
I agree with Christer [EDIT: and Antheus] here that cooperative multitasking could be very useful. And in fact, similar ideas and approaches get employed extensively in resource constrained environments. For instance in Symbian platform one could regard the equivalent to Windows fibers be ActiveObjects:
The Active Object concept is a specificity of the Symbian Operating System that allows you to handle several asynchronous requests "at the same time" and in the same thread. It can be also defined as a kind of cooperative scheduling framework.

There are benefits in using those, but the benefits will get negated in some circumstances, like enough available hardware threads.

[Edited by - Naurava kulkuri on August 17, 2007 7:59:59 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Christer Ericson
Quote:
Original post by Emmanuel Deloget
Fibers are a thing of the past. They are emulated threads that were nearly useful at the time of Windows 3.0. They cost a lot to manage (meaning that you'll get no performance gains), and are inferior to threads in many ways.
That's so wrong in so many ways! Fibers/coroutines are not "emulated" threads and they are not inferior to threads; fibers address a completely different need. Fibers are cooperative, threads are preemptive. In that they're cooperative, fibers are arguably the most lightweight mechanism for multitasking: you save/restore registers, switching to a different stack inbetween. Threads typically need to issue kernel/OS calls to switch, and are much more heavyweight. Fibers are very much not a thing of the past!

Well, ok, I've been a bit more extreme than needed, and my wording was a bit imprecise. Fibers are cooperative tasks that are handled in user space, while threads are usually understood to be preemptive tasks that are handled in kernel space (although this is not true for every OS thread implementation out there). That's why I said that they are basically emulated threads (they look like threads from a design point of view) and they are inferior to threads in many way (because they are cooperative tasks). The only good point is that it does not go to the kernel when switching contexts (or during init and destruction), but on the other end you have to cope with much more management (tracking the running fibers, schedule them and so on).

What are the use cases of Fibers? Anderson Bailey from AMD says:
Quote:
This code reveals one of the shortcomings of fibers: they do not operate in parallel. At any given time, only one fiber associated with a given thread can be running. In other words, fibers on a single thread execute sequentially. This can be an advantage, in situations where sequential execution is desired.

Nothing prevents you from setting up fibers on multiple threads, so that you achieve parallelism that way. However, a fiber can be associated with only one thread, so there are limits to this approach.

However, fibers have some advantages over threads, beyond your ability to schedule them: they are cheap to create and destroy. Whereas threads require expensive kernel calls to create, fibers are built in user space very inexpensively. They are ideally suited when multiple different tasks have to be done by one thread, but no more than one can be done at a time. In such cases, they work beautifully and the control they offer can be a real boon.

To conclude by reiterating an opening point: Fibers are not a silver bullet, and I am not recommending them as a replacement for threads. However, they can be a useful alternative, particularly when you are working with or porting applications that are designed to schedule their own threads. Add fibers to your toolbox, and you'll be better for it.


Emphasis is mine.

So my opinion is that they are inferior to threads in many way, and are a thing of the past (especially with the rise of multicore CPUs and alternative solutions; for local multithreading, I'd prefer an OpenMP solution when applicable, and if not then a thread will do the job just fine. Fibers appears to be a less scalable solution).

I encourage everyone to challenge this opinion (like any opinion I have, it exists only to be changed). Christer's example is possibly a valid use - although the same implementation using a thread would lead to other benefits - the thread not being ran when it sleeps, you save one heavy context switch while the other threads are working. So in the end, one might have to profile in order to decide for the best strategy.

One of the biggest shortcomming IMHO is that terminating the current running fiber kills the thread to which the fiber is attached. This makes handling fibers a bit more difficult to manage than threads and can introduce a fair overhead if you want to keep a complete control on this situation (depending on your code architecture, you might be forced to implement a full blown scheduler which is likely to be slower than the normal thread scheduler (including task switching)). Another shortcomming is the fact that not all registers are saved - meaning that if for one reason or another a modified register is not saved, you're going to face a debugging nightmare. For instance the floating point registers (and, of course) the MMX/SSE state are not saved/restored:

Quote:
Fiber implementation save MMX registers since Windows Server 2003
The ConvertFiberToThread API undoes the effect of ConvertThreadToFiber. Once called, no more fiber functions can be called on the thread. The remaining two APIs listed in Figure 3 are just "Ex"-tended versions of existing APIs. CreateFiberEx is just like CreateFiber, but with the ability to specify the stack reserve size. ConvertThreadToFiberEx is interesting, though. In the original fiber implementation, the floating point, MMX, and SSE registers weren't saved and restored across fiber switches to improve performance. The new API lets you specify that these registers need to be saved and restored as well.

ConvertThreadToFiberEx() is not available on Windows XP. This severly limit the use of Fibers if you plan to perform any kind of floating point or SIMD computation.

BTW I didn't said that Fibers were around at the time of Win 3.0 - I was refereing to the cooperative nature of Win 3.0 multitasking. Ok, wording was poor... Sorry for the added confusion.

Share this post


Link to post
Share on other sites
I still wouldn't say they were 'inferior' to threads; infact the AMD quote serves to show they a designed to handle a different set of problems than threads.

While you might well have the scheduling of them to deal with unlike with a thread you have complete control over what fibre is swapped in next to execute. In some situations this level of control is desirable.

Consider, for example, an AI agent; the main 'think' could be written as a fibre, with the master AI thread restarting each fibre as required to resume it's processing. This is going to be considerably lighter and more controlable than a thread for every AI agent in the world, which would probably do very bad things due to all the crossing to and from kernel mode to swap threads as well as remove the control over which thread gets executed next.

Really, Fibres are not unlike co-routines in Lua (or whatever Python uses), designed to maintain a stack and call state while allowing their execution to be suspended.

Share this post


Link to post
Share on other sites
Fibers are far from a thing of the past. In fact, they are being used more and more these days. It's well documented how EVE online used stackless python and its Tasklets/Microthreads to simplify development significantly. Supreme Commander uses lua and forks microthreads from their unit scripts to handle asynchronous game logic. Doom3/Quake4 engine uses a custom scripting system with essentially fiber support built in, much like Game Monkey Script has.

Fibers are not so much competition with threads as they are useful for very different things. If your quest scripts in your RPG could be written serially within a single function for example, or some AI logic written the same, that simplifies the script alot(both size and complexity), reduces the script running time, and ultimately makes things much easier and faster to develop for.

I've never used windows fibers, only GM script, and never before would I have been able to get as much logic into script as easily and with the least amount of overhead as with using the GM threads(fibers).

edit: So it sounds like windows fibers themselves are rather limited and quirky by the explanations of Emmanuel Deloget, so I'm basically expressing their usefulness in general

[Edited by - DrEvil on August 18, 2007 4:21:28 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Emmanuel Deloget
That's why I said that they are basically emulated threads (they look like threads from a design point of view) and they are inferior to threads in many way (because they are cooperative tasks). The only good point is that it does not go to the kernel when switching contexts (or during init and destruction), but on the other end you have to cope with much more management (tracking the running fibers, schedule them and so on).
Again, fibers are not inferior to threads. That's like saying apples are inferior to oranges! Also, your insinuating that cooperative multitasking is somehow inferior to preemptive multitasking, but that too is wrong as a generic statement. Cooperative multitasking is superior e.g. when you need lightweight task switches, or when you want switching to happen as soon as possible (to best take advantage of a warm cache, for example). Preemptive multitasking is superior e.g. if you have tasks that can't be expected to cooperate or may crash (and you don't want to bring down the system when they do crash).

Yes, you may have to schedule your fibers yourself (but that depends on what fiber library you're using, some already have a scheduler) but that's about it as far as additional complexity goes. "Much more management" is an exaggeration.

All your comments seem to be regarding the standard Windows fibers calls (which is fine, of course, because that is the name of the thread after all) but there are several other fibers/coroutine libraries available for Windows (and other OS's), including

* Libtask (http://swtch.com/libtask/)
* coro (http://www.goron.de/~froese/coro/coro.html)
* libcoroutine (http://www.dekorte.com/projects/opensource/libCoroutine/)
* Portable Coroutine Library (PCL) (http://www.xmailserver.org/libpcl.html)

all with different characteristics. (Of these, I like Libtask and coro the best, although admittedly I haven't explored all libs in detail.)

My comments are about fibers in general, but also in context of their applicability to a machine like the PS3, where you may want to consider running fibers on the PPU, to allow a hardware thread to continue running while one or more SPUs are processing some task that the PPU needs the result of (such as collision queries, line of sight, path finding, etc).

While you could get better performance rewriting your game code to batch queries, using fibers as I suggested in my previous post (and the previous paragraph) is a minimally intrusive way of giving gameplay coders the ability to utilize the parallelism of a system like the PS3.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this