Multithreaded Engine Design?
Hiya,
After looking at the feature lists for some high-end commercial game engines (Source, id Tech 4, CryEngine), I've noticed that most of them run their major subsytems on different threads. This has made me wonder about how you would go about doing this?
I was under the impression that it's generally better to design an engine as a collection of high-level components (e.g. AudioSystem, SceneGraph) which can be used by themselves if needs be, and that an all-encompassing 'Engine' class is too restrictive. But how can the engine designers run different components on different threads, if they don't have this 'Engine' class to control them?
I'm sure the engines above are very complex and don't easily fit into either of these designs, but could anyone give me some information?
Any info would be much appreciated [smile]
What's stopping you from creating compltely seperated AudioSystem and SceneGraph engines, then creating a higher Engine to contain them all?
For what it's worth, it's rarely ever advantageous to use multiple threads, barring networking and timer related tasks.
For what it's worth, it's rarely ever advantageous to use multiple threads, barring networking and timer related tasks.
Quote:Original post by Kest
For what it's worth, it's rarely ever advantageous to use multiple threads, barring networking and timer related tasks.
False. The Xbox 360 has 3 hyper-threaded cores, giving you access to 6 hardware threads. The PS3 has (IIRC) one hyper-threaded core and all those SPUs to work with. I don't even think you can buy a non-hyper-threaded single core PC any more, and dual and quad cores are becoming increasingly common, especially for gaming rigs.
By not taking advantage of threading, you are wasting vast amounts of CPU on modern systems, and if you're making a AAA quality game, you really can't afford that waste. In fact, I would say it's rarely ever advantageous not to use multiple threads any more (although, if you are not trying to make a AAA quality game, you may well be able to get away with a single-threaded architecture).
Quote:Original post by beebs1But how can the engine designers run different components on different threads, if they don't have this 'Engine' class to control them?
I'm not sure I see why this would be different. Obviously, somewhere you have some logic that controls, say, the audio component (initializes it, sends messages to it, tracks its lifetime). Why would it make a difference whether the audio component runs in its own thread or not?
That said, don't assume that commercial engines are particularly good examples of "good design".
Some of them might be, some of them are definitely not.
Sooner or later, you will have to deal with the multi core trend, particularly because the individual cores are actually going to become slower (the experimental 80 core Intel for example runs each core at 400 MHz [that is MEGAhertz not GIGAhertz]) so unless you want to waste a ton of CPU power, your application better make use of concurrency.
The problem with concurrency however is that it's complex. Concurrent programming is fundamentally different from non-concurrent programming...even more so than most programmers generally acknowledge. It's not so much about avoiding race conditions and deadlocks, it's about not killing the benefits of concurrency with unnecessary locking and about scaling. If you write your engine in a fashion that your core subsystems run on separate threads, your app may scale to four cores but what happens if next year Intel releases a 16 core or a 32 core CPU. And what if those cores no longer run at 3 Ghz but at just 1 Ghz? Although that new CPU technically has more processing power than say a 3 GHz quad core, your app will actually perform worse (this is not at all an unrealistic szenario if you believe hardware makers).
I think the only way to write a multi-threaded engine that makes good use of the available processing power and scales to basically any number of cores with a near-linear performance gain is to have a job system. The way this works is to have a pool of threads (the number of threads should correspond to the number of cores) that are given jobs to process by a thread manager. This manager keeps a queue (preferably a lock-free queue) of incoming jobs and distributes them to the individual threads once they're available (i.e. they've completed a previous job). For this to work, the jobs need to be completely isolated which means they can't access shared data (this corresponds to the functional paradigm). So when for example you need to do simulations for a particle system, a job would consist of a function that does the simulation along with all the data required by the function (= the input).
Now of course there isn't enough to do in most game engines to efficiently occupy say 32 cores... this is when it can make sense to actually process several frames in advance even if that means you have to copy data to avoid having to share state.
The kind of concurrent design I just described is apparently employed by the latest version of the source engine (they call it "HybridThreading"). Other engines such as the CryEngine or the Unreal Engine seem to follow the more conventional "one thread per subsystem" model which scales very poorly even on todays hardware. Bioshock (an Unreal Engine game) for example doesn't seem to occupy more than two cores which is quite wasteful.
The problem with concurrency however is that it's complex. Concurrent programming is fundamentally different from non-concurrent programming...even more so than most programmers generally acknowledge. It's not so much about avoiding race conditions and deadlocks, it's about not killing the benefits of concurrency with unnecessary locking and about scaling. If you write your engine in a fashion that your core subsystems run on separate threads, your app may scale to four cores but what happens if next year Intel releases a 16 core or a 32 core CPU. And what if those cores no longer run at 3 Ghz but at just 1 Ghz? Although that new CPU technically has more processing power than say a 3 GHz quad core, your app will actually perform worse (this is not at all an unrealistic szenario if you believe hardware makers).
I think the only way to write a multi-threaded engine that makes good use of the available processing power and scales to basically any number of cores with a near-linear performance gain is to have a job system. The way this works is to have a pool of threads (the number of threads should correspond to the number of cores) that are given jobs to process by a thread manager. This manager keeps a queue (preferably a lock-free queue) of incoming jobs and distributes them to the individual threads once they're available (i.e. they've completed a previous job). For this to work, the jobs need to be completely isolated which means they can't access shared data (this corresponds to the functional paradigm). So when for example you need to do simulations for a particle system, a job would consist of a function that does the simulation along with all the data required by the function (= the input).
Now of course there isn't enough to do in most game engines to efficiently occupy say 32 cores... this is when it can make sense to actually process several frames in advance even if that means you have to copy data to avoid having to share state.
The kind of concurrent design I just described is apparently employed by the latest version of the source engine (they call it "HybridThreading"). Other engines such as the CryEngine or the Unreal Engine seem to follow the more conventional "one thread per subsystem" model which scales very poorly even on todays hardware. Bioshock (an Unreal Engine game) for example doesn't seem to occupy more than two cores which is quite wasteful.
@Kest: I recommend dropping your 1995 thinking and advance to 2007 when 90% of CPUs are multicore or have hyperthreading. Unless you're building an old school game or refuse to modernize there is no point in not learning multicore techniques now, rather then playing a game of major catch up in a years time.
Well..... anyone have some tips links for where to get started with something like boos::thread or similar?
And also how hard is it to change a sizable program from single thread to multi thread?
And also how hard is it to change a sizable program from single thread to multi thread?
Quote:Original post by vs322
And also how hard is it to change a sizable program from single thread to multi thread?
Depends. You could just spawn half a dozen threads doing nothing much, and technically your program would be multithreaded.
The hard part is actually splitting up the workload so it can be executed in parallel. With an existing singlethreaded codebase, that might be quite tricky.
The problem is to isolate each task so much that it doesn't depend on other parts of your program, and so other parts of your program don't depend on it. In short, side-effects are bad.
For adapting an existing program to run multithreaded, I'd suggest looking at OpenMP, which VS2k5 (and, I think, GCC) support. That just requires you to insert a couple of #pragma's in your code, and it'll be (somewhat) parallelized without you having to completely overhaul your existing code.
Quote:@Kest: I recommend dropping your 1995 thinking and advance to 2007 when 90% of CPUs are multicore or have hyperthreading. Unless you're building an old school game or refuse to modernize there is no point in not learning multicore techniques now, rather then playing a game of major catch up in a years time.
Major catch up, eh? I haven't seen a lot of concrete multicore designs or architectures discussed, I would say its more beneficial to work on other aspects of your game, and let mp development mature some. Since when did multicore architectures gaurantee a good game? I know tons of games that aren't multi threaded and are fun to play. Multi threading is great, but if you're counting on that to make your game awesome, I wish you luck.
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement