for循环中一个始终为false的布尔条件是否会被优化掉?

时间:2009-11-27 01:27:28

标签: c++ optimization loops

我有以下情况

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吗?

我问这个的原因是因为我的应用程序是时间关键的,它会尽可能快地处理帧。一个总是错误的分支应该能够在运行时以某种方式确定吗?

7 个答案:

答案 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)

您说用户实际上有一个设置可以将此标记设置为truefalse。这意味着它可以在运行时更改。这意味着,它(通常)无法优化。

通常,编译器只能在编译时“优化”它所知道的 。这意味着:此时您点击编辑菜单中的“构建”项目。如果它可以改变,它 - 通常 - 不能被优化掉。

但是,很容易(很好地,取决于你没有展示的部分)自己优化它。如果您对循环中使用的一个汇编指令感到困扰,请将if语句放在循环外部。这样,它只对函数的调用执行一次。

答案 5 :(得分:0)

如果在编译时知道flag的值,可以添加不包含if语句的编译标志:

while(1){
   #ifdef user_set_flag
    {
        //do some computation and output

    }
   #endif


    //do other computation
}

答案 6 :(得分:0)

如果您真的想尽可能快,那么您希望进行积极的性能调整。所以不要试图猜测编译器可能会做些什么来优化你的程序。

那是胆小的。

相反,请负责。 This shows you how.