x > -1 与 x >= 0,是否存在性能差异
我曾经听过一位老师放弃这个,从那以后它一直困扰着我。假设我们要检查整数是否大于或等于 0。有两种方法可以检查这一点:x
if (x > -1){
//do stuff
}
和
if (x >= 0){
//do stuff
}
根据这位老师的说法,那会稍微快一点。在这种情况下,它是Java,但据他说,这也适用于C,c ++和其他语言。这种说法有道理吗?>
>=
我曾经听过一位老师放弃这个,从那以后它一直困扰着我。假设我们要检查整数是否大于或等于 0。有两种方法可以检查这一点:x
if (x > -1){
//do stuff
}
和
if (x >= 0){
//do stuff
}
根据这位老师的说法,那会稍微快一点。在这种情况下,它是Java,但据他说,这也适用于C,c ++和其他语言。这种说法有道理吗?>
>=
它在很大程度上取决于底层架构,但任何差异都将是微不足道的。
如果有的话,我预计会稍微快一点,因为在某些指令集(如ARM)上免费比较。(x >= 0)
0
当然,任何明智的编译器都会选择最佳的实现,而不管源代码中是哪个变体。
在任何现实世界的意义上都没有区别。
让我们看一下各种编译器为各种目标生成的一些代码。
-O2
/Ox
-Oh
使用以下模块:
void my_puts(char const* s);
void cmp_gt(int x)
{
if (x > -1) {
my_puts("non-negative");
}
else {
my_puts("negative");
}
}
void cmp_gte(int x)
{
if (x >= 0) {
my_puts("non-negative");
}
else {
my_puts("negative");
}
}
以下是他们每个人为比较操作生成的内容:
MSVC 11 针对 ARM:
// if (x > -1) {...
00000 |cmp_gt| PROC
00000 f1b0 3fff cmp r0,#0xFFFFFFFF
00004 dd05 ble |$LN2@cmp_gt|
// if (x >= 0) {...
00024 |cmp_gte| PROC
00024 2800 cmp r0,#0
00026 db05 blt |$LN2@cmp_gte|
面向 x64 的 MSVC 11:
// if (x > -1) {...
cmp_gt PROC
00000 83 f9 ff cmp ecx, -1
00003 48 8d 0d 00 00 // speculative load of argument to my_puts()
00 00 lea rcx, OFFSET FLAT:$SG1359
0000a 7f 07 jg SHORT $LN5@cmp_gt
// if (x >= 0) {...
cmp_gte PROC
00000 85 c9 test ecx, ecx
00002 48 8d 0d 00 00 // speculative load of argument to my_puts()
00 00 lea rcx, OFFSET FLAT:$SG1367
00009 79 07 jns SHORT $LN5@cmp_gte
面向 x86 的 MSVC 11:
// if (x > -1) {...
_cmp_gt PROC
00000 83 7c 24 04 ff cmp DWORD PTR _x$[esp-4], -1
00005 7e 0d jle SHORT $LN2@cmp_gt
// if (x >= 0) {...
_cmp_gte PROC
00000 83 7c 24 04 00 cmp DWORD PTR _x$[esp-4], 0
00005 7c 0d jl SHORT $LN2@cmp_gte
GCC 4.6.1 定位 x64
// if (x > -1) {...
cmp_gt:
.seh_endprologue
test ecx, ecx
js .L2
// if (x >= 0) {...
cmp_gte:
.seh_endprologue
test ecx, ecx
js .L5
GCC 4.6.1 定位 x86:
// if (x > -1) {...
_cmp_gt:
mov eax, DWORD PTR [esp+4]
test eax, eax
js L2
// if (x >= 0) {...
_cmp_gte:
mov edx, DWORD PTR [esp+4]
test edx, edx
js L5
GCC 4.4.1 针对 ARM:
// if (x > -1) {...
cmp_gt:
.fnstart
.LFB0:
cmp r0, #0
blt .L8
// if (x >= 0) {...
cmp_gte:
.fnstart
.LFB1:
cmp r0, #0
blt .L2
IAR 5.20 针对 ARM Cortex-M3:
// if (x > -1) {...
cmp_gt:
80B5 PUSH {R7,LR}
.... LDR.N R1,??DataTable1 ;; `?<Constant "non-negative">`
0028 CMP R0,#+0
01D4 BMI.N ??cmp_gt_0
// if (x >= 0) {...
cmp_gte:
80B5 PUSH {R7,LR}
.... LDR.N R1,??DataTable1 ;; `?<Constant "non-negative">`
0028 CMP R0,#+0
01D4 BMI.N ??cmp_gte_0
如果你仍然和我在一起,以下是评估和显示之间的任何注释的差异:(x > -1)
(x >= 0)
cmp r0,#0xFFFFFFFF
(x > -1)
cmp r0,#0
(x >= 0)
(x >= 0)
cmp ecx, -1
(x > -1)
test ecx, ecx
(x >= 0)
(x >= 0)
请注意,GCC和IAR为两种比较生成了相同的机器代码(可能除了使用哪个寄存器)。因此,根据这项调查,它似乎有一个非常小的机会“更快”。但是,无论最小较短的操作码字节编码可能具有什么优势(我强调可能具有)肯定会被其他因素完全掩盖。(x >= 0)
如果你发现Java或C#的jitted输出有什么不同,我会感到惊讶。我怀疑即使对于像8位AVR这样非常小的目标,您也不会发现任何值得注意的差异。
简而言之,不要担心这种微优化。我认为我在这里写的文章已经花费了更多的时间,而不是在我有生之年执行它们的所有CPU上累积的这些表达式的性能的任何差异。如果你有能力测量性能的差异,请把你的努力放在更重要的事情上,比如研究亚原子粒子的行为。