是 (x <= y) 或 !(x > y) 更有效率?

Is (x <= y) or ! (x > y) more efficient?

本文关键字:gt 有效率 lt      更新时间:2023-10-16

我想知道这些片段中哪一个更快。

我通常使用这个表达式:

if(x <= y)
    break;

但是使用这个有好处吗?

if(!(x > y))
    break;

以下是我的推理。我认为第一个声明实际上是这样的:

if(x == y)
    break;
if(x < y)
    break;

但我认为第二种说法只是:

if(!(x > y))
     break;

这使我相信第二种说法更快。这是对的吗?

使用gcc -O3 -march=native -m64 -fomit-frame-pointer -S:编译

int f(int x, int y) {
    if (x <= y) return 1;
    return 0;
}
f:
    xorl    %eax, %eax
    cmpl    %edx, %ecx
    setle   %al
    ret

int f(int x, int y) {
    if (!(x > y)) return 1;
    return 0;
}
f:
    xorl    %eax, %eax
    cmpl    %edx, %ecx
    setle   %al
    ret

也就是说,对于整数,它们完全相等——事实上,编译器将第二个例子优化为第一个例子,因为它更快,而不是更慢

编译器不太可能生成与其他编译器不同的东西。几乎所有现代处理器都有greater or equalless or equal比较/分支操作,因此没有理由进行更复杂的比较。

声明

 if(x == y) 
      if (x < y) 
         break;

没有任何意义。要么x == y为真,在这种情况下x < y不为真。或者x == y为false,并且根本不输入第二个if。

显然,如果xy是一个类,那么operator<=可以写成:

operator<=(const A& x, const A& y)
{
    if (x == y) return true;
    return x < y; 
}

但这将是相当愚蠢的,因为它也可以写成:

operator<=(const A& x, const A& y)
{
    return !(x > y); 
}

假设x&y是内置类型,

以下是我的推理。我认为第一个声明实际上是这样的:

if(x == y)
    if(x < y)
        break;

这是不对的。CPU可以执行<=操作。不要过度优化;)

您可以期望任何足够高级的编译器自动优化这些等价语句,使其成为目标体系结构上最快的语句,因此在实践中它们的行为是相同的。

但是,当这些片段被字面解释时,在x86体系结构上,第一个操作将是单个操作,因为x86 CPU指令集既有在较少时跳转的指令,也有在较少或相等时跳转的指示。

就int(或short或long-long或其他)而言,只需使用x <= y,编译器应该对其进行优化。例如,在x86-64:上

    cmpq %rax, %rcx
    jg false
#This is code to execute if (x <= y).
# Code
# .......
false:
    #This is code to execute once the
    # if statement is done or the condition
    # resulted in a falsy value.

如果你有一个If-else语句,有两个跳转指令:第一个是如果条件产生了一个假值,第二个是在代码块的末尾,当条件产生了真值时(这样它就可以跳过用于else块的代码)。

请注意,我使用了一个jg(如果大于则跳转)指令。x86和x86-64都有一条jnle(如果不小于或等于,则跳转)指令,但它做的是相同的事情(毕竟,如果x不小于或小于y,则逻辑上x必须大于y),但从ASM工作的角度来看,反转条件更有意义。如果条件没有反转,您会向前跳以执行if语句的代码,然后向后跳以恢复程序的主流。为什么在两次跳跃中做一次能做的事?

顺便说一句,我不是ASM大师。然而,如果您稍微使用它,它可以帮助您避免提出这样的问题,因为编译器应该将(x <= y)条件优化为!(x > y)代码,正如我所展示的那样。没有必要对编译器进行事后猜测。专注于可以帮助编译器优化代码的常规优化,例如消除最初不需要执行的条件,而不是它已经知道如何执行的小事情。

使用<= 有两个原因

  1. CPU可以对基元执行<=操作。

  2. 如果类有一个重载运算符<=,那么可以肯定地假设它经过了优化,比if(!(x > y))做得更好,或者至少等于它。所以,如果它存在,就使用它。有人努力实现它是有原因的。