java 为什么处理排序数组比处理未排序数组更快?

注:本文转自《白煮蛋的博客》  

数据排序后性能显着提高的原因是分支预测惩罚被删除,正如Mysticial 的回答中所解释的那样。

现在,如果我们看一下代码

if (data[c] >= 128)
    sum += data[c];

我们可以发现这个特定if... else...分支的含义是在满足条件时添加一些东西。这种类型的分支可以很容易地转换为条件移动语句,该语句将在系统中编译为条件移动指令:cmovlx86分支和潜在的分支预测惩罚被移除。

C因此,C++中,将直接编译(没有任何优化)到条件移动指令中的语句x86是三元运算符... ? ... : ...所以我们把上面的语句改写成等价的:

sum += data[c] >=128 ? data[c] : 0;

在保持可读性的同时,我们可以检查加速因子。

在 Intel Core i7 -2600K @ 3.4 GHz 和 Visual Studio 2010 发布模式上,基准测试为:

x86

设想时间(秒)
分支 - 随机数据 8.885
分支 - 排序数据 1.528
无分支 - 随机数据 3.716
无分支 - 排序数据 3.71

x64

设想时间(秒)
分支 - 随机数据 11.302
分支 - 排序数据 1.830
无分支 - 随机数据 2.736
无分支 - 排序数据 2.737

结果在多次测试中是稳健的。当分支结果不可预测时,我们得到了很大的加速,但当它是可预测的时,我们会受到一点影响。事实上,当使用条件移动时,无论数据模式如何,性能都是相同的。

x86现在让我们通过调查它们生成的程序集来更仔细地观察。为简单起见,我们使用两个函数max1max2

max1使用条件分支if... else ...

int max1(int a, int b) {
    if (a > b)
        return a;
    else
        return b;
}

max2使用三元运算符... ? ... : ...

int max2(int a, int b) {
    return a > b ? a : b;
}

在 x86-64 机器上,GCC -S生成下面的程序集。

:max1
    movl    %edi, -4(%rbp)
    movl    %esi, -8(%rbp)
    movl    -4(%rbp), %eax
    cmpl    -8(%rbp), %eax
    jle     .L2
    movl    -4(%rbp), %eax
    movl    %eax, -12(%rbp)
    jmp     .L4
.L2:
    movl    -8(%rbp), %eax
    movl    %eax, -12(%rbp)
.L4:
    movl    -12(%rbp), %eax
    leave
    ret

:max2
    movl    %edi, -4(%rbp)
    movl    %esi, -8(%rbp)
    movl    -4(%rbp), %eax
    cmpl    %eax, -8(%rbp)
    cmovge  -8(%rbp), %eax
    leave
    ret

max2由于使用指令,使用更少的代码cmovge但真正的收获是max2不涉及分支跳转,jmp如果预测结果不正确,这将产生显着的性能损失。

那么为什么有条件的移动表现更好呢?

在典型的x86处理器中,一条指令的执行分为几个阶段。粗略地说,我们有不同的硬件来处理不同的阶段。所以我们不必等待一条指令完成来开始新的指令。这称为流水线

在分支情况下,后面的指令是由前面的指令决​​定的,所以我们不能进行流水线操作。我们必须等待或预测。

在条件移动情况下,执行条件移动指令分为几个阶段,但前面的阶段喜欢Fetch并且Decode不依赖于前一条指令的结果;只有后期需要结果。因此,我们等待一条指令执行时间的一小部分。这就是为什么当预测很容易时条件移动版本比分支慢的原因。

Computer Systems: A Programmer's Perspective》一书,第二版详细解释了这一点。您可以查看第 3.6.6 节中的条件移动指令、整个第 4 章中的处理器架构以及第 5.11.2 节中有关分支预测和错误预测惩罚的特殊处理。

有时,一些现代编译器可以将我们的代码优化为具有更好性能的汇编,有时一些编译器不能(有问题的代码使用 Visual Studio 的本机编译器)。当场景变得如此复杂以至于编译器无法自动优化它们时,了解分支和条件移动之间的性能差异可以帮助我们编写性能更好的代码。

posted @ 2022-02-21 20:49  small_123  阅读(40)  评论(0编辑  收藏  举报