Multithreading

Started by
8 comments, last by gwihlidal 16 years, 4 months ago
Hiya, I've been looking into ways of multithreading game systems, and I was wondering if someone could share their thoughts or experiences? I've found several ideas so far: The first is to let appropriate subsystems run independently on their own thread. When they've done processing (maybe simulating physics, streaming sound), they store a 'latest update', and start processing again. Other subsystems can grab this latest data when they need to, and continue their own processing without waiting. The problem I see with this is that a lot of data needs to duplicated. Has anyone tried this? Also, you'd still have to use a lock/mutex when you update the 'latest update'... The other idea is to thread each subsystem, at a lower level. The frame processing could proceed in a linear fashion, but each system will create it's own threads internally every frame to distribute the work. This seems like a good idea, but the overhead of creating and destroying many threads per frame seems very wasteful. I guess another way would be to use a mix of the two. What do you think? Has anyone tried either of these methods before, or used something entirely different? Thanks for any input [smile]
Advertisement
Well I am new on this forum and not really a game writter but I have written a good deal of multithreaded applications mostly in telecomms realtime stuff.
Mu/flags waits are fine but when dealing with several theads they can become tricky and just result in the old deadly embrace when everyone is waiting for something else to happen and the whole system just locks up.. I like using pipes/queues that are coupled to some OS managed wait mechanism, so you basically start up the thread (setup its priority) and hang a read on an empty pipe/queue, thus when you want it to wake up and do stuff just send it a message. Also doig it this way makes it real easy to debug stuff inasmuch as you can write the thread as a proccess initailly while you test it out.
Hope this helps IMK
Task-level parallelism seems to be all the rage these days, which is where threads are given queues of generic tasks to work on rather than being dedicated to specific sub-systems (IE a rendering thread, or a physics thread). What platform are you working on? If you're working in Windows, I can point you to some MS presentations you might find interesting.
Quote:Original post by beebs1
..let subsystems run independently on their own thread. When they've done processing, they store a 'latest update', and start processing again. Other subsystems can grab this latest data when they need to, and continue their own processing without waiting.

I went to a MS presentation recently that described a similar technique, they called it "double buffering". Here's the notes I took if you're interested (sorry they're JPEGs! It was easier than finding a host for the MSWord doc...)
MS and Sony also went on at length about the Task-level parallelism the MJP mentioned above. They create one thread for every CPU core the system has (or 2 thread for every hyper-threaded core), and then the engine just queues up lots of "tasks" to be handed out to each thread in turn.
Double Buffering
Thread pooling
Pooling tips from Sony
Doing data exchange between modules is one of the most complex parts of game program design. Adding synchronization complicates that a ton (in my limited experience).

I've only worked on hobby games, but in my experience, the best things to multithread are things that are self-contained in a module. Loading mechanisms, the 'server' part of a game, background music, pathfinding providers...
Quote:Original post by Telastyn
Doing data exchange between modules is one of the most complex parts of game program design. Adding synchronization complicates that a ton (in my limited experience).

I've only worked on hobby games, but in my experience, the best things to multithread are things that are self-contained in a module. Loading mechanisms, the 'server' part of a game, background music, pathfinding providers...

Emphasis on both of these points. Trying to synchronise multiple threads every frame is a mess, but they are well suited to background loaders, procedural generation, etc.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Quote:Original post by MJP
If you're working in Windows, I can point you to some MS presentations you might find interesting.


I am indeed developing for Windows. If you come back to this thread, any links would be very much appreciated.

Parallelizing tasks instead of entire subsystems is an interesting idea, and not one I've considered. I think I've heard of this, but called the 'thread-pool' method (IIRC)? It will take some thinking to design it in such a way that the tasks are handed out in the right order at the right times, and minimize the data that needs to be shared at any one time.

Well, thanks very much for all your replies!
I'd also appreciate the MS presentations.
Quote:Original post by swiftcoder
Emphasis on both of these points. Trying to synchronise multiple threads every frame is a mess, but they are well suited to background loaders, procedural generation, etc.

If you're writing your game for a 360, PS3 or a PC built in the last 12 months (Most new PCs are dual core now, soon quad core will be popular), then you don't have a choice but to start going fully multi-threaded and constantly using 2-8 threads for your game-logic... In other words, the free lunch is over.
You don't want to do little units of work on threads as, yes, the overhead is quite costly for "chatty" operations instead of "chunky" operations.

The more work you can push through a thread before requiring a sync with another thread, the better.

Threading tasks instead of systems is my prefered approach, since threading a system usually means you aren't thinking in terms of units of work, you are trying to force things to be threaded, even when not appropriate to do so.

Data loaders (especially on consoles) receive numerous benefits from being asynchronous, mainly because DVD (and HD) access is very very expensive and the game usually stalls if this part is a blocking call.

"Double buffering" is great if you can keep the memory footprint down. Also, critical sections are somewhat expensive, so stick to the "spin lock" approach if possible. Basically each thread has a frame counter (a volatile int32 basically), and whether or not it needs to wait for a different thread to catch up (or vice versa). The thread to wait just spins in an infinite loop (or returns out of a block of logic to continue processing unrelated logic) on the oppposing thread's counter until both threads are at the requested state, and then the "spin lock" is released and life goes on.

Hopefully that helped, and my spin lock summary wasn't too watered down for readability :)

~Graham

This topic is closed to new replies.

Advertisement