• 为什么处理有序的数组比无序的数组更快


    考虑一个铁路枢纽:

    显示铁路路口的图像 Mecanismo 提供的图像,来自 Wikimedia Commons。在 CC-By-SA 3.0 许可下使用。

    现在为了争论,假设这是在 1800 年代——在远距离或无线电通信之前。

    你是一个路口的盲人操作员,你听到火车来了。你不知道它应该走哪条路。你停下火车问司机他们想要哪个方向。然后你适当地设置开关。

    火车很重并且有很大的惯性,所以它们需要很长时间才能启动和减速。

    有没有更好的办法?你猜火车会开往哪个方向!

    如果你猜对了,它会继续。
    如果你猜错了,船长会停下来,后退,然后对你大喊大叫来拨动开关。然后它可以重新启动另一条路径。
    如果你每次都猜对了,火车就永远不用停下来。
    如果您经常猜错,火车将花费大量时间停止、倒车和重新启动。

    考虑一个 if 语句:在处理器级别,它是一个分支指令:

    包含 if 语句的已编译代码的屏幕截图

    你是一个处理器,你看到一个分支。你不知道它会朝哪个方向发展。你做什么工作?您停止执行并等待前面的指令完成。然后你继续沿着正确的路径前进。

    现代处理器很复杂并且有很长的流水线。这意味着他们需要永远“热身”和“减速”。

    有没有更好的办法?你猜这个分支会往哪个方向走!

    如果你猜对了,你继续执行。
    如果你猜错了,你需要刷新管道并回滚到分支。然后,您可以重新启动另一条路径。
    如果你每次都猜对了,执行将永远不会停止。
    如果您经常猜错,您会花费大量时间停止、回滚和重新启动。

    这是分支预测。我承认这不是最好的类比,因为火车只能用旗帜指示方向。但是在计算机中,处理器直到最后一刻才知道分支将走向哪个方向。

    您将如何策略性地猜测以最小化火车必须倒车并沿另一条路径行驶的次数?你看看过去的历史!如果火车 99% 的时间都是左转,那么你猜是左转。如果它交替,那么你交替你的猜测。如果它每 3 次去一个方向,你猜同样…

    换句话说,您尝试识别一种模式并遵循它。这或多或少是分支预测器的工作方式。

    大多数应用程序都有良好的分支。因此,现代分支预测器通常会达到 >90% 的命中率。但是当面对无法识别的模式的不可预测的分支时,分支预测器实际上是无用的。

    进一步阅读:维基百科上的“分支预测器”文章。

    正如上面所暗示的,罪魁祸首是这个 if 语句:
    如果(数据 [c] >= 128)
    总和 += 数据[c];
    注意数据均匀分布在 0 到 255 之间。当对数据进行排序时,大约前一半的迭代不会进入 if 语句。之后,它们都将进入 if 语句。

    这对分支预测器非常友好,因为分支连续多次朝同一个方向移动。即使是一个简单的饱和计数器也能正确预测分支,除了它切换方向后的几次迭代。

    快速可视化:

    T = 采用的分支
    N = 未采用分支

    数据[] = 0, 1, 2, 3, 4, … 126, 127, 128, 129, 130, … 250, 251, 252, …
    分支 = N N N N N … N N T T T … T T T …

       = NNNNNNNNNNNN ... NNNNNNNTTTTTTTTT ... TTTTTTTTTT(易于预测)
    

    但是,当数据完全随机时,分支预测器就变得毫无用处,因为它无法预测随机数据。因此可能会有大约 50% 的错误预测(不比随机猜测好)。

    数据[] = 226, 185, 125, 158, 198, 144, 217, 79, 202, 118, 14, 150, 177, 182, …
    分支 = T, T, N, T, T, T, T, N, T, N, N, T, T, T …

       = TTNTTTTNTNNTTT ...(完全随机 - 无法预测)
    

    可以做什么?

    如果编译器无法将分支优化为条件移动,如果您愿意牺牲可读性来换取性能,您可以尝试一些技巧。

    代替:

    如果(数据 [c] >= 128)
    总和 += 数据[c];
    和:

    int t = (data[c] - 128) >> 31;
    总和 += ~t & 数据[c];
    这消除了分支并用一些按位操作替换它。

    (请注意,这个 hack 并不严格等同于原始 if 语句。但在这种情况下,它对 data[] 的所有输入值都有效。)

    基准测试:Core i7 920 @ 3.5 GHz

    C++ - Visual Studio 2010 - x64 版本

    场景时间(秒)
    分支 - 随机数据 11.777
    分支 - 排序数据 2.352
    无分支 - 随机数据 2.564
    无分支 - 排序数据 2.587
    Java - NetBeans 7.1.1 JDK 7 - x64

    场景时间(秒)
    分支 - 随机数据 10.93293813
    分支 - 排序数据 5.643797077
    无分支 - 随机数据 3.113581453

    https://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-processing-an-unsorted-array/11227902#11227902

  • 相关阅读:
    深度解读昇腾CANN多流并行技术,提高硬件资源利用率
    为什么你买店铺管理软件总是踩雷?实测市面上十几个店铺管理软件,才总结出这三个大坑,行家也难逃过!
    牛客剑指offer刷题算法篇
    平均负载与 CPU 使用率,到底有啥区别?
    springboot注解方式实现aop及常规方式
    2023年09月IDE流行度最新排名
    边缘混合计算智慧矿山视频智能综合管理方案:矿山安全生产智能转型升级之路
    安装显卡驱动报错
    Session共享问题
    1.CF240F TorCoder 线段树区间覆盖(区间排序回文构造)
  • 原文地址:https://blog.csdn.net/wang704987562/article/details/127045067