If语句与函数指针

时间:2014-01-03 14:39:35

标签: c performance if-statement function-pointers data-oriented-design

目标是更改事件循环中的行为,具体取决于是否打开或关闭复选框。我能想到的最简单的方法就是每次运行循环时测试复选框状态。

// if-statement

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


void someLoop() {

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

}

如果使用函数指针,代码会更高效,更清晰,还是更好?像这样:

// function pointer

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


void checkboxChanged(int val) {

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

}

void someLoop() {

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

}

6 个答案:

答案 0 :(得分:9)

  1. 一个间接函数调用比一个条件更昂贵。

  2. 如果条件比间接函数调用更昂贵,可以使用几个。

  3. 在这一点上担心速度是没有意义的:
    您正在等待用户的延迟,并且您正在处理他可以查看的内容(即,不会有大量的复选框)。在这样的详细级别上优化每秒执行不到一百万次的代码绝对没有意义。

  4. 所以,我的建议是:在编写用户界面时,不要担心if或函数调用的成本。只考虑耗时算法中的这些内容。

    但是,如果您发现内部循环中确实使用了复杂的if / else梯形图和/或switch语句,则可以通过使用间接函数调用替换它们来进行优化


    编辑:
    你说你每秒有600张支票。假设你只有一个if个案件处理(if更快的情况),你不使用函数指针间接“每秒约”6微秒,即运行时的0.0006%。绝对不值得努力...

答案 1 :(得分:5)

没有条件分支显然会节省一些时间,当然它只是围绕分支进行分支,因此您需要进行管道刷新,这可能是两次刷新而不是一次(当然,除非处理器优化)加上进行比较的额外代码。

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);
}

给出了类似的东西,例如

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

如果您仔细制作测试,您应该能够衡量性能差异。这将是一个非常小的差异,但绝对可以衡量。

答案 2 :(得分:3)

Imho,指针对于调用具有多个参数的例程来说有点清晰:

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) { ... }

直接电话:

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

间接通话:

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

...

f(1,2,3,"fire!");

答案 3 :(得分:1)

要获得准确的结果,需要进行时间测量,但是:
我确定if的开销比完整的函数调用少 - &gt;如果更快

答案 4 :(得分:1)

你的函数指针实现是IMO根本不清楚。现在发生的事情并不明显,所以你应该避免这种情况。

也许不同的选择是将检查/操作函数指针放入数组中。并在循环中检查它。您有一个干净的someLoop功能,并且您的检查/操作(“业务逻辑”)在此数组中。

答案 5 :(得分:0)

如果您只是在一个中心位置检查这些内容,我建议尽可能使用switch,否则if/else

对于一个实际上更容易扩展和添加新案例的开始,而不是必须编写新函数,检查事物,并使用函数指针。如果您试图在多个位置检查条件,则函数指针解决方案变得更容易扩展。

但其次(并且不那么重要),它往往更有效,有时甚至更高效,并且不仅仅是比较动态调度与本地分支/跳转的成本。这是因为您为优化程序提供了更多信息以供使用。

我曾经发现一个案例,GCC 5.3设法将一个涉及多个函数调用的switch cases复杂集合压缩到一个无分支查找表中,该表用于加载到寄存器中的数据。我期待一个跳转表,但它跳过了整个跳转表,只是想出了每个案例需要加载哪些数据而没有分支。

我印象深刻,因为我甚至没有意识到所有那些cases调用函数,它可以归结为从LUT加载数据。但我很确定如果涉及间接函数调用,它将不能像我这样压缩我的代码。它们最终用作优化障碍,除非优化器非常智能,它可以为每个可能的类型的函数指针生成所有代码路径,同时确定哪些函数将通过给定的FP调用。