我有一个500GB的文本文件,大约有10亿行需要按字母顺序排序。什么是最好的算法?我可以实施吗?设置有待改进吗?
目前,我正在使用coreutils sort命令:
LANG=C
sort -k2,2 --field-separator=',' --buffer-size=(80% RAM) --temporary-directory=/volatile BigFile
我在AWS EC2上使用120GB RAM& amp; 16核虚拟机。这需要一天的大部分时间。
/ volatile是一个10TB的RAID0阵列。
'LANG = C'技巧提供x2速度增益(感谢1)
默认情况下,'sort'使用50%的可用内存。上升到80-90%会有所改善。
我的理解是gnu'sort'是合并排序算法的变体,其中O(n log n)是最快的:见2& 3。是否会转向QuickSort帮助(我对不稳定的排序很满意)?
我注意到的一件事是只使用了8个核心。这与linux coreutils sort.c中default_max_threads设置为8有关(参见4)。用16?
重新编译sort.c会有帮助吗?谢谢!
后续行动:
@dariusz
我在下面使用了克里斯和你的建议。
由于数据已经批量生成:我分别对每个桶进行分类(在几台不同的机器上),然后使用'sort --merge'函数。像魅力一样工作得快得多:O(log N / K)vs O(log N)。
我还从头开始重新考虑这个项目:现在在生成数据时执行一些数据后处理,以便在排序之前可以丢弃一些不需要的数据(噪声)。
总之,数据大小减少&排序/合并导致大量减少实现我的目标所需的计算资源。
感谢所有有用的评论。
答案 0 :(得分:4)
quicksort优于mergesort的好处是没有额外的内存开销。 mergesort的好处是保证O(n log n)运行时间,其中如果不良的枢轴点采样,快速排序可能会更糟。如果您没有理由担心内存使用,请不要更改。如果这样做,只需确保选择执行实体轴采样的快速排序实现。
我不认为重新编译sort.c会有很大帮助。它可能是微观优化规模。但是你的瓶颈将是内存/磁盘速度,而不是可用的处理器数量。我的直觉是8个线程已经最大化了你的I / O吞吐量,你会发现没有性能提升,但这肯定取决于你的特定设置。
此外,您可以通过利用数据分布获得显着的性能提升。例如,可以通过单个存储桶排序传递非常快速地对均匀分布的数据进行排序,然后使用mergesort对存储桶进行排序。这还有一个额外的好处,即减少mergesort的总内存开销。如果mergesort的内存复杂性为O(N),并且您可以将数据分成K个桶,则新的内存开销为O(N / K)。
答案 1 :(得分:1)
只是一个想法:
我假设文件内容生成了相当长的时间。编写一个应用程序(脚本?),它会定期将直到现在生成的文件移动到另一个位置,将其内容附加到另一个文件,对该文件执行排序,然后重复直到收集到所有数据。
这样你的系统会花更多的时间进行排序,但结果会更快,因为排序部分排序的数据会比排序未排序的数据更快。
答案 2 :(得分:1)
我认为,您需要分两个阶段执行:
这是一个例子。
想象一下,您只有2行限制,输入文件是:
infile中: 0000 0001 技术 0003 五 53 52 7000
在第一次迭代中,您读取输入文件“super-bucket,带有空前缀”,并按照第一个字母进行拆分。
将有3个输出文件:
0: 000 001 002 003
5: (空) 3 2
7: 000
如您所见,带有文件名/前缀7的存储桶只包含一个记录000,即“7000”,分割为7 - filename,以及000 - 字符串尾部。由于这只是一条记录,因此不再需要拆分此文件。但是,文件“0”和“5”包含4个和3个记录,超过限制2.因此,需要再次拆分它们。 分手后:
00: 01 02 03
5: (空)
52: (空)
53: (空)
7: 000
如您所见,前缀为“5”和“7”的文件已经拆分。所以,只需要拆分文件“00”。
如您所见,分割后,您将拥有一组相对较小的文件。 此后,进入第二阶段:
对文件名进行排序,并按照排序顺序处理文件名。 对每个文件进行排序,并将resut附加到输出,并将文件名添加到输出字符串。