Arrays 合并排序--按缓存线大小进行优化?

Arrays 合并排序--按缓存线大小进行优化?,arrays,sorting,caching,mergesort,insertion-sort,Arrays,Sorting,Caching,Mergesort,Insertion Sort,我的一个朋友最近提到,可以通过“缩短”来减少合并排序的实时运行时间。他提到,您不应该将阵列分解为各个块,而应该在各个阵列大小等于缓存线大小的位置停止,因为整个阵列将加载到缓存中。此时,应使用替代排序(即插入排序)合并每个数组,然后完成合并排序 虽然BigO提出了相反的建议,但他的建议似乎有直观的意义。有人能证实或否认这一点,和/或提供更多关于这一点如何以及为什么起作用的信息吗 谢谢你们的帮助 答案(有点抽象)是大O只对大数有用:它抛弃了常数因子:O(n)=O(3n),它抛弃了低阶项:O(n²+3

我的一个朋友最近提到,可以通过“缩短”来减少合并排序的实时运行时间。他提到,您不应该将阵列分解为各个块,而应该在各个阵列大小等于缓存线大小的位置停止,因为整个阵列将加载到缓存中。此时,应使用替代排序(即插入排序)合并每个数组,然后完成合并排序

虽然BigO提出了相反的建议,但他的建议似乎有直观的意义。有人能证实或否认这一点,和/或提供更多关于这一点如何以及为什么起作用的信息吗

谢谢你们的帮助

答案(有点抽象)是大O只对大数有用:它抛弃了常数因子:O(n)=O(3n),它抛弃了低阶项:O(n²+3n)=O(n²)。所以是的,你不能从大O符号中分辨出来

此外,Big-O表示法通常用于一个非常简单的模型,其中每个“操作”只需花费1美元,它不知道缓存


这就是为什么模型没有告诉你这可能有用的原因。我想你可以看看Donald E.Knuth的“排序和搜索”,他在一种虚构的汇编语言上进行运行时分析(但仍然没有考虑缓存,IIRC)。

插入排序组合创建小运行,然后切换到合并排序称为timsort。维基文章:


使用O(Ω、Θ等)分析复杂性只是为了描述算法在输入大小增加时的性能。如果你看一下实际的函数,你会发现随着输入的增加,常数因子变得不那么重要了。总的来说,输入大小决定了功能

但在实践中,常量因素确实很重要(缓存未命中、指令延迟等),这就是通常很少使用的原因。例如,从寄存器读取大约需要从缓存中的最低级别读取的1/5时间(即下一级别的1/5时间,以此类推)。由于它们是数量级,在实践中,缓存成本通常会主导算法的实际性能


插入排序确实相当有效地使用缓存,通常只要数据适合缓存。因为它是连续的,所以它也能与预测器很好地交互。这两个原因通常都是较小的输入更好的原因。另一个很好的例子是,它在技术上是
O(n^2)
,但在实践中仍然大量使用,因为它具有更好的缓存特性。(Python和Java的默认设置)也对较小的输入使用插入排序。

在我的系统Intel 2600K 3.4 ghz上,对psuedo随机数据的4194304个64位无符号整数进行排序的时间:基数排序-203毫秒;合并排序-297毫秒;微软标准:排序-344毫秒;微软标准::稳定排序-375毫秒。排序是。std::stable_排序是一种自底向上的合并排序,它使用一个临时数组,大小为原始数组的1/2,执行一些额外的拷贝和最后的合并步骤。使用位图进行计数排序可以在250毫秒内在我的笔记本电脑(x86_64 Intel(R)Core(TM)i7-3632QM CPU@2.20GHz)上进行排序,即使编译器生成了中断的代码(314ms用于
std::sort
)。它比intro-sort有更高的内存开销,但它像基数排序一样没有伪分支(1/4分支未命中),因此它的平均指令吞吐量仍然更高(2/周期比1.9/周期)我不知道为什么合并排序比它好。我认为在某些情况下,扩展排序也比介绍排序好。