使用GCC优化C/C++中循环内的嵌套if语句

Par*_*e56 9 c c++ optimization gcc loops

我正在使用GCC编译器测试C/C++中的各种优化.我目前有一个包含多个嵌套if语句的循环.条件是在程序执行开始时计算的.看起来有点像这样:

bool conditionA = getA();
bool conditionB = getB();
bool conditionC = getC();
//Etc.

startTiming();

do {
    if(conditionA) {
        doATrueStuff();
        if(conditionB) {
            //Etc.
        } else {
            //Etc.
        }
    } else {
        doAFalseStuff();
        if(conditionB) {
            //Etc.
        } else {
            //Etc.
        }
    }
} while (testCondition());

endTiming();
Run Code Online (Sandbox Code Playgroud)

doATrueStuff()内联函数在哪里进行一些简单的数值计算,因此调用它没有任何开销.

不幸的是,不能事先定义条件,它们必须在运行时计算.我们甚至无法可靠地预测他们是真是假的可能性.getA()也许是rand()%2.但经过计算,它们的价值永远不会改变.

我想到了两个解决方案,一个是全局函数指针,用于在循环中调用适当的函数,如下所示:

void (*ptrA)(void);
//Etc.

int main(int argc, char **argv) {
    //...
    if (conditionA) {
        ptrA=&aTrueFunc;
    } else {
        ptrA=&aFalseFunc;
    }
    //...
    do {
        (*ptrA)();
    } while (testCondition());
    //...
}
Run Code Online (Sandbox Code Playgroud)

这样我可以从循环中消除所有分支,但是然后我将有多个函数调用的开销减慢我的速度.

或者我可以简单地为每个条件组合设置一个不同的循环,如下所示:

if(conditionA) {
    if(conditionB) {
        do {
            //Do A == true B == true stuff
        } while (testCondition());
    } else {
        do {
            //Do A == true B == false stuff
        } while (testCondition());
    }
} else {
    //Etc.
}
Run Code Online (Sandbox Code Playgroud)

然而,当一个人开始拥有太多条件时,这样就不那么优雅并且不可能有效地做到这一点,因为对于X条件,人们需要编写2 ^ X个循环.

是否有更优雅/更快的方式来优化它?

在这方面是否有任何意义,或者编译器是否会以某种方式理解条件在循环期间不会发生变化并自行优化?

出于好奇,有没有其他编程语言可以使编写这样的代码更容易/可能?或者只有通过使用程序集在程序加载到内存后更改程序的指令才能实现?

VAn*_*rei 2

理论:

尝试通过一些古怪的重写来优化代码可能会使编译器难以进行通常的优化。编译器和处理器可以使用两种技术来优化代码:

  1. 分支预测:编译器可以通过使用配置文件引导优化来做到这一点,主要是通过估计每个分支的概率。除了计算每个目标的统计信息之外,CPU 还具有尝试检测分支模式的分支目标缓冲区。
  2. 分支预测:编译器或CPU将使代码并行执行两个分支(因为现在的处理器是超标量),并且根据条件结果,它只会忽略不正确路径的结果(例如CMOV指令)。您可以尝试使用 -fno-if-conversion 和 -fno-if-conversion2 禁用分支预测。如果每个分支上有大量计算并且执行所有路径将导致指令解码器和执行端口的浪费,这可能会有所帮助。

作为一个简单的开发人员,使用 gcc,您还可以使用“可能”和“不太可能”编译提示来帮助分支预测或代码生成。点击此处了解更多详情。例如,如果您知道一种情况比另一种情况更有可能发生,那么这可能会起作用。

要查看分支预测效率,请使用perf stat ./binary并检查分支未命中率以及您执行的每次优化的分支未命中数。

在您的代码案例中:

如果条件A、条件B和条件C在循环之前计算并且不改变,则分支预测器很容易检测到该模式。CPU 的预测器通过跟踪最后采用/未采用的分支来实现这一点,并将使用记录的历史记录来预测后续分支。因此,我实际上预计由于代码中的分支而导致的性能损失非常小,您可以按照上面的方法进行验证。