哪个运算符更快(>或> =),(<或<=)?

Dim*_*hev 6 c optimization performance assembly operators

是否<<=(和>更快)更便宜(更快)>=

免责声明:我知道我可以测量,但这只会在我的机器上,我不确定答案是否可能是"特定于实现"或类似的东西.

Ric*_*III 12

TL; DR

四个运营商之间似乎差别不大,因为它们几乎在同一时间对我来说(在不同的系统上可能会有所不同!).因此,如果有疑问,只需使用对情况最有意义的运算符(特别是在使用C++时).

所以,不用多说,这是一个很长的解释:

假设整数比较:

就生成程序集而言,结果取决于平台.在我的计算机(Apple LLVM编译器4.0,x86_64)上,结果(生成的程序集如下):

a < b (uses 'setl'):

movl    $10, -8(%rbp)
movl    $15, -12(%rbp)
movl    -8(%rbp), %eax
cmpl    -12(%rbp), %eax
setl    %cl
andb    $1, %cl
movzbl  %cl, %eax
popq    %rbp
ret

a <= b (uses 'setle'):

movl    $10, -8(%rbp)
movl    $15, -12(%rbp)
movl    -8(%rbp), %eax
cmpl    -12(%rbp), %eax
setle   %cl
andb    $1, %cl
movzbl  %cl, %eax
popq    %rbp
ret

a > b (uses 'setg'):

movl    $10, -8(%rbp)
movl    $15, -12(%rbp)
movl    -8(%rbp), %eax
cmpl    -12(%rbp), %eax
setg    %cl
andb    $1, %cl
movzbl  %cl, %eax
popq    %rbp
ret

a >= b (uses 'setge'): 

movl    $10, -8(%rbp)
movl    $15, -12(%rbp)
movl    -8(%rbp), %eax
cmpl    -12(%rbp), %eax
setge   %cl
andb    $1, %cl
movzbl  %cl, %eax
popq    %rbp
ret
Run Code Online (Sandbox Code Playgroud)

这并不是真的告诉我什么.所以,我们跳到一个基准:

女士们,先生们,结果是,我创建了以下测试程序(我知道'时钟'不是计算这样结果的最佳方法,但现在必须要做).

#include <time.h>
#include <stdio.h>

#define ITERS 100000000

int v = 0;

void testL()
{
    clock_t start = clock();

    v = 0;

    for (int i = 0; i < ITERS; i++) {
        v = i < v;
    }

    printf("%s: %lu\n", __FUNCTION__, clock() - start);
}

void testLE()
{
    clock_t start = clock();

    v = 0;

    for (int i = 0; i < ITERS; i++)
    {
        v = i <= v;
    }

    printf("%s: %lu\n", __FUNCTION__, clock() - start);
}

void testG()
{
    clock_t start = clock();

    v = 0;

    for (int i = 0; i < ITERS; i++) {
        v = i > v;
    }

    printf("%s: %lu\n", __FUNCTION__, clock() - start);
}

void testGE()
{
    clock_t start = clock();

    v = 0;

    for (int i = 0; i < ITERS; i++) {
        v = i >= v;
    }

    printf("%s: %lu\n", __FUNCTION__, clock() - start);
}

int main()
{
    testL();
    testLE();
    testG();
    testGE();
}
Run Code Online (Sandbox Code Playgroud)

哪个,在我的机器上(编译-O0),给我这个(5个独立的运行):

testL: 337848
testLE: 338237
testG: 337888
testGE: 337787

testL: 337768
testLE: 338110
testG: 337406
testGE: 337926

testL: 338958
testLE: 338948
testG: 337705
testGE: 337829

testL: 339805
testLE: 339634
testG: 337413
testGE: 337900

testL: 340490
testLE: 339030
testG: 337298
testGE: 337593

我认为这些运算符之间的差异充其量是微不足道的,并且在现代计算世界中并没有太大的重要性.

  • 汇编代码实际上讲了很多.它告诉所有这些片段必须完全相同的时间,所有情况都是平等的.无论`cc`是什么,`setcc`需要1个循环(P4除外,需要3个循环).但这又如何相关呢?比较运算符几乎从未使用过这种方式 - 比较`jcc`的性能(不管`cc`也是如此)似乎更合乎逻辑. (3认同)

old*_*mer 3

它有所不同,首先从检查不同的指令集以及编译器如何使用这些指令集开始。以 openrisc 32 为例,它显然是受到 mips 的启发,但条件的处理方式有所不同。对于or32有比较和设置标志指令,比较这两个寄存器如果小于或等于无符号则设置标志,比较这两个寄存器如果等于则设置标志。然后有两个条件分支指令在标志设置时分支和在标志清除时分支。编译器必须遵循这些路径之一,但小于、小于、小于或等于、大于等都将使用相同数量的指令、条件分支的相同执行时间以及不执行的相同执行时间。条件分支。

现在,对于大多数架构来说,执行分支比不执行分支需要更长的时间,因为必须刷新并重新填充管道,这肯定是正确的。有些人进行分支预测等来帮助解决这个问题。

现在某些架构的指令大小可能会有所不同,比较 gpr0 和 gpr1 与比较 gpr0 和立即数 1234,可能需要更大的指令,例如,您会在 x86 中看到很多这种情况。因此,尽管这两种情况都可能是一个分支,但如果小于您的编码方式,则取决于哪些寄存器恰好保存哪些值可以产生性能差异(当然x86会进行大量的流水线操作,大量的缓存等来弥补这些问题)。另一个类似的例子是 mips 和 or32,其中 r0 始终为零,它并不是真正的通用寄存器,如果你写入它它不会改变,它被硬连线到零,所以如果等于 0 的比较可能会花费你如果等于某个其他数字,则需要一个或两个额外的指令来用该立即数填充 gpr 以便进行比较,最坏的情况是必须将寄存器逐出到堆栈或内存,以释放寄存器寄存器将立即数放在那里,以便可以进行比较。

有些架构有条件执行,例如arm,对于完整的arm(而不是thumb)指令,您可以在每个指令的基础上执行,所以如果您有代码

if(i==7) j=5; else j=9;
Run Code Online (Sandbox Code Playgroud)

arm 的伪代码是

cmp i,#7
moveq j,#5
movne j,#7
Run Code Online (Sandbox Code Playgroud)

没有实际的分支,所以没有管道问题,你可以直接飞轮通过,非常快。

一种架构与另一种架构的比较,如果这是一个有趣的比较,如提到的一些,mips 或 32,您必须专门执行某种指令进行比较,其他架构如 x86、msp430 以及绝大多数每个 alu 操作都会更改标志、arm 和如果您告诉它更改标志,则类似于更改标志,否则不要如上所示。所以一个

while(--len)
{
  //do something
}
Run Code Online (Sandbox Code Playgroud)

循环减去 1 也会设置标志,如果循环中的内容足够简单,您可以使整个事情成为有条件的,因此您可以节省单独的比较和分支指令,并节省管道损失。Mips 通过比较和分支是一条指令解决了这个问题,它们在分支后执行一条指令以在管道中保存一点。

一般的答案是,您不会看到差异,各种条件的指令数量、执行时间等都是相同的。特殊情况(例如小立即数与大立即数等)可能会对极端情况产生影响,或者编译器可能只是根据您进行的比较选择以不同的方式进行操作。如果您尝试重写算法以使其给出相同的答案,但使用小于而不是大于和等于,则您可能会更改代码以获得不同的指令流。同样,如果您执行的性能测试过于简单,编译器可以/将优化比较完成并仅生成结果,这可能会根据导致不同执行的测试代码而有所不同。所有这一切的关键是反汇编你想要比较的东西,看看指令有何不同。这将告诉您是否应该看到任何执行差异。