对一段简单代码的按位运算

Bitwise operation over a simple piece of code

最近遇到一个code that can compute the largest number given two numbers using XOR。虽然这看起来很漂亮,但同样的事情可以通过简单的三元运算符或 if else 来实现。不只是这个例子,但是按位运算比普通代码有什么优势吗?如果是这样,这种优势是在计算速度还是内存使用方面?我假设在按位运算中汇编代码看起来比普通代码简单得多。与此相关的是,在编写嵌入式系统时哪个效率更高?

*普通代码指的是您通常的做法。例如 a*2 是普通代码,我可以用 a<<1

实现同样的事情

在某些平台上,分支很昂贵,因此找到一种无需分支即可获得 min(x,y) 的方法有一定的好处。我认为这在 CUDA 中特别有用,因为硬件中的管道很长。

当然,在具有条件执行和发出这些操作码的编译器的其他平台(如 ARM)上,它归结为比较和条件移动(两条指令),没有管道气泡。几乎肯定比比较和一些逻辑运算要好。

按位运算符通常具有常数时间的优点,与输入值无关。条件移动和分支可能是某些应用程序(例如加密库)中定时攻击的目标,而按位运算则不受此类攻击。 (忽略缓存时序攻击等)

通常,如果处理器能够进行流水线操作,则使用按位运算比条件移动或分支更有效,从而绕过整个分支预测问题。这可能会或可能不会加速您生成的代码。

不过,您必须小心,因为某些操作在 C 中构成未定义的行为,例如移动有符号整数等。因此,执行 "normal" 的操作可能对您有利方式。

do bitwise operations have any advantage over normal code?

位运算是普通代码。现在大多数编译器都有优化器,可以为 a << 1 生成与 a * 2 相同的指令。在某些硬件上,尤其是在低功率微处理器上,移位运算比乘法需要更少的 CPU 周期,但在某些硬件上这没有任何区别。

不过,在您的特定情况下,有一个优势:使用 XOR 的代码避免了 分支 ,这很有可能加快代码速度。当没有分支时,CPU 可以使用 流水线 来更快地执行相同的操作。

when programming embedded systems which is more efficient?

嵌入式系统通常 CPU 功能较弱,因此按位运算确实具有优势。例如,在 68HC11 CPU multiplication takes 10 cycles, while shifting left takes only 3.

但是请注意,这并不意味着您应该明确地使用按位运算。大多数编译器,包括嵌入式编译器,会将常量乘法转换为一系列移位和加法,以防节省 CPU 个周期。

由于发帖者使用列出的 Embedded 标签询问,我将尝试在我的回答中主要反映这一点。

简而言之,通常您不应该尝试 "creative" 编码,因为以后会变得更难理解! (老话,"premature optimization is the root of all evils")

所以只有当您知道您在做什么时,才做类似的事情,在任何其他情况下,尝试编写最容易理解的 C 代码。

好吧,这是一般部分,现在让我们了解这些技巧可以做什么,它们如何影响执行时间。

  • 首先,在嵌入式中,最好查看反汇编列表。如果您使用具有 -O2 优化的 GCC 变体,您通常可以假设它非常聪明地理解代码的意图,并且会产生可能很好的结果。它甚至可以自己使用这些技巧来弄清楚代码,如果它 "sees" 它在目标 CPU 上会更快,所以你不需要用技巧来破坏代码的可理解性。对于其他编译器,结果可能会有所不同,有疑问的是,应该观察汇编列表以查看是否可以使用这些位 hack 技巧来改善执行时间。

  • 在通常的嵌入式平台上,尤其是在 8 位平台上,您不需要太在意流水线(以及相关的分支预测错误),因为它很短(或不存在)。因此,通过以算术运算为代价消除条件通常一无所获,而且实际上可能会通过使用一些精心设计的 hack 来破坏性能。

  • 在更快的 32 位 CPUs 上通常有更长的管道和分支预测器来消除刷新(花费很多周期),因此消除条件可能会有所回报。但前提是它们具有分支预测器无法正确猜测它们的性质(例如对 "random" 数据的比较),否则条件可能仍然更好,花费最少的时间(单循环甚至 "less" 如果 CPU 每个周期能够处理多个操作)当它们被预测正确时。