代码之家  ›  专栏  ›  技术社区  ›  ldog

for循环中始终为false的布尔条件是否得到优化?

  •  4
  • ldog  · 技术社区  · 16 年前

    我有以下情况

    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 回复  |  直到 16 年前
        1
  •  14
  •   int3    16 年前

    首先,处理器具有一种称为 branch prediction . 运行几次循环后,处理器将能够注意到 if 陈述总是单向的。(它甚至可以注意到规则的模式,比如 true false true false 然后) speculatively execute 只要它能正确预测,这个分支的额外成本 如果 这句话几乎被删去了。如果你认为用户更有可能选择 true 而不是 false 你甚至可以 tell this to the gcc compiler (GCC特定扩展)。

    然而,在你的一条评论中,你提到过你有一个“更加复杂的bools序列”。我认为处理器可能没有内存来匹配所有这些跳转——当它返回到第一个时 如果 声明,关于跳跃的方向的知识已经从记忆中转移了。但我们可以在这里帮忙…

    编译器有能力 转换循环和if语句 它认为是更理想的形式。例如,它可能会将您的代码转换成schnaader给出的格式。这被称为 loop unswitching . 你可以通过做来帮助它 Profile-Guided Optimization (PGO) 让编译器知道热点在哪里。(注:在海湾合作委员会, -funswitch-loops 仅在以下时间打开 -O3 )

    你应该 轮廓 您的指令级代码( VTune 这将是一个很好的工具)看看if语句是否真的是瓶颈。如果它们真的是这样,并且通过查看生成的程序集,您认为编译器在PGO的情况下把它弄错了,那么您可以尝试自己提升if语句。也许模板化的代码会使它更方便:

    template<bool B> void innerLoop() {
        for (int i=0; i<10000; i++) {
            if (B) {
                // some stuff..
            } else {
                // some other stuff..
            }
        }
    }
    if (user_set_flag) innerLoop<true>();
    else innerLoop<false>();
    
        2
  •  6
  •   Igor Zevaka    16 年前

    我认为根本不可能进一步优化这一点。编译器足够聪明,可以知道 user_set_flag 不会在循环的执行过程中更改,并且会为此生成最有效的机器代码。

    这在某种程度上也属于对编译器的二次猜测。除非你真的知道自己在做什么,否则最好坚持最简单的解决方案。

    作为一个练习,尝试使用 if (true) if(user_set_flag) . 我的猜测是,执行时间的差异将为零。

        3
  •  5
  •   schnaader    16 年前

    另一种选择是:

    if(user_set_flag){
        while(1){
          ComputationAndOutput();
          OtherComputation();
        }
    } else {
        while(1){
          OtherComputation();
        }
    }
    

    但正如Smashery已经说过的,这是一个微优化,不会像其他优化那样加速程序。

        4
  •  4
  •   visitor    16 年前

    从技术上讲,编译器可以优化这样的情况。

    例如:

    #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
    

    如您所见,条件只计算一次,以确定要运行哪个循环。它把真正的树枝展开了一点。)

    我的结论是,没有理由担心这些微优化,除非您确定编译器无法优化不断变化的布尔值的重复计算(例如,如果它是全局的,编译器如何知道它不会被函数调用修改),并且它确实是瓶颈。

        5
  •  1
  •   Abel    16 年前

    您说用户实际上有一个设置可以将此标志设置为 true false .这意味着它可以在运行时改变。也就是说,它不能被优化掉(通常)。

    一般来说,编译器只能“优化”它知道的东西。 编译时 . 这意味着:当你点击编辑器菜单中的“构建”项时。如果它可以改变,它通常不能被优化掉。

    然而,这是相当容易(好,取决于你没有显示的部分)优化它远离自己。如果您被它在循环内部使用的一条汇编指令所困扰,请将if语句放在循环外部。这样,函数调用只执行一次。

        6
  •  0
  •   user2054758    16 年前

    如果您在编译时知道标志的值,则可以将不包括if语句的编译标志添加为:

    while(1){
       #ifdef user_set_flag
        {
            //do some computation and output
    
        }
       #endif
    
    
        //do other computation
    }
    
        7
  •  0
  •   Community Mohan Dere    9 年前

    如果您真的希望尽可能快,那么您需要进行积极的性能调优。因此,忘记尝试猜测编译器可能会做些什么来优化您的程序。

    那是胆怯。

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