11

The goal is to change the behaviour in an event loop, depending on whether a checkbox is toggled on or off. The simplest way, that I can think of, is just to test the checkbox state each time the loop is run.

// if-statement

void action() { /* ... */ }


void someLoop() {

  if (checkboxTrue) {
    action();
  }
  // ... other stuff

}

Would the code be more performant and cleaner or in any other way better, if a function pointer was being used? Like this:

// function pointer

void action() { /* ... */ }
void empty() {}
void (*actionPtr)();


void checkboxChanged(int val) {

  if (val == 1)
    actionPtr = &realAction;
  else
    actionPtr = ∅

}

void someLoop() {

  (*actionPtr)();
  // ... other stuff

}
Sergey K.
  • 24,894
  • 13
  • 106
  • 174
Davorin
  • 1,160
  • 15
  • 31
  • 3
    No. Simple is good. Always choose readability over performance if there are no performance issues. – Bart Friederichs Jan 03 '14 at 14:42
  • 2
    I would do it the first way. It's shorter and says what it means. Performance is not an issue for something so inconsequential. – Mike Dunlavey Jan 03 '14 at 14:42
  • 6
    You may be achieving the opposite effect. Branches can be predicted and function calls inlined. Calling through a function pointer, however, *always* incurs the cost of the indirection. – Kerrek SB Jan 03 '14 at 14:43
  • @KerrekSB that's actually a good point, can you post an answer? – Davorin Jan 03 '14 at 14:44
  • 1
    Apart from the obvious bug in potentially invoking an indeterminate function pointer, I see little use of the second method *at all*. And if checking the state of a checkbox is truly a performance kink in your app's armor, I'm trying very hard to imagine how simple it is. Regardless, profile, profile, profile... – WhozCraig Jan 03 '14 at 14:45
  • 2
    I disagree with everyone here, I think the function pointer will be faster overall. The difference is very slight though. – old_timer Jan 03 '14 at 14:45
  • 1
    If you care about performance then you'll use neither of these options. Running a busy loop to wait for user input is as bad a solution as is possible to make. – David Heffernan Jan 03 '14 at 14:51
  • My actual use case is a display loop of 60Hz * 10 visual elements = 600 checks in a second. – Davorin Jan 03 '14 at 14:52
  • KerrekSB: indirect branches can also be predicted, but probably less accurately than branches. – dbrank0 Jan 03 '14 at 14:53

6 Answers6

13
  1. One indirect function call is more expensive than one if condition.

  2. Several if conditions are more expensive than an indirect function call.

  3. Worrying about speed at this point is pointless:
    You are waiting on the latency of the user, and you are handling stuff he can look at (i. e. there won't be huge amounts of checkboxes). Optimizing code that is executed less than a million times per second on a detailed level like this is absolutely pointless.

So, my advise is: stop worrying about the cost of an if or function call while you are programming a user interface. Only think about such stuff inside your time consuming algorithms.

However, if you find that you are indeed using complex if/else ladders and/or switch statements inside your inner loop, you might optimize by replacing those with indirect function calls.


Edit:
You say that you have 600 checks per second. Assuming you have only one if case to handle (the situation where the if is faster), you "save" roughly 6 microseconds per second by not using function pointer indirection, that's 0.0006% of the runtime. Definitely not worth the effort...

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106
  • 4
    Several if conditions may still be faster than one indirect call. On recent Intel CPUs, one indirect call is performance-wise equivalent of ~ 3 to 4 (if) branches. –  Jan 03 '14 at 15:12
  • 5
    @VladLazarenko Only if they are predicted correctly. A function call takes 10 to 20 cycles afaik, one cached memory access is four cycles latency, that's 14 to 24 cycles for an indirect call, ignoring the possibility of preloading the function pointer which would bring this figure down again. Two pipeline flushes due to branch misprediction should be at least as expensive. – cmaster - reinstate monica Jan 03 '14 at 16:01
5

Not having the conditional branch will obviously save some time, of course it is simply branching around a branch so you are incurring a pipe flush, it is a case of maybe two flushes instead of one (barring processor optimization of course) plus the extra code to do the compare.

extern void fun0 ( unsigned int );
extern void fun1 ( unsigned int );

void (*fun(unsigned int));


void dofun0 ( unsigned int x, unsigned int y )
{
    if(x) fun0(y);
    else  fun1(y);
}

void dofun ( unsigned int y )
{
    fun(y);
}

gives something like this for example

Disassembly of section .text:

00000000 <dofun0>:
   0:   e3500000    cmp r0, #0
   4:   e92d4008    push    {r3, lr}
   8:   e1a00001    mov r0, r1
   c:   1a000002    bne 1c <dofun0+0x1c>
  10:   ebfffffe    bl  0 <fun1>
  14:   e8bd4008    pop {r3, lr}
  18:   e12fff1e    bx  lr
  1c:   ebfffffe    bl  0 <fun0>
  20:   e8bd4008    pop {r3, lr}
  24:   e12fff1e    bx  lr

00000028 <dofun>:
  28:   e92d4008    push    {r3, lr}
  2c:   ebfffffe    bl  0 <fun>
  30:   e8bd4008    pop {r3, lr}
  34:   e12fff1e    bx  lr

You should be able to measure that performance difference if you carefully craft your test. It is going to be a very small difference, but definitely measurable.

old_timer
  • 69,149
  • 8
  • 89
  • 168
  • 1
    True. But if fun0 & fun1 are visible to compiler, it can choose to inline them, which again changes the story. – dbrank0 Jan 03 '14 at 15:06
  • You are absolutely right, and depending on the instruction set and other factors all kinds of things can happen. I would assume function pointers were invented specifically to avoid a switch or if-then-else tree (for various reasons), but that is another topic. So either you might get something that is equal or the if-then-else is slower although slightly. It was very easy to demonstrate, the slower case with carefully crafted code. Likewise it is easy to demonstrate the "there is no difference code", but I will leave that up to the reader. – old_timer Jan 03 '14 at 18:19
  • It is likely possible to come up with a corner case or few where the if-then-else produces the faster code. – old_timer Jan 03 '14 at 18:20
4

Imho, pointers are a bit cleaner for calling routines having many arguments:

int good(int a, int b, int c, char *d) { ... }
int bad (int a, int b, int c, char *d) { ... }
int ugly(int a, int b, int c, char *d) { ... }

Direct calls:

if (is_good) {
   good(1,2,3,"fire!");
} else if (is_bad) {
   bad(1,2,3,"fire!");
} else {
   ugly(1,2,3,"fire!");
}

Indirect calls:

if (is_good) {
   f = good;
} else if (is_bad) {
   f = bad;
} else {
   f = ugly;
}

...

f(1,2,3,"fire!");
user2743554
  • 491
  • 2
  • 11
1

To get exact results, time measurements would be needed, but:
I´m certain that a if has less overhead than a full function call.
->If is faster

deviantfan
  • 11,268
  • 3
  • 32
  • 49
1

Your function pointer implementation is IMO not clear at all. It is not obvious what is going on there, so you should avoid this.

Maybe a different option is to put your check/action function pointers into an array. And check it inside a loop. You have a clean someLoop functionality and your check/action ("business logic") is inside this array.

duedl0r
  • 9,289
  • 3
  • 30
  • 45
0

If you are only going to be checking for these things in one central place, I recommend using a switch if possible, if/else if not.

For a start that's actually easier to extend and add new cases if you need them than having to write a new function, check for things, and use a function pointer. The function pointer solution becomes easier to extend if you were otherwise tempted to check for the conditions in multiple places.

But secondly (and less importantly), it will tend to be more efficient, sometimes considerably so, and in ways that go beyond just comparing the cost of a dynamic dispatch vs. a local branch/jump. It's because you give the optimizer so much more information to work with.

I once found a case where GCC 5.3 managed to squash down a complex set of switch cases involving multiple function calls in each case to a single branchless look-up table of what data to load into registers. I expected a jump table but it skipped the whole jump table and just figured out what data needed to be loaded for each case without branching at all.

I was so impressed as I didn't even realize through all those cases calling functions that it could just boil down to loading data from a LUT. But I'm pretty sure it would not have been able to squash my code like this if there were indirect function calls involved. They end up serving as optimization barriers unless the optimizer is so smart that it can generate all the code paths for every possible type of function pointer that can be called while figuring out which functions are going to be called through a given FP.