11

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

// 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

}
4

6 回答 6

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

  2. 几个 if 条件比间接函数调用更昂贵。

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

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

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


编辑:
你说你每秒有 600 次检查。假设您只有一种if情况要处理(if更快的情况),通过不使用函数指针间接,您每秒“节省”大约 6 微秒,这是运行时间的 0.0006%。绝对不值得努力...

于 2014-01-03T14:52:49.607 回答
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

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

于 2014-01-03T14:55:19.917 回答
4

恕我直言,对于调用具有许多参数的例程来说,指针更干净一些:

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!");
于 2014-01-03T15:08:36.977 回答
1

为了获得准确的结果,需要时间测量,但是:
我确信 if 的开销比完整的函数调用少。
->如果更快

于 2014-01-03T14:43:16.203 回答
1

您的函数指针实现在 IMO 中根本不清楚。那里发生了什么并不明显,所以你应该避免这种情况。

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

于 2014-01-03T14:46:11.813 回答
0

如果你只打算在一个中心位置检查这些东西,我建议switch如果可能的话,if/else如果没有的话。

首先,如果需要,扩展和添加新案例实际上比编写新函数、检查内容和使用函数指针更容易。如果您想在多个地方检查条件,则函数指针解决方案变得更容易扩展。

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

我曾经发现一个案例,其中 GCC 5.3 设法将一组复杂的switch cases涉及多个函数调用的集合压缩到一个单一的无分支查找表中,该表将哪些数据加载到寄存器中。我希望有一个跳转表,但它跳过了整个跳转表,只是弄清楚了每种情况需要加载哪些数据而根本没有分支。

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

于 2017-12-21T04:01:58.643 回答