避免在 C++ 中的循环中的每一步都检查相同的条件

Avoid checking the same condition every step in a loop in C++

我正在检查循环内的条件,如果条件成立,则执行某些操作。

for (i = 0; i < n; i++)
{
    // do lots of work here
    .
    .
    .
    if (constant_condition)
        do_something(n);
}

条件独立于n,所以每次都检查感觉多余。我可以这样做:

if (constant_condition)
    for (i = 0; i < n; i++)
    {
        // do lots of work here
        .
        .
        .

        do_something(n);
    }
else
    for (i = 0; i < n; i++)
    {
        // do lots of work here
        .
        .
        .    
    }

这个新代码更有效率,但我不得不在我的程序中复制粘贴相同的代码。有没有一种有效的方法可以做到这一点而无需重复相同的代码块?

编辑:条件在编译时未知,但会在运行时给出并且不会改变。

首先,配置文件以查看它是否重要。如果是,您有多种选择:

  • 如果编译器还没有缓存常量在循环外。这是最简单的,在大多数情况下足够了:

    const bool constant_condition = ...;
    for (...) {
       ...
       if (constant_condition) do_something(...);
    }
    
  • 如果你真的需要避免分支,一个典型的方法是定义一个辅助函数或一个本地lambda (C++11)来分解出公共代码块。但是,这仍然是重复代码,并且根据情况,可能看起来一点也不漂亮:

    auto main_work = [...](...) { ... };
    if (constant_condition)
        for (...) { main_work(...); }
    else
        for (...) { main_work(...); do_something(...); }
    
  • 定义模板并根据需要进行参数化。编译器通常会适当优化,因此您可以简单地 copy-paste 代码。如果你真的想确保分支被删除,你可以强制它专门化模板,或者利用 if constexpr (C++17) 等。但是,要注意代码膨胀和编译时间。

    template <bool constant_condition>
    void f(...) { ... }
    
    if (constant_condition)
        f<true>(...);
    else
        f<false>(...);
    

最后,不要忘记再次剖析。有时,删除分支可能看起来不错,但总体上是有害的。如果代码更改很多并且最初看起来像是一小段指令重复现在是几个充满重复代码的内存页,则尤其如此。

另一种方法是尝试查看 algorithm/code 是否可以写成无分支;但是,这不是通用的解决方案。

我没有分析这段代码,但由于 CPU 中的分支预测器,这对代码速度的影响肯定可以忽略不计。

if 指令会通过 CPU 一堆,但是分支对代码速度的主要影响是并行化、乱序指令执行和缓存优化被搞砸了,但是它们不会因为分支预测器而受到干扰。