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

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

本文关键字:if 语句 运算符 C++      更新时间:2023-10-16

我在某个地方读到,尽可能使用按位运算符而不是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 类型的成员变量

给定以下代码:

#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()函数可读性更强?

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

不过,上面列出的代码让我很困扰。对比较运算(布尔值运算,除非你重写了运算符)的结果取反对位或你正在做的事情来说都不安全。此外,它很难理解,这意味着以后很难对其进行维护。不过,if版本解释了发生了什么。

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

关于按位操作可能更快的理论是,分支预测失败可能会对性能造成重大影响。通过使用逐位操作而不是控制流开关,您可能能够完全避免条件分支,因此您永远不会错过分支预测。

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

但除非你进行测量,否则你不会知道实际效果。有太多因素在起作用,无法简单地分析代码。

这就是三十年前的微优化,因为它可以节省微秒。今天我称之为纳米优化:-)

除非你有理由相信速度会影响到任何人,否则就做可读性更强的事情。

如果速度很重要,例如,因为您每秒需要处理60张1000万像素的图像,那么您可以尝试各种方法并测量执行时间。确切的速度将取决于编译器、处理器,有时纯粹取决于运气好还是坏。显然,您使用的编译器优化设置与生产代码中使用的设置相同。

在不同的处理器上,哪种代码产生最快的结果可能会有所不同。因此,你将所有变体都留在源代码中(包括可读性最强的变体),并使用#ifdef从你测量过的已知最快的变体中选择。假设你的代码应该在三个当前设备和五个未来设备上运行。你在目前的两台设备上进行了测量。然后,您要确保在这两个设备上运行的是最快的代码。在其他设备上,这是一个判断。如果A在处理器X上比B快40%,但在处理器Y上慢5%,那么你可能会在所有没有测量速度的处理器上使用A。然后将测量结果添加为注释。

现在,所有这些都是大量的工作,所以只有在节省的时间值得付出的情况下,你才能这样做。