外部合并中的传递次数

时间:2013-03-23 08:25:13

标签: algorithm sorting external-sorting

似乎没有任何先前存在的问题,至少从标题搜索来看。我正在寻找外部合并的最佳通过量。因此,如果我们有1000个数据块,则一次传递将是1000路合并。两遍可以是5组200个块,然后最后合并1组5个块。等等。我做了一些数学运算,这必须有一个缺陷,因为看起来两次传球永远不会超过一次传球。但是,很可能是对数据读取方式的误解。

首先,一个数值例子:

数据:100 GB
Ram:1 GB

由于我们有1GB内存,我们可以一次加载1GB来使用quicksort或mergesort进行排序。现在我们有100个块来排序。我们可以做100路合并。这是通过制作RAM/(chunks+1)尺寸存储桶= 1024MB/101 = 10.14MB来完成的。 100个块中的每个块都有100个10.14MB桶,还有一个大小为10.14MB的输出桶。当我们合并时,如果任何输入存储桶为空,我们会执行磁盘搜索以重新填充该存储桶。同样,当输出桶已满时,我们会写入磁盘并清空它。我声称“磁盘需要读取的次数”为(data/ram)*(chunks+1)。我从我们有ram/(chunks+1)大小的输入桶的事实中得到了这一点,我们必须读取给定传递的整个数据,因此我们阅读(data/bucket_size)次。换句话说,每当输入桶清空时,我们必须重新填充它。我们在此处执行了100多个块,因此numChunks*(chunk_size/bucket_size) = datasize/bucket_size100*(1024MB/10.14MB)。 BucketSize = ram/(chunks+1)因此100*(1024/10.14) = (data/ram) * (chunks+1) = 1024*100MB/1024MB * 101 = 10100读取。

对于双通系统,我们做A组B #chunks,然后最后合并1组A #chunks。使用以前的逻辑,我们有numReads = A*( (data/ram)*(B+1)) + 1*( (data/ram)*(A+1))。我们还有A*B = Data/Ram。例如,10组10个块,其中每个块是GB。这里,A = 10 B = 10. 10 * 10 = 100/1 = 100,即Data/Ram。这是因为Data/Ram是原始数量的块。对于2次传递,我们希望将Data/Ram分成A组B #chunk。

我会尝试在这里细分公式,让D =数据,A = #groups,B =#chunks / group,R = RAM

A*(D/R)*(B+1) + 1*(D/R)*(A+1) - 这是B #chunks上外部合并的读取次数加上A #chunks的最终合并的次数。

A = D/(R*B) => D^2/(B*R^2) * (B+1) + D/R * (D/(R*B)+1)

(D^2/R^2)*[1 + 2/B] + D/R是2遍外部合并的读取次数。对于1次传递,我们有(data/ram)*(chunks+1),其中chunks = data / ram为1次传递。因此,对于一次通过,我们有D^2/R^2 + D/R。我们看到2个传递仅在块大小B变为无穷大时才到达,即使这样,额外的最终合并也会给我们D^2/R^2 + D/R。所以必须有一些关于我缺少的读物,或者我的数学有缺陷。感谢任何花时间帮助我的人!

2 个答案:

答案 0 :(得分:3)

您忽略了从磁盘读取数据块所需的总时间是

的总和
  • 访问时间大致恒定,旋转硬盘驱动器的时间大约为几毫秒。
  • 传输时间取决于数据块的大小和传输速率。

随着块数的增加,输入缓冲区(称为桶)的大小会减小。输入缓冲区越小,常量访问时间对填充缓冲区的总时间的影响就越明显。在某个时刻,填充缓冲区的时间几乎完全由访问时间决定。因此,合并传递的总时间开始随缓冲区的数量而不是读取的数据量进行扩展。

这是其他合并通行证可以加快这一过程的地方。它允许使用更少和更大的输入缓冲区,并减少访问时间的影响。

编辑:这是一个快速的包络计算,可以了解盈亏平衡点的位置。

可以轻松计算总转移时间。所有数据必须每次读取和写入一次:

total_transfer_time = num_passes * 2 * data / transfer_rate

缓冲区读取的总访问时间为:

total_access_time = num_passes * num_buffer_reads * access_time

由于只有一个输出缓冲区,因此可以使其大于输入缓冲区而不会浪费太多内存,因此我将忽略写入的访问时间。缓冲区读取的数量为data / buffer_size,一遍方法的缓冲区大小约为ram / num_chunks,块的数量为data / ram。所以我们有:

total_access_time1 = num_chunks^2 * access_time

对于双遍解决方案,使用sqrt(num_chunks)缓冲区来最小化访问时间是有意义的。因此缓冲区大小为ram / sqrt(num_chunks),我们有:

total_access_time2 = 2 * (data / (ram / sqrt(num_chunks))) * acccess_time
                   = 2 * num_chunks^1.5 * access_time

因此,如果我们使用transfer_rate = 100 MB/saccess_time = 10 msdata = 100 GBram = 1 GB,则总时间为:

total_time1 = (2 * 100 GB / 100 MB/s) + 100^2 * 10 ms
            = 2000 s + 100 s = 2100 s
total_time2 = (2 * 2 * 100 GB / 100 MB/s) + 2 * 100^1.5 * 10 ms
            = 4000 s + 20 s = 4020 s

访问时间的影响仍然非常小。所以让我们将数据更改为1000 GB:

total_time1 = (2 * 1000 GB / 100 MB/s) + 1000^2 * 10 ms
            = 20000 s + 10000 s = 30000 s
total_time2 = (2 * 2 * 1000 GB / 100 MB/s) + 2 * 1000^1.5 * 10 ms
            = 40000 s + 632 s = 40632 s

现在,一次通过版本的一半时间用于磁盘搜索。让我们试试5000 GB:

total_time1 = (2 * 5000 GB / 100 MB/s) + 5000^2 * 10 ms
            = 100000 s + 250000 s = 350000 s
total_time2 = (2 * 2 * 5000 GB / 100 MB/s) + 2 * 5000^1.5 * 10 ms
            = 200000 s + 7071 s = 207071 s

现在双通版本更快。

答案 1 :(得分:2)

要获得最佳效果,您需要更复杂的磁盘模型。让时间填充S大小的rS + k块,其中k是寻求时间,r是读取率。

如果将大小为M的RAM划分为大小为C+1的{​​{1}}缓冲区,则加载RAM一次的时间为M/(C+1) = (C+1) (r M/(C+1) + k)。正如您所期望的那样,通过消除搜索,使rM + k(C+1)更小的速度可以缩短读取时间。在一个顺序块中读取所有内存的速度最快,但合并不允许。我们必须做出权衡。这就是我们需要寻找最佳状态的地方。

如果总数据大小为C倍RAM大小,则需要合并c个块。

在一次通过方案中,c,总读取时间必须只是填充RAM C=c次的时间:c

在第一次传递的c (rM + k(c+1)) = c(rM + kc + k) - 方式数据划分的双通道方案中,该传递具有N,在第二次传递中为C=c/N。所以总费用是

C=N

注意此模型省略了写入时间。你应该最终填写它,除非你假设它在不同的设备上重叠I / O,因此可以忽略。

这里不难看出,如果c ( rM + k(c/N+1) ) + c ( rM + k(N+1) ) = c ( 2rM + k(c/N + N) + 2k ) c适当大,那么2遍模型中的k项与{{1}相比可能会非常小在一遍中,2遍模型会更快。

我现在要停止了,但是你可以将这个逻辑带到(可能)获得任意数量的通过的闭合近似公式。这将需要解决无限系列。然后,您可以将导数设置为零,并求解最佳传递数的估计值。如果生命是好的,你还可以通过将传递数和c/N+N中的2d函数的梯度设置为零来学习c的最佳值。我的直觉说N

如果数学难以处理,你仍然可以在开始时使用上面那种简单代数模拟合理的通过次数范围,然后选择最佳方式。

这是一个有趣的问题,对不起,我现在没有更多的时间来处理它。我希望分析框架足以让你获得良好的结果。