在 C++ 中使用位运算符还是使用 if 语句更快?

Is it faster to use bit operators or if statements in C++?

我在某处读到,在可能的情况下使用按位运算符而不是 if 语句会更快。我正在从事一个图像处理项目,我有多种方法可以对像素进行数学运算。例如,当我添加一个像素时,我会检查并确保总和没有超过最大值。我改成了这个...

    Pixel16 operator+(Pixel16 p) const noexcept
    {
        uint_fast32_t r = red + p.red;
        uint_fast32_t g = green + p.green;
        uint_fast32_t b = blue + p.blue;
        return Pixel16(r | -(r > 0xffff), g | -(g > 0xffff), b | -(b > 0xffff));
    }

你们认为它比写语句更快...

if(r > 0xffff)
 r = 0xffff;

仅供参考,红色、绿色和蓝色是 uint16_t

类型的成员变量

在几乎所有情况下,"faster" 将在很大程度上取决于目标系统和使用的编译器,以及运算符的重载方式。不过,在这种情况下,我严重怀疑会有那么大的不同,因为您仍在使用比较运算符,这应该是比简单的 if 分支更昂贵的操作。

不过,上面列出的代码让我很困扰。否定比较运算的结果(布尔值 运算,除非您已经覆盖运算符)对于按位或像您正在做的那样是不安全的。此外,它很难理解,这意味着以后有人很难维护。不过,if 版本解释了发生了什么。

知道,您必须测量(剖析代码)。

为什么按位运算 可能 更快的理论是分支预测失败可能会严重影响性能。通过使用按位运算而不是控制流开关,您可以完全避免条件分支,因此您永远不会错过那里的分支预测。

根据具体情况,计算而不是分支可能对缓存更友好。

但除非您进行测量,否则您不会知道实际效果。影响因素太多,无法简单分析代码。

鉴于此代码:

#include <algorithm>
#include <cstdint>

struct Pixel16 {
    uint16_t red;
    uint16_t blue;
    uint16_t green;

    Pixel16(uint16_t red, uint16_t green, uint16_t blue);

};

Pixel16 v1(Pixel16 const & p, Pixel16 const & s) {
    uint_fast32_t r = p.red   + s.red;
    uint_fast32_t g = p.green + s.green;
    uint_fast32_t b = p.blue  + s.blue;
    return Pixel16(r | -(r > 0xffff), g | -(g > 0xffff), b | -(b > 0xffff));
}

Pixel16 v2(Pixel16 const & p, Pixel16 const & s) {
    uint_fast32_t r = p.red   + s.red;
    uint_fast32_t g = p.green + s.green;
    uint_fast32_t b = p.blue  + s.blue;

    r = std::min(r, (uint_fast32_t) 0xFFFF);
    g = std::min(g, (uint_fast32_t) 0xFFFF);
    b = std::min(b, (uint_fast32_t) 0xFFFF);

    return Pixel16(r, g, b);
}

我的编译器给出了什么结果?

Clang on OS X 将为 v1v2 生成功能相同的代码。唯一的区别是对 min() 的调用顺序和 v1 中的等效工作以不同的顺序发生。

在这两种情况下,都没有分支。

总结:

写出最容易理解的代码。使用函数和语言特性以可读的方式表达您的代码。您的按位代码比 min() 函数更易读还是更差?