运算符|与运算符+的性能

Performance of operator| vs operators+

本文关键字:运算符 性能      更新时间:2023-10-16

从长远来看,|和+之间是否存在会影响代码性能的主要差异?或者两者都是O(1)?我正在使用的代码是这样的:

uint64_t dostuff(uint64_t a,uint64_t b){
        // the max values of the inputs are 2^32 - 1
        // lots of stuff involving boolean operators
        // that have no way of being substituted by 
        // arithmetic operators
        return (a << 32) + b;
        //or
        return (a << 32) | b;
}

这个代码会被使用很多次,所以我想尽可能地加快它的速度。

在任何现代计算机上都没有性能差异。

不过,这两个运算符的含义不同。如果该位已被设置,则|将不执行任何操作,但+将清除该位和所有后续非零位,并将下一个零位设置为1。

两者都肯定是O(1),因为O(1"表示常数。它们可能不是同一个常数。Big Oh表示法旨在理解独立于常数的渐近行为。

哦,是的,还有一件事在优化之前始终配置文件。你很快就会发现时间并没有花在你想的地方总是

使用|

+只能增加操作时间,原因很明显。

两者都是一条指令。至于电子的传播时间,不知道哪一个更快。

我想,你可以自己测试速度,但由于差异可能是线性的(如果可以检测到的话),并且受到噪音因素的影响,这可能有点困难。

这里的最佳答案不是试图预测哪一个更好,而是对其进行基准测试或检查汇编代码。我猜两者都将被优化为相同的指令,并且在任何情况下,两者占用的CPU周期数都可能相等。

但我强烈建议您检查ASM并对这两种解决方案进行基准测试。

如果有任何优势,它将有利于or。然而,在现实中,任何合理现代的CPU(甚至除了真正的古代CPU之外的任何CPU)都不太可能有任何区别。

基本上,or只是设置比特,仅此而已。只需要一个双输入or门,所以你就可以得到一个传播延迟门。

加法器有点复杂:计算当前位需要一个三输入XOR。XOR通常由两级门组成。此外,它还生成一个进位,该进位必须用作下一位加法器的输入。因此,"纹波进位加法器"需要与添加的位一样多的时钟周期。有一些更聪明的方法可以处理这个问题,即将进位与其他加法分开处理,这样可以获得更低的传播延迟,但在最坏的情况下,即使是这些方法也无济于事。

不过,只有当你自己设计CPU时,这些才是最重要的。如果你使用的是典型的CPU,那么功能单元中的门运行得足够快,它可以/将在一个时钟周期内完成完整的加法运算。一些相当新的甚至可以在单个功能单元中每个时钟周期进行两次加法运算。

|和'+`是不同的数学运算
给定方程式:

  unsigned int y = 2 + 2;
  unsigned int z = 2 | 2;

会得到不同的答案。

从技术上讲,"|"操作更快,因为它只在处理器内部使用OR门。加法运算需要更多的门。

通过使用"|"而不是"+"获得的性能通常会被将数据取入和取出处理器所需的时间所浪费。换句话说,净性能可以忽略不计。(时差通常在纳秒范围内。)

然而,两种形式之间的维护时间可能会更长。当一个人需要算术而不是比特处理(反之亦然)时,试图找到这个运行时错误可能会很好。

为正确的目的使用正确的操作员。让测试和维护小组休息一下。这种微观优化是不值得的。

这是特定于平台的(可能也是特定于编译器的)。在PS3上的SPU上,如果我没记错的话,动态OR是相当昂贵的。我不确定数字,但我认为它最终会将其划分为多个操作,导致成本扩展到几个指令。在x86/x64或大多数现代CISC上,很可能其中任何一条都只是一条指令,不太可能导致任何管道暂停或其他昂贵的操作。

编辑:成本的原因是Cell处理器只有一个通用寄存器,这意味着它无法将两个变量加载到标准寄存器中并执行优化。相反,必须将值加载到必须进行操作的altivec寄存器集中,然后必须通过掩码将结果从altivec注册表提取到gpr中,以便检索结果。

如果你把这些操作推到任何现代计算机上的PS3或GPU上,你可能想了解这些处理器的行为。GPU可能也有类似的问题,因为它们也是专门用于SIMD操作的RISC处理器。