2012-08-01 63 views
6

<便宜(快)比<=,同样,是>便宜(快)比>=哪个运算符更快(>或> =),(<或<=)?

声明:我知道我可以测量,但那只会在我的机器上,我不确定答案可能是“具体实现”还是类似的东西。

+12

我猜想他们在大多数架构中都可以编译成单一指令,但答案是:谁在乎? – meagar 2012-08-01 16:17:50

+1

它们在生成汇编指令方面几乎相同,如果这就是您要求的。 – 2012-08-01 16:18:04

+8

我明白你的问题背后的精神,但是:你是出于学术兴趣问这个问题,还是因为你认为这可能会影响你的应用的性能?它不会。如果存在的话,这个差异绝对会被你的应用中的其他因素所淹没**。不是2或10倍,而是1m或更多。我敢打赌你根本无法衡量它。 – 2012-08-01 16:19:38

回答

3

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

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

现在一些体系结构的指令大小可能会有所不同,比较gpr0和gpr1与比较gpr0和立即数1234,可能需要更大的指令,例如x86中会看到很多。所以虽然这两种情况可能是一个分支,如果少于你如何编码,取决于哪些寄存器碰巧保持什么值会导致性能差异(当然,x86会做很多流水线处理,大量缓存等来弥补这些问题)。另一个类似的例子是mips和or32,其中r0总是零,它不是一个真正的通用寄存器,如果你写它,它不会改变,它是硬连线到零,所以比较,如果等于0可能会花费你如果需要额外的指令或两个指令来填充gpr以使比较可能发生,最坏的情况是必须将寄存器驱​​逐到堆栈或存储器,释放注册立即在那里,以便可以发生比较。

某些架构,条件执行像手臂,为全面臂(未拇指)的指令,你可以在每个指令的基础上执行的,所以如果你有代码

if(i==7) j=5; else j=9; 

对手臂的伪代码将是

cmp i,#7 
moveq j,#5 
movne j,#7 

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

一个体系结构到另一个如果这是一个有趣的比较一些提到的,mips或32,你必须专门执行某种比较的指令,其他像x86,msp430和绝大多数每个alu操作改变标志,手臂等改变标志,如果你告诉它改变标志,否则不要如上所示。所以

while(--len) 
{ 
    //do something 
} 

环1减法还设置了标志,如果在循环中的东西是很简单的,你可以使整个事情的条件,因此可以节省您的独立的比较和分支指令并且您在保存管道罚款。 Mips通过比较解决了这一点,并且分支是一条指令,并且它们在分支之后执行一条指令以在管道中保存一点。

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

10

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 

这是不是真的告诉我了。所以,我们跳到基准测试:

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

#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(); 
} 

其中,我的机器(带-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 

我认为,这些运营商之间的差异是次要的,在最好的,不要在现代计算机世界中不占重要地位。

+3

汇编代码实际上告诉了很多。这是说,所有这些片段必须完全相同,所有情况都是平等的。 'setcc'需要1个周期(P4除外,需要3个周期),而不管cc是什么。但这又如何相关?比较运算符几乎从不以这种方式使用 - 比较'jcc'的性能(不管cc是什么)似乎更合理。 – harold 2012-08-01 17:15:53

+1

我第二@harold。程序集讲述了很多 - 所有的比较都是使用相同的'cmpl'指令完成的,它比较了它的参数。基本上它从第一个参数中减去第二个参数(丢弃结果),ALU设置标志寄存器中的相应位。然后可以对这些进行测试,根据或用于设置内存/寄存器值。 – 2012-08-01 18:17:26

+1

@harold他大概的意思是“它不会告诉_me_多” – hirschhornsalz 2012-08-01 18:43:33

相关问题