C语言 对一段简单的代码进行按位操作



最近我遇到了一个代码,可以使用异或计算给定两个数字的最大数字。虽然这看起来很漂亮,但同样的事情也可以通过简单的三元操作符或if else来实现。不仅仅是这个例子,但是位操作比普通代码有什么优势吗?如果是这样,这种优势是在计算速度还是内存使用方面?我假设按位操作的汇编代码看起来比普通代码简单得多。与此相关的是,在为嵌入式系统编程时,哪个更高效?

*正常代码指的是你通常会怎么做。例如,a*2是正常的代码,我可以用a<<1

来实现同样的事情

位操作比普通代码有什么优势吗?

位操作是正常的代码。现在大多数编译器都有优化器,可以为a << 1生成与a * 2相同的指令。在一些硬件上,特别是在低功耗的微处理器上,移位操作比乘法操作占用更少的CPU周期,但是在某些硬件上这没有什么区别。

在您的特定情况下,

有一个优势:带有异或的代码避免了分支,这有很大的加速代码的潜力。当没有分支时,CPU可以使用管道来更快地执行相同的操作。

嵌入式系统编程时哪个更高效?

嵌入式系统通常具有较弱的cpu,因此按位操作确实具有优势。例如,在68HC11 CPU上,乘法运算需要10个周期,而向左移位只需要3个周期。

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

位操作符通常具有恒定时间的优点,无论输入值如何。在某些应用程序(如加密库)中,条件移动和分支可能是定时攻击的目标,而按位操作则不会受到此类攻击。(不考虑缓存定时攻击等)

一般来说,如果处理器能够流水线操作,那么使用按位操作将比条件移动或分支更有效,从而绕过整个分支预测问题。这可能会加快生成的代码的速度,也可能不会。

但是,您必须小心,因为某些操作在C中构成未定义的行为,例如移动有符号整数等。出于这个原因,以"正常"的方式做事可能对你有利。

在某些平台上分支是昂贵的,所以找到一种不需要分支就能获得min(x,y)的方法是有价值的。我认为这在CUDA中特别有用,因为硬件中的管道很长。

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

既然海报问它与嵌入式标签列出,我将尽量主要反映在我的回答。

简而言之,通常你不应该尝试"创造性"的编码,因为它会变得更加难以理解!(俗话说:"过早优化是万恶之源")

所以,只有当你确切地知道你在做什么时,才做类似的事情,在任何情况下,尽量写出最容易理解的C代码。

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

  • 首先,在嵌入式中,最好检查反汇编清单。如果您使用带有-O2优化的GCC变体,您通常可以假设它非常聪明地理解代码的含义,并且将产生可能很好的结果。如果它"看到"它在目标CPU上会更快,它甚至可以自己使用这样的技巧来计算代码,因此您不需要使用技巧来破坏代码的可理解性。对于其他编译器,结果可能会有所不同,如果有疑问,应该观察程序集清单,看看是否可以使用这些位hack技巧来改进执行时间。

  • 在通常的嵌入式平台上,特别是在8位时,您不需要太关心管道(以及相关的分支错误预测),因为它很短(或不存在)。因此,以算术运算为代价消除条件通常不会获得任何好处,而且通过使用一些精心设计的hack实际上可能会破坏性能。

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

最新更新