views:

316

answers:

3

I'm working on an RTS game in C++ targeted at handheld hardware (Pandora). For reference, the Pandora has a single ARM processor at ~600Mhz and runs Linux. We're trying to settle on a good message passing system (both internal and external), and this is new territory for me.

It may help to give an example of a message we'd like to pass. A unit may make this call to load its models into memory:

sendMessage("model-loader", "load-model", my_model.path, model_id );

In return, the unit could expect some kind of message containing a model object for the particular model_id, which can then be passed to the graphics system. Please note that this sendMessage function is in no way final. It just reflects my current understanding of message passing systems, which is probably not correct :)

From what I can tell there are two pretty distinct choices. One is to pass messages in memory, and only pass through the network when you need to talk to an external machine. I like this idea because the overhead seems low, but the big problem here is it seems like you need to make extensive use of mutex locking on your message queues. I'd really like to avoid excess locking if possible. I've read a few ways to implement simple queues without locking (by relying on atomic int operations) but these assume there is only one reader and one writer for a queue. This doesn't seem useful to our particular case, as an object's queue will have many writers and one reader.

The other choice is to go completely over the network layer. This has some fun advantages like getting asynchronous message passing pretty much for free. Also, we gain the ability to pass messages to other machines using the exact same calls as passing locally. However, this solution rubs me the wrong way, probably because I don't fully understand it :) Would we need a socket for every object that is going to be sending/receiving messages? If so, this seems excessive. A given game will have thousands of objects. For a somewhat underpowered device like the Pandora, I fear that abusing the network like that may end up being our bottleneck. But, I haven't run any tests yet, so this is just speculation.

MPI seems to be popular for message passing but it sure feels like overkill for what we want. This code is never going to touch a cluster or need to do heavy calculation.

Any insight into what options we have for accomplishing this is much appreciated.

+2  A: 

The network will be using locking as well. It will just be where you cannot see it, in the OS kernel.

What I would do is create your own message queue object that you can rewrite as you need to. Start simple and make it better as needed. That way you can make it use any implementation you like behind the scenes without changing the rest of your code.

Look at several possible implementations that you might like to do in the future and design your API so that you can handle them all efficiently if you decide to implement in those terms.

If you want really efficient message passing look at some of the open source L4 microkernels. Those guys put a lot of time into fast message passing.

Zan Lynx
Agreed, with the caveat that you should start by implementing the simplest approach first (probably networking), and only bother recoding it for speed if profiling later shows you need to.
T.E.D.
A: 

I agree with Zan's recommendation to pass messages in memory whenever possible.

One reason is that you can pass complex objects C++ without needing to marshal and unmarshal (serialize and de-serialize) them.

The cost of protecting your message queue with a semaphore is most likely going to be less than the cost of making networking code calls.

If you protect your message queue with some lock-free algorithm (using atomic operations as you alluded to yourself) you can avoid a lot a context switches into and out of the kernel.

Cayle Spandon
+1  A: 

Since this is a small platform, it might be worth timing both approaches.

However, barring some kind of big speed issue, I'd always go for the approach that is simpler to code. That is probably going to be using the network stack, as it will be the same code no matter where the recipient is, and you won't have to manually code and degug your mutual exclusions, message buffering, allocations, etc.

If you find out it is too slow, you can always recode the local stuff using memory later. But why waste the time doing that up front if you might not have to?

T.E.D.