我有以下情况
bool user_set_flag;
getFlagFromUser(&user_set_flag);
while(1){
if(user_set_flag){
//do some computation and output
}
//do other computation
}
变量user_set_flag
仅在代码中设置一次且仅一次,在一开始,它基本上是用户选择他想对程序执行的操作。假设用户选择user_set_flag = false
,那么编译器将以这样的方式编译代码,使if(user_set_flag)
语句只被检查一次,或者总是被检查。我可以给编译器提示如将bool设置为const吗?
我问这个的原因是因为我的应用程序是时间关键的,它会尽可能快地处理帧。一个总是错误的分支应该能够在运行时以某种方式确定吗?
答案 0 :(得分:14)
首先,处理器具有称为 branch prediction 的功能。经过几次循环后,处理器将能够注意到您的if
语句始终是单向的。 (它甚至可以注意到常规模式,例如true false true false
。)然后它将 speculatively execute 该分支,并且只要它能够正确预测,{的额外成本{1}}陈述几乎被消除了。如果您认为用户更有可能选择if
而非true
,则您甚至可以tell this to the gcc compiler(特定于gcc的扩展名)。
但是,你的一条评论中确实提到你有一个“更复杂的bools序列”。我认为处理器可能没有内存来模式匹配所有跳转 - 当它返回到第一个false
语句时,知道跳转的方式已经取代从它的记忆中。但我们可以在这里帮助它......
编译器能够将循环和if语句转换为它认为更优化的形式。例如。它可能会将您的代码转换为schnaader给出的形式。这称为loop unswitching。您可以通过 Profile-Guided Optimization (PGO) 来帮助它,让编译器知道热点的位置。 (注意:在gcc中,if
仅在-funswitch-loops
启用。)
您应该在指令级配置文件您的代码(VTune将是一个很好的工具)来查看if语句是否真的是瓶颈。如果它们确实存在,并且通过查看生成的程序集,您认为尽管PGO编译器已经错了,您可以尝试自己提升if语句。也许模板化的代码会使它更方便:
-O3
答案 1 :(得分:6)
我认为根本无法进一步优化这一点。编译器非常聪明,知道user_set_flag
的值在执行循环期间不会改变,并且会为此生成最有效的机器代码。
这也是二次猜测编译器的问题。除非你真的真的知道自己在做什么,否则最好坚持使用最简单的解决方案。
作为练习,尝试使用if (true)
和if(user_set_flag)
来分析(时间)执行。我的猜测是执行时间差别不大。
答案 2 :(得分:5)
另一种选择是:
if(user_set_flag){
while(1){
ComputationAndOutput();
OtherComputation();
}
} else {
while(1){
OtherComputation();
}
}
但正如Smashery所说,这是微优化,不会像你可以做的其他优化那样加速你的程序。
答案 3 :(得分:4)
从技术上讲,编译器可以优化这种情况。
例如:
#include <cstdio>
int main(int argc, char* [])
{
while (true)
{
if (argc == 1) {
puts("one");
}
puts("some more");
}
}
main编译为(G ++ -O3):
cmpl $1, 8(%ebp)
je L9
.p2align 4,,15
L2:
movl $LC1, (%esp)
call _puts
jmp L2
L9:
movl $LC0, (%esp)
call _puts
movl $LC1, (%esp)
call _puts
movl $LC0, (%esp)
call _puts
movl $LC1, (%esp)
call _puts
jmp L9
正如您所看到的那样,只评估条件一次以确定要运行的循环。它已经展开了真正的分支:)
我会得出结论,没有理由担心这些微优化,除非你确定编译器无法优化掉不变的布尔值的重复评估(例如,如果它是全局的,编译器将如何知道它不会被函数调用修改)并且它确实是瓶颈。
答案 4 :(得分:1)
您说用户实际上有一个设置可以将此标记设置为true
或false
。这意味着它可以在运行时更改。这意味着,它(通常)无法优化。
通常,编译器只能在编译时“优化”它所知道的 。这意味着:此时您点击编辑菜单中的“构建”项目。如果它可以改变,它 - 通常 - 不能被优化掉。
但是,很容易(很好地,取决于你没有展示的部分)自己优化它。如果您对循环中使用的一个汇编指令感到困扰,请将if语句放在循环外部。这样,它只对函数的调用执行一次。
答案 5 :(得分:0)
如果在编译时知道flag的值,可以添加不包含if语句的编译标志:
while(1){
#ifdef user_set_flag
{
//do some computation and output
}
#endif
//do other computation
}
答案 6 :(得分:0)