• 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 的本机编译器)。当场景变得如此复杂以至于编译器无法自动优化它们时,了解分支和条件移动之间的性能差异可以帮助我们编写性能更好的代码。

  • 相关阅读:
    Sql日期时间格式转换
    c#被指定为此窗体的 MdiParent 的窗体不是 MdiContainer?
    kmeans聚类分析
    C# VS2005打开没问题,但是运行解决方案时就整个自动关闭了
    PowerDesigner教程系列(一)概念数据模型
    PowerDesigner概念设计模型(CDM)中的3种实体关系
    spss clementine Twostep Cluster(两步聚类 二阶聚类)
    PowerDesigner教程系列(三)概念数据模型
    Kmeans聚类算法
    c# 中窗体居中代码怎么写?
  • 原文地址:https://www.cnblogs.com/myhomepages/p/15920593.html
Copyright © 2020-2023  润新知