views:

160

answers:

3

What is the best implementation, from a performance point of view, of branched function calls?

In the naive case we have a rather large switch statement that interprets bytecode and executes a function call depending on code.

In the normal case we have computed gotos and labels that do the same thing.

What is the absolute best way to do this?

An abstract example,

schedule: 
    swap_entity();
    goto *entity_start();

lb_code1:
    do_stuff();
    goto *next_code_item();

lb_code2:
    do_stuff();
    goto *next_code_item();

...

Edit: My reference to "branched function calls" was perhaps somewhat erroneous. Branched code execution.

+3  A: 

Maybe an array of function pointers, at a guess:

void dispatch(Message* message)
{
  //MessageType is a finite enum
  MessageType messageType = message->messageType;
  int index = (int)messageType;
  //there's an array element for each enum value
  FunctionPointer functionPointer = arrayOfFunctionPointers[index];
  (*functionPointer)(message);
}

The actual answer is hardware-dependent, and depends on things like the size of the problem and the CPU's cache.

ChrisW
Just for my own edification, what is the Message data type? I ask because I don't remember -> being in C.
CAbbott
I'm giving 'Message' as an example of a "user-defined type" i.e. a struct; this hypothetical struct would contain several fields, one of which is named messageType whose type is MessageType which is a user-defined enum type. The "`->`" goes with the "`*`" and is to do with the instance's being passed by pointer instead of by value.
ChrisW
@Chris - thanks for explaination, I couldn't remember when the '->' syntax was introduced, I thought it was a C++ introduction (i.e. message->messageType vs. (*message).messageType
CAbbott
The -> operator is part of C, and always has been.
anon
@Neil - thanks. I learned something today/
CAbbott
I like this solution as well. It's also more maintainable since you can move function pointers in your array or add new ones or even new message types without having to change the code in your dispatch function. Also, sometimes a compiler will optimize a switch statement into a jump table for you in some circumstances but I don't know the specifics.
Jeff Tucker
Elegant and maintainable. However, won't a high call frequency to dispatch degrade performance?
psyeugenic
@Wallentin - I don't understand your question. IMO what could make this slower than the equivalent if-then-else-if-then-else-if-etc is that dereferencing `arrayOfFunctionPointers` means pulling that array/memory/data into the CPU cache.
ChrisW
+2  A: 

It depends. Some table driven approach will normally be fastest, but you may well find that is what your switch statement is implemented as. Certainly, you should not take it as read that ANY recommendation in this area from SO users is the best. If we suggest something, you need to implement it and measure the performance in a build with all compiler optimisations turned on.

anon
+1  A: 

If you're looking for a speed boost here, you should look at other bytecode dispatch mechanisms. There was a question which sort-of asked that before.

Basically, you now have a goto which is probably incorrectly predicted every time, followed by a function call. With a technique like direct threading, you can probably reduce your interpreter overhead significantly. Inline threading is harder, but with greater benefit.

I gave some further resources in the other question.

Paul Biggar