If语句与函数指针
目标是更改事件循环中的行为,具体取决于是否打开或关闭复选框。 我能想到的最简单的方法就是每次运行循环时测试复选框状态。
// 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 }
-
如果条件,一个间接函数调用比一个函数调用更昂贵。
-
如果条件比间接函数调用更昂贵,则有几个。
-
在这一点担心速度是没有意义的:
您正在等待用户的延迟,并且您正在处理他可以查看的内容(即,不会有大量的复选框)。 在这样的详细级别上优化每秒执行不到一百万次的代码是绝对没有意义的。
所以,我的建议是:在编写用户界面时,不要担心if
或函数调用的成本。 只考虑耗费时间的算法中的这些东西。
但是,如果您发现在内部循环中确实使用了复杂的if
/ else
梯形图和/或switch
语句,则可以通过使用间接函数调用替换它们来进行优化。
编辑:
你说你每秒有600张支票。 假设你只有一个if
case要处理( if
更快的情况),你通过不使用函数指针间接来“保存”大约每秒6微秒,这是运行时的0.0006%。 绝对不值得努力……
没有条件分支显然会节省一些时间,当然它只是围绕一个分支进行分支,因此您需要进行管道刷新,这可能是两次刷新而不是一次(当然,除非处理器优化)加上额外的代码做比较。
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 : 0: e3500000 cmp r0, #0 4: e92d4008 push {r3, lr} 8: e1a00001 mov r0, r1 c: 1a000002 bne 1c 10: ebfffffe bl 0 14: e8bd4008 pop {r3, lr} 18: e12fff1e bx lr 1c: ebfffffe bl 0 20: e8bd4008 pop {r3, lr} 24: e12fff1e bx lr 00000028 : 28: e92d4008 push {r3, lr} 2c: ebfffffe bl 0 30: e8bd4008 pop {r3, lr} 34: e12fff1e bx lr
如果您仔细制作测试,您应该能够衡量性能差异。 这将是一个非常小的差异,但绝对是可衡量的。
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!");
要获得准确的结果,需要进行时间测量,但是:
我确定if的开销比完整的函数调用少。
– >如果更快
你的函数指针实现是IMO根本不清楚。 在那里发生的事情并不明显,所以你应该避免这种情况。
也许不同的选择是将检查/操作函数指针放入数组中。 并在循环中检查它。 你有一个干净的someLoop
function,你的检查/操作(“业务逻辑”)在这个数组中。
如果您只是在一个中心位置检查这些东西,我建议尽可能使用switch
, if/else
。
对于一个开头实际上更容易扩展并添加新案例,如果你需要它们而不是必须编写新函数,检查事物,并使用函数指针。 如果您试图在多个位置检查条件,则函数指针解决方案变得更容易扩展。
但其次(并且不那么重要),它往往更有效率,有时甚至更高效,并且不仅仅是比较动态调度与本地分支/跳转的成本。 这是因为您为优化器提供了更多信息。
我曾经发现一个案例,GCC 5.3设法将一组涉及多个函数调用的复杂switch cases
压缩到一个无分支查找表中,该查询表表示要加载到寄存器中的数据。 我期待一个跳转表,但它跳过了整个跳转表,只是想出了每个案例需要加载哪些数据而根本没有分支。
我印象非常深刻,因为我甚至没有意识到通过所有这些调用函数的情况,它可以归结为从LUT加载数据。 但我很确定,如果涉及间接函数调用,它将不能像我这样压缩我的代码。 它们最终用作优化障碍,除非优化器非常智能,它可以为每个可能的类型的函数指针生成所有代码路径,同时确定哪些函数将通过给定的FP调用。