为什么三元和逻辑运算符比分支更有效?

时间:2015-06-03 13:43:54

标签: performance operation branch-prediction

我偶然发现this question/answer提到了大多数语言中的逻辑运算符,例如:

x == y && doSomething();

可能比使用if分支做同样的事情更快:

if(x == y) {
  doSomething();
}

同样,它说三元运算符:

x = y == z ? 0 : 1

通常比使用if分支更快:

if(y == z) {
  x = 0;
} else {
  x = 1;
}

这让我得到了谷歌搜索,这导致我this fantastic answer解释了分支预测。

基本上,它所说的是CPU以非常快的速度运行,而不是减速以计算每个if分支,它试图猜测将发生什么结果并将适当的指令放在其管道中。但如果它做出了错误的猜测,就必须备份并重新计算相应的指令。

但是这仍然没有向我解释为什么逻辑运算符或三元运算符的处理方式与if分支不同。由于CPU不知道x == y的结果,不应该猜测是否要将调用发送到doSomething()(因此,doSomething全部(#)代码)进入其管道?因此,如果猜测不正确,请备份吗?类似地,对于三元运算符,在确定y == z中要存储的内容时,CPU是否不得不猜测x是否会评估为真,如果猜测错误则需要备份?

我不明白为什么编译器对分支的处理方式与有条件的任何其他语句的处理方式不同。不应该以同样的方式评估所有条件吗?

1 个答案:

答案 0 :(得分:3)

简短的回答 - 它根本就不是。虽然帮助分支预测可以提高性能 - 使用它作为一个部分,逻辑语句不会改变编译的代码。 如果你想帮助分支预测使用__builtin_expect(对于GNU)

要强调让我们比较编译器输出:

#include <stdio.h>


int main(){
        int foo;

        scanf("%d", &foo); /*Needed to eliminate optimizations*/

#ifdef IF       
        if (foo)
                printf("Foo!");
#else
        foo &&  printf("Foo!");
#endif 
        return 0;
}

对于gcc -O3 branch.c -DIF 我们得到:

0000000000400540 <main>:
  400540:       48 83 ec 18             sub    $0x18,%rsp
  400544:       31 c0                   xor    %eax,%eax
  400546:       bf 68 06 40 00          mov    $0x400668,%edi
  40054b:       48 8d 74 24 0c          lea    0xc(%rsp),%rsi
  400550:       e8 e3 fe ff ff          callq  400438 <__isoc99_scanf@plt>
  400555:       8b 44 24 0c             mov    0xc(%rsp),%eax
  400559:       85 c0                   test   %eax,%eax #This is the relevant part
  40055b:       74 0c                   je     400569 <main+0x29>
  40055d:       bf 6b 06 40 00          mov    $0x40066b,%edi
  400562:       31 c0                   xor    %eax,%eax
  400564:       e8 af fe ff ff          callq  400418 <printf@plt>
  400569:       31 c0                   xor    %eax,%eax
  40056b:       48 83 c4 18             add    $0x18,%rsp
  40056f:       c3                      retq 

对于gcc -O3 branch.c

0000000000400540 <main>:
  400540:       48 83 ec 18             sub    $0x18,%rsp
  400544:       31 c0                   xor    %eax,%eax
  400546:       bf 68 06 40 00          mov    $0x400668,%edi
  40054b:       48 8d 74 24 0c          lea    0xc(%rsp),%rsi
  400550:       e8 e3 fe ff ff          callq  400438 <__isoc99_scanf@plt>
  400555:       8b 44 24 0c             mov    0xc(%rsp),%eax
  400559:       85 c0                   test   %eax,%eax
  40055b:       74 0c                   je     400569 <main+0x29>
  40055d:       bf 6b 06 40 00          mov    $0x40066b,%edi
  400562:       31 c0                   xor    %eax,%eax
  400564:       e8 af fe ff ff          callq  400418 <printf@plt>
  400569:       31 c0                   xor    %eax,%eax
  40056b:       48 83 c4 18             add    $0x18,%rsp
  40056f:       c3                      retq 

这是完全相同的代码。

您与JAVAScript的衡量绩效相关联的问题。请注意,它可以被解释(因为Java脚本被解释或JIT取决于版本)到两种情况不同的东西。 无论如何,JavaScript并不是学习性能的最佳选择。