在C++中哪个更快?(2*i + 1)或(i << 1 | 1)?

M. *_*its 8 c++ performance assembly bit-shift

我意识到答案可能是硬件特定的,但我很好奇是否有一种更普遍的直觉,我错过了?

我问了这个问题并给出了答案,现在我想知道我是否应该改变我的方法一般使用"(i << 1 | 1)"而不是"(2*i + 1)"??

pax*_*blo 13

由于ISO标准实际上并未要求性能要求,这将取决于实现,所选的编译器标志,目标CPU以及很可能是月亮的相位.

这些优化(节省几个周期)在投资回报方面几乎总是微不足道,而不是像算法选择这样的宏观优化.

首先瞄准代码的可读性.如果您的意图是移位,并OR使用位移版本.如果您的意图是倍增,请使用该*版本.一旦确定存在问题,只关心性能.

任何体面的编译器都会比你能更好地优化它:-)


Fra*_*kH. 8

只是关于"......它将使用LEA"的答案的实验:
以下代码:

int main(int argc, char **argv)
{
#ifdef USE_SHIFTOR
return (argc << 1 | 1);
#else
return (2 * argc + 1);
#endif
}
Run Code Online (Sandbox Code Playgroud)

gcc -fomit-frame-pointer -O8 -m{32|64}(用于32位或64位)编译成以下汇编代码:

  1. x86,32bit:
    080483a0 <main>:
    80483a0:    8b 44 24 04             mov    0x4(%esp),%eax
    80483a4:    8d 44 00 01             lea    0x1(%eax,%eax,1),%eax
    80483a8:    c3                      ret
  2. x86,64bit:
    00000000004004c0 <main>:
    4004c0: 8d 44 3f 01             lea    0x1(%rdi,%rdi,1),%eax
    4004c4: c3                      retq
  3. x86,64bit , -DUSE_SHIFTOR:
    080483a0 <main>:
    80483a0:    8b 44 24 04             mov    0x4(%esp),%eax
    80483a4:    01 c0                   add    %eax,%eax
    80483a6:    83 c8 01                or     $0x1,%eax
    80483a9:    c3                      ret
  4. x86,32bit , -DUSE_SHIFTOR:
    00000000004004c0 <main>:
    4004c0: 8d 04 3f                lea    (%rdi,%rdi,1),%eax
    4004c3: 83 c8 01                or     $0x1,%eax
    4004c6: c3                      retq

事实上,大多数情况都会使用LEA.然而,这两个案例的代码并不相同.这有两个原因:

  1. 另外可以溢出和环绕,而位操作喜欢<<|不能
  2. (x + 1) == (x | 1)只有!(x & 1)在添加到下一位的情况下才有效.通常,仅添加一个导致在一半的情况下设置最低位.

虽然我们(和编译器,可能)知道第二个必然适用,但第一个仍然是可能的.因此,编译器会创建不同的代码,因为"或 - 版本"需要将位0强制为1.


Cra*_*rks 5

除了脑力不足的编译器之外,任何人都会看到这些表达式是等效的,并将它们编译为相同的可执行代码.

通常情况下,优化像这样的简单算术表达式并不值得过多担心,因为它是编译器最擅长优化的东西.(与"智能编译器"可以做正确的事情的许多其他情况不同,但是实际的编译器会失败.)

这将通过PPC,Sparc和MIPS上的相同指令顺序进行:顺序后跟添加.在ARM上,它将煮熟为单个融合的移位添加指令,而在x86上,它可能是单个LEA操作.

  • 是的,可能是`LEA EAX,EAX + EAX + 1`在x86下的迷人方式. (2认同)