Algorithm 如果数据不适合物理RAM内存,则进行最快排序?

Algorithm 如果数据不适合物理RAM内存,则进行最快排序?,algorithm,performance,sorting,parallel-processing,low-latency,Algorithm,Performance,Sorting,Parallel Processing,Low Latency,我希望在8-128核的系统上对10亿到1000亿个元素的列表进行排序,其中10%的元素使用RAM,磁盘速度为100-1000 MBytes/s 我测试了一个简单的合并排序,其中每个合并都由CPU并行执行: sorted_part_a:__ \__[CPU.1]__ sorted_part_b:__/ \ \__[CPU.5]__ sorted_part_c:__

我希望在8-128核的系统上对10亿到1000亿个元素的列表进行排序,其中10%的元素使用RAM,磁盘速度为100-1000 MBytes/s

我测试了一个简单的合并排序,其中每个合并都由CPU并行执行:

sorted_part_a:__
                \__[CPU.1]__
sorted_part_b:__/           \
                             \__[CPU.5]__
sorted_part_c:__             /           \
                \__[CPU.2]__/             \
sorted_part_d:__/                          \
                                            \__[CPU.7]
sorted_part_e:__                            /
                \__[CPU.3]__               /
sorted_part_f:__/           \             /
                             \__[CPU.6]__/
sorted_part_g:__             /
                \__[CPU.4]__/
sorted_part_h:__/

但这有一个问题,即最后的合并步骤[
CPU.7
]在合并最后两个输入时,必须在单个内核上进行n次比较,而且比较可能会很昂贵(考虑必须遵守区域设置的字符串)。在我的测试中,[
CPU.7
]是瓶颈

然后我看了看红黑相间的树。它们有几个优点:

  • 构建树时,获取排序列表是不进行比较的。这避免了我在合并排序测试中看到的瓶颈
  • 因此,您可以使用多个内核
  • 在开始构建树之前,您不需要所有数据(因此,如果您是从速度较慢的设备读取,您可以在读取时进行排序,这样就不会浪费挂钟时间)
将树保存到磁盘似乎也很容易(只需导出已排序的列表和树的高度),但仅从磁盘获取树的一部分似乎更为棘手

我读过,但它似乎忽略了中等大小数据的常见情况:数据适合服务器的磁盘,但不适合RAM

考虑到硬件(8-128核,10%的元素使用RAM,磁盘提供100-1000 MBytes/s的数据流,1000 iops),对10-100字节的10^9到100*10^9元素列表进行排序的最快方法是什么


用外行的话来说:
在单台服务器上快速排序最大数量的数据时,最可靠的方法是什么?

如果没有定制的软件来帮我完成繁重的工作,我从来没有做过这种事情

但我在谷歌时的标准解决方案是将初始数据存储在分布式文件系统中,进行分布式合并排序,并将最终数据存储在分布式文件系统中。由于最终排序的数据结构存储在块中,这意味着即使在最后一次传递中,每个CPU也只需在其块中进行比较,从而允许整个过程中CPU的完全使用


对于大型数据集,基本上没有一个用例需要它在一个时间、一个地点重复整个过程。相反,强加这种任意的限制只会造成一个不必要的瓶颈。

当我没有定制的软件来帮我完成繁重的工作时,我从来没有做过这种事情

但我在谷歌时的标准解决方案是将初始数据存储在分布式文件系统中,进行分布式合并排序,并将最终数据存储在分布式文件系统中。由于最终排序的数据结构存储在块中,这意味着即使在最后一次传递中,每个CPU也只需在其块中进行比较,从而允许整个过程中CPU的完全使用


对于大型数据集,基本上没有一个用例需要它在一个时间、一个地点重复整个过程。相反,强加这种任意限制只会造成不必要的瓶颈。

在传统的合并中,使用已排序的子文件,最终合并是O(n log k),其中n是项目总数,k是子文件数。基本上,您可以从每个已排序的子文件中构建第一个项目的优先级队列,删除第一个项目,将其写出,然后从具有最小项目的文件中插入下一个项目

但是你可以将合并并行化。假设你有8个子文件。您可以像这样构建合并网络:

    f1    f2    f3    f4    f5    f6    f7    f8
      \  /        \  /        \  /        \  /
       p1          p2          p3          p4
         \__    __/              \__    __/
            \  /                    \  /
             p5                      p6
                \_______    _______/
                        \  /
                         p7

这里的想法是每个处理器核心p1到p4开始合并两个文件。处理器p5和p6各自合并来自两个一级处理器的输出,而p7合并来自它们的结果。p7最终会进行n次比较,而不是进行O(n log k)次比较,如果使用单个CPU内核进行合并,则会进行O(n log k)次比较。

在传统合并中,使用排序的子文件,最终合并是O(n log k),其中n是项目总数,k是子文件数。基本上,您可以从每个已排序的子文件中构建第一个项目的优先级队列,删除第一个项目,将其写出,然后从具有最小项目的文件中插入下一个项目

但是你可以将合并并行化。假设你有8个子文件。您可以像这样构建合并网络:

    f1    f2    f3    f4    f5    f6    f7    f8
      \  /        \  /        \  /        \  /
       p1          p2          p3          p4
         \__    __/              \__    __/
            \  /                    \  /
             p5                      p6
                \_______    _______/
                        \  /
                         p7

这里的想法是每个处理器核心p1到p4开始合并两个文件。处理器p5和p6各自合并来自两个一级处理器的输出,而p7合并来自它们的结果。p7最终进行了n次比较,而不是O(n log k)次比较,如果使用单个CPU核进行合并,它会进行比较。

对于这种情况,bucket sort作为第一阶段非常有效。同样,这类似于我研究了一段时间的问题,这带来了一场有趣的革命:由于磁盘的速度比CPU慢得多,所以您的目标应该是制作一个IO限制的算法。CPU实际上是免费的。最有可能的是,最佳解决方案可以在单个CPU内核中实现。(再说一遍,我的硬盘不支持1GB/s:O)@MooingDuck一个RAID-0阵列很容易达到1GB/s。我认为你反对最后的合并步骤是没有根据的。我的经验是,最后的合并步骤是输出绑定的,即使涉及字符串比较。应该很容易检查。加载十亿个字符串:一半在一个数组中,一半在另一个数组中。对两个数组进行排序。然后将它们合并到磁盘。看看在合并过程中是CPU受限还是I/O受限。对于这种情况,bucket sort作为第一个阶段非常有效。同样,这是simil