Threading

Jones
Unregistered
 
Post: #1
I'm currently working on some functions that I realized are going to be a big slowdown in a game, or at least I think they might. Stuff with nested while loops, etc. etc. For such proccesses, would it be useful to use the magic of "hyperthreading", or whatever Apple's equivalent is.

I've never really looked into it before, but I know SDL can do it, or something like it. How easy is it to implement? Is it really that beneficial?

Thanks!
Quote this message in a reply
Member
Posts: 86
Joined: 2005.01
Post: #2
SDL threads are actually quite easy to use (they are a direct ripoff of the Windows API's thread management routines). The SDL website has a lot of documentation on it, and you can see here (assuming I didn't duff the bbcode) for some examples.

Threading is most beneficial if you have a lot of stuff running in the background; with intelligent use of mutexes you can do things like process world events, preload maps and prepare data in the background for future rendering. It's more or less critical if you are using blocking sockets in your networking code.

Games are still largely single-threaded applications, but as time goes on (especially with the new multicore consoles) someone will probably find new and interesting game-engine designs that scale to a multithreaded design much better.

Newest game: Glow, a sci-fi RPG with lots of zombie bashing. Get it: OS X
Quote this message in a reply
Sage
Posts: 1,403
Joined: 2005.07
Post: #3
pthreads are totally easy to use and stuff, just thought i'd say that.

Sir, e^iπ + 1 = 0, hence God exists; reply!
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #4
Yeah pThreads (POSIX threads) are real easy. I would imagine that SDL is probably basing their threads on them underneath as well. The most basic threading mechanism on OS X are Mach threads (kernel threads). pThreads are based upon Mach threads. Actually, everything is based upon Mach threads on the Mac. However, Mach threads are only recommended for low-level stuff like kernel extensions. It's best to use something higher-level like POSIX or NSThreads or Carbon Multiprocessing Services.

I've been toying with a new project lately that uses threading -- the main thread for OpenGL rendering, and a second thread for the simulation updating. So far it is working really well. I read somewhere that, on average, the simulation and the rendering each take up fifty percent of the processing. I haven't been able to test how close that is to reality yet, but it's nice to finally be using that `other' processor for something.

There are two major restrictions to threading that I've found: One, if you're using Cocoa you can only practically call Cocoa from the main thread since it is largely non-reentrant. Simple things like calling NSDate seem to work just fine however. Two, OpenGL can only be called from the main thread as well since it is also pretty much non-reentrant. But for the simulation thread (the update thread), you can do pretty much everything else like loading files and updating the positions of objects, collision, physics, etc.
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #5
Premature optimization is the root of all evil. Don't randomly throw in threads just because you think you might need them.

Quite frankly Jones, you're having enough trouble with the basics of C. Leave threads alone for another year or two until you get a handle on single-threaded programming. Multi-threaded programming is orders of magnitude harder.

Jake: OpenGL contexts are not thread-safe, but OpenGL is. OpenGL contexts are current per-thread. It's perfectly possible to call GL from a thread other than the main one; just make sure that your context is current in that secondary thread, and that no other thread touches the context simultaneously.
Quote this message in a reply
Member
Posts: 86
Joined: 2005.01
Post: #6
OneSadCookie Wrote:Jake: OpenGL contexts are not thread-safe, but OpenGL is. OpenGL contexts are current per-thread. It's perfectly possible to call GL from a thread other than the main one; just make sure that your context is current in that secondary thread, and that no other thread touches the context simultaneously.

Is there a way to lock the context with a mutex? It seems that would slaughter performance.

Newest game: Glow, a sci-fi RPG with lots of zombie bashing. Get it: OS X
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #7
ravuya Wrote:Is there a way to lock the context with a mutex? It seems that would slaughter performance.
In 10.4 you can use CGLLockContext and CGLUnlockContext. You have to do it for every single (gl* and CGL*) call in every single thread, which is a big, fat, PITA if you ask me...

OpenGL Programming Guide for Mac OS X describes how to do threading with OpenGL. Most of what I gathered from it is that it's best to keep your OpenGL calls in the main thread (I say `main', but it could be another), and your data manipulation in another, which is what I'm doing. Specifically, I'm following the model shown in figure 11-1. As OSC pointed out, it also says, "OpenGL commands for a specific context are not reentrant.", and additionally, "the OpenGL API is not inherently thread-safe." I guess you can take that however you want. To me it means: Don't bother. They do explain how to do it if you want to get crazy.
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #8
There's no performance-killing here, and very minimal locking. You're writing the app; you know when which threads are going to do GL stuff. Generally the only time you'll need to lock is when resizing the window in the main thread. Your code might look like this:

Code:
// main thread's resizing
CGLContextObj context = [[self openGLContext] CGLContextObj];
CGLLockContext(context);
[self update];
CGLUnlockContext(context);

// secondary thread's loop
while (1) {
    // non-GL setup
    CGLLockContext(context);
    // drawing
    CGLUnlockContext(context);
}
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #9
You know, I guess that doesn't seem so bad. The guide gives me the impression that it's not as good of an idea as clearly dividing the labor between a gl-calling thread and a non-gl-calling thread.

But you're saying that you'll only need to worry about locking during resize. I'm a little confused about that. Wouldn't you just lock in the main thread regardless? That is, assuming you need to do so because you're calling gl in another thread as well. Why would drawing during resize be the only time to need locking?
Code:
- (void)reshape
{
    bounds = [self bounds].size;
    [[NSOpenGLContext currentContext] update];
}

- (void)timerCallback
{
    [self setNeedsDisplay:YES];
}

- (void)drawRect:(NSRect)rect
{
    [self mainThreadRenderFrame];
}

- (void)mainThreadRenderFrame
{
    [NSThread detachNewThreadSelector:@selector(otherThreadRenderFrame)
                        toTarget:self withObject:nil];
    CGLLockContext(context);
    // drawing
    CGLUnlockContext(context);
}

- (void)otherThreadRenderFrame
{
    CGLLockContext(context);
    // drawing
    CGLUnlockContext(context);
}

[adding] I think the code that I just listed looks pretty simplistic and doesn't really tell the story as it would be in reality. There would be a lot more going on in each thread than *just* drawing the whole time, which would give the other thread the opportunity to draw while one is doing non-drawing tasks.

[adding 2] I can't think of a way to synchronize drawing in two threads to the same frame (or before the same flush) if they were on separate timers, which is why I show it as detaching a separate thread every frame here. You'd still have to set a flag so that the main thread would know to wait to flush until the secondary thread finished -- would they call that a semaphore (I'm still relatively new to threading)?
Quote this message in a reply
Jones
Unregistered
 
Post: #10
Thanks all you guys. I'm not planning on implementing it yet. I just wanted to know if not doing it now would result in huge amounts of work later.

Thanks!
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #11
Jake: I'm not talking about drawing to the same context from two threads at once; that's insanity. What I'm talking about is doing *all* the GL drawing on a secondary thread. Unfortunately, resize events are delivered to the main thread, and you can't call [context update] whilst another thread might be modifying the context, so you need the lock in that case to prevent the concurrent update.
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #12
Aahh... Right, I see what you are saying.

It's interesting. I've had a lot of success with my threading project in the last few weeks. You really have to have at least some reasonable amount of experience to attempt it. The concept is certainly simple enough, but the bugs that can pop up can be truly challenging to track down -- even if you're a good sleuth they can definitely try your patience.

Off-topic: I did get your threaded Newton demo working and I used it as a starting point for playing around with Newton myself actually. I've never messed around with a third-party physics package until now, and it's pretty neato. I re-implemented your demo in my own project and it works really well -- real simple. For some reason, your demo on its own makes my computer whine. I suspect it's because of the way you use a while(1) loop for your physics thread, but that is purely speculative and I haven't tried tracking the cause down. It doesn't do it in my project and I calc the physics thread on a ~60-100 Hz timer. My personal opinion is that there shouldn't be any program that makes the computer `whine' period. Whatever... Also, I haven't seen it mentioned around here, but the latest Newton SDK is universal, as of late last month I think... Pretty cool stuff. Smile
Quote this message in a reply
Moderator
Posts: 3,577
Joined: 2003.06
Post: #13
Jones Wrote:Thanks all you guys. I'm not planning on implementing it yet. I just wanted to know if not doing it now would result in huge amounts of work later.

Thanks!
Sorry for the double post here, I missed this the first time through...

Yeah, you definitely will not incur much if any work on yourself by skipping threading until it's time. Just make an effort to keep rendering and updating separate like any good game design and it should fall into place pretty easy later on. Generally speaking, threading doesn't just magically offer massive performance benefits all by itself for games anyway. And not to blab on about this too much, but one of the things to realize with games is that we're typically doing everything on a frame-by-frame basis and in an orderly fashion, which makes trying to find things for another thread to do kind of tricky. An easy idea is splitting up rendering and updating, which is what I'm doing. I read a keynote by Tim Sweeny (Epic, Unreal genius) not too long ago where he was talking about that too. Games are not dead simple to thread in that respect (even for those guys!). Carmack says pretty much the same thing.
Quote this message in a reply
Jones
Unregistered
 
Post: #14
AnotherJake Wrote:Sorry for the double post here, I missed this the first time through...

Yeah, you definitely will not incur much if any work on yourself by skipping threading until it's time. Just make an effort to keep rendering and updating separate like any good game design and it should fall into place pretty easy later on. Generally speaking, threading doesn't just magically offer massive performance benefits all by itself for games anyway. And not to blab on about this too much, but one of the things to realize with games is that we're typically doing everything on a frame-by-frame basis and in an orderly fashion, which makes trying to find things for another thread to do kind of tricky. An easy idea is splitting up rendering and updating, which is what I'm doing. I read a keynote by Tim Sweeny (Epic, Unreal genius) not too long ago where he was talking about that too. Games are not dead simple to thread in that respect (even for those guys!). Carmack says pretty much the same thing.


Thanks! I would keep most of the important stuff in one thread, and a second just for file loading closing, as my file i/o functions are slow. Smile
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Mud with or without Multi-Threading ? MadPsycho 21 8,755 Oct 3, 2006 11:06 AM
Last Post: Dan Potter