stackoverflow:为什么排序后的数组要比未排序数组运行快3倍以上?

简介:

周末,在 stackoverflow 上面发现一个有趣的问题:Why is it faster to process a sorted array than an unsorted array?


a0402616c4c90e890ca78a32bae4d2f291c5aac7

提问者用256的模数随机填充一个固定大小的大数组,然后对数组的一半元素求和,并分别用 C++ 和 Java 代码来证实了这一现象,本文主要用 Java 来说明,代码如下。


9159e748b29ce57ae3d90b99d4c2c66534c0b6b1

我通过 IDEA 运行发现,先排序后计算比不排序直接计算快三倍。

那么,问题来了,本例子中的代码在数组填充时已经加入了分区函数,充分保证填充值的随机性,计算时也是按一半的元素来求和,所以不存在特例情况。而且,计算也完全不涉及到数据的有序性,即数组是否有序理论上对计算不会产生任何作用。在这样的前提下,为什么排序后的数组要比未排序数组运行快3倍以上?

我看获赞最高的答案提到了一个关键字:分支预测器

它的英文名叫做 Branch predictor,是一种数字电路,在分支指令执行结束之前猜测哪一路分支将会被运行,以提高处理器的指令流水线的性能。使用分支预测器的目的,在于改善指令管线化的流程。—— 引自维基百科

这个分支预测器,跟在无线电还未普及的 19 世纪的铁路交叉口的扳道工很相似,假如你就是一个搬道工,当听到火车快来了的时候,你无法猜测它应该朝哪个方向走。于是你叫停了火车,上前去问火车司机该朝哪个方向走,以便你能正确地切换铁轨。


fbc5cc61c0636792b6d8b96b0c08e96a8c333f89要知道,火车是非常庞大的,切急速行驶时有巨大的惯性。为了完成上述停车-问询-切轨的一系列动作,火车需耗费大量时间减速,停车,重新开启。

既然上述过程非常耗时,那是否有更好的方法?当然有!当火车即将行驶过来前,你可以猜测火车该朝哪个方向走。

如果猜对了,它直接通过,继续前行。

如果猜错了,车头将停止,倒回去,你将铁轨扳至反方向,火车重新启动,驶过道口。

如果你不幸每次都猜错了,那么火车将耗费大量时间,停车-倒回-重启。

如果你很幸运,每次都猜对了呢?火车将从不停车,持续前行!

我相信谁都不会一直都有好运,那有没有好的方法使得每次猜对的几率变大呢?是有的,可以通过小旗子来作为信号判断火车的走向。

是不是很形象?这样你就知道引起本文代码出现这一现象的关键原因在于分支跳转指令


c01c0f31273aefaaed1ee97bf9fc5c88ffd8d69d

那么问题又来了,处理器是采用什么策略用最小的次数来尽量猜对指令分支的下一步走向呢?

一般来说,对于处理器而言,只能利用历史运行记录来进行分析。

由于上例 data 数组里的元素是按照 0-255 的值被均匀存储的,数组 data 有序时,前面一半元素的迭代将不会进入 if-statement,超过一半时,元素迭代将全部进入 if-statement。

具体分析如下。

有序数组的分支预测流程:


a34a52ebfea6bbdb1eaa07e0d13a7871a901d10e

无序数组的分支预测流程:


dfd04e4c88f614a725ab6684ebc50ff187293245

所以,咱们可以得出结论:造成无序数组耗时较多的原因在于它迭代过程中 50% 的错误率。

那问题来了,对于这个例子,有没有好的优化方案呢?

是有的,可以利用位运算来取消分支跳转,代码如下。


ccd5a3cf4eec5bf2952174b8d9ce1940b2996f44

更多的位运算 hack 操作,可以参考 bithacks。

这个例子是不是很有趣啊?

如果你涨知识了,记得转发哦。

参考

https://en.wikipedia.org/wiki/Branch_predictor

http://graphics.stanford.edu/~seander/bithacks.html

https://stackoverflow.com/questions/11227809/why-is-it-faster-to-process-a-sorted-array-than-an-unsorted-array


原文发布时间为: 2018-11-27
本文作者:Java面试那些事儿
本文来自云栖社区合作伙伴“Java面试那些事儿”,了解相关信息可以关注“Java面试那些事儿”。

相关文章
|
缓存 资源调度 前端开发
从0到1带你用webpack 5构建monorepo项目——上篇(二)
别名配置 对于ts+webpack 的「monorepo」项目 别名的配置有 两种 1. 第一个是在tsConfig 中的别名配置, 这个配置的好处方便 子项目中 互相引用 2. 第二个是在webpack 中配置别名, 为了可以少写很长路径, 同时打包的时候也能找到文件。 1. 如上图 我 在 「3d」 这个项目 我想引用 「utils」 中的方法, 首先这两个项目 分别都 是单独的项目, 都有自己的「tsConfig.json」 文件 为了 防止ts 报错 我们项目根目录的 「tsConfig.json」 配置下别名 "baseUrl": "./packages", // 根路径 路径
从0到1带你用webpack 5构建monorepo项目——上篇(二)
|
网络协议 安全 Linux
网卡接口跃点数:概念与重要性解析
在计算机网络中,跃点数(Hop Count)是指数据包从源设备传输到目标设备时经过的路由器或网关数量,是衡量路径长度的关键指标。本文详细介绍了跃点数的概念、计算方法及其在网络管理中的重要性,包括性能评估、故障排除、网络优化及路由选择等方面的应用。通过使用traceroute或tracert命令,网络管理员可以轻松获取跃点数信息,并据此优化网络结构,提高数据传输效率和安全性。尽管跃点数是重要指标,但仍需与其他因素结合分析以全面评估网络性能。
2071 verbose node v16.6.0 2072 verbose npm v7.19.1或者 no such file or directory, lstat ‘D:\wor
该博客文章提供了解决在使用npm版本7.19.1时出现的"no such file or directory"错误的具体方法,建议通过降级npm到6.14.8版本来解决问题,并确认了该方法可以成功安装node_modules。
2071 verbose node v16.6.0 2072 verbose npm v7.19.1或者 no such file or directory, lstat ‘D:\wor
|
数据采集 前端开发 JavaScript
【面试题】常见前端基础面试题(HTML,CSS,JS)
【面试题】常见前端基础面试题(HTML,CSS,JS)
1038 0
|
Ubuntu
Ubuntu19.10安装搜狗输入法
Ubuntu19.10安装搜狗输入法
265 0
|
存储 物联网 Apache
阿里巴巴布道师冯嘉:分布式消息系统的现状、挑战与未来
本文帮助大家了解对于分布式消息系统,传统的消息系统以及Messaging生态面临的挑战;结合阿里巴巴自身的开源实践,提供如何应对以上挑战的参考答案,以及OpenMessaging生态现状和未来的发展方向。
4703 0
阿里巴巴布道师冯嘉:分布式消息系统的现状、挑战与未来
|
关系型数据库 MySQL 数据库
MySQL collate的选择
MySQL collate的选择
337 0
MySQL collate的选择

热门文章

最新文章

下一篇
开通oss服务