我们正在对包含37个字段的5GB文件进行排序,并使用5个键对其进行排序。大文件由1000个文件组成,每个文件5MB。
190分钟后仍未完成。
我想知道是否有其他方法可以加快排序速度。我们选择unix排序因为我们不希望它耗尽所有内存,所以任何基于内存的方法都不行。
独立排序每个文件的优点是什么,然后使用-m选项合并排序呢?
答案 0 :(得分:39)
使用-S
将其缓冲在内存中。例如,要使用(最多)50%的内存作为排序缓冲区:
sort -S 50% file
请注意,现代Unix sort
可以并行排序。我的经验是它会自动使用尽可能多的内核。您可以使用--parallel
直接设置它。要使用4个线程进行排序:
sort --parallel=4 file
总而言之,你应该将所有内容放在一个文件中并执行如下操作:
sort -S 50% --parallel=4 file
答案 1 :(得分:6)
sort -m files
...
; -m是POSIX,并且应该受到各种排序的支持;双关语意图)。对每个文件进行排序会消耗更少的资源。答案 2 :(得分:4)
Unix sort
的主要消费者之一是找到密钥;这不过是您在简单的排序练习中常见的简单比较操作。即使找到其中一个键也是一个非常缓慢的过程。
因此,加快速度的一种方法是让sort
更容易找到密钥,通过预处理文件,使你提到的5个密钥位于每行的前面,然后排序数据(可能使用其他人建议的拆分和合并操作),然后删除密钥。
例如,如果您有冒号分隔的字段,并且排序键是1,3,7,10,12,并且它们都是常规的字母排序,那么您可以使用:
awk -F: '{print "%s:%s:%s:%s:%s:%s\n", $1, $3, $7, $10, $12, $0; }' monster-file |
sort -t: -k1,1 -k2,2 -k3,3 -k4,4 -k5,5 |
sed 's/^[^:]*:[^:]*:[^:]*:[^:]*:[^:]*://'
您甚至可以不使用五个-k
选项,只需运行sort -t:
即可。实际上,您可以安排使用不同的分隔符(可能是控制字符,如^ A)来简化代码。您可以使用以下替代字符将主要记录中的关键字段分开:
awk -F: '{print "%s:%s:%s:%s:%s^A%s\n", $1, $3, $7, $10, $12, $0; }' monster-file |
sort -t$'\001' |
sed 's/^[^^A]*^A//'
这会在bash
的{{1}}参数中使用$'\001'
- ism(ANSI-C Quoting); sort
和^A
脚本中的awk
项是您输入 Control-A 时获得的项目,但您也可以安排sed
也提供角色的符号:
bash
(警告:未经测试的脚本。)
有一篇关于重新设计Unix排序('构建工作排序例程的理论与实践',JP Linderman,AT& T贝尔实验室技术期刊,1984年10月)的一篇引人入胜的文章,这篇文章并不容易获得(I'尽管多次尝试搜索它,但仍没有在互联网上找到它,这描述了awk -F: '{print "%s:%s:%s:%s:%s'$'\001''%s\n", $1, $3, $7, $10, $12, $0; }' monster-file |
sort -t$'\001' |
sed "s/^[^$'\001']*$'\001'//"
如何得到改善。即使在完成所有改进之后,其对复杂类型的建议之一也完全符合这些方针。
答案 3 :(得分:2)
通过任何方式,Unix排序并不是最快的排序。它使用了一个奇怪的实现,可以很容易地超过足够大的数据集,需要多次合并传递,正如你的那样。我会四处寻找替代品。您甚至可以考虑将文件加载到数据库中:您可能会以这种方式获得更好的性能,之后您肯定会以更方便的形式获得数据。
为了完整性,主要问题是存储桶排序本身。它对于小型数据集来说很快,虽然不如Quicksort快,但它产生的次数是替换选择的两倍。一旦进入多级合并,运行次数和合并次数完全占据了CPU绑定的分配阶段。
我多年前为COBOL实现了一个排序合并包,直接来自Knuth vol。 III,通过更换选择进行分配,并通过虚拟运行进行平衡合并。在足够大的数据集上,它很容易胜过Unix排序,随着N的增加,梯度逐渐增大,而且“足够大”并不是那么大的特定磁盘大小。
答案 4 :(得分:2)
将文件拆分为较小的文件,使用许多cpus对较小的文件进行排序,然后重新合并。
我过去做过这个:
split -l5000000 data.tsv '_tmp';
ls -1 _tmp* | while read FILE; do sort $FILE -o $FILE & done;
sort -m _tmp* -o data.tsv.sorted
对我来说效果很好。
20M行文件的示例性能:
joshua10.13> wc randn20M.csv
20000000 20000000 163197726 randn20M.csv
joshua10.14> cat par_srt.sh
#!/bin/bash
split -l5000000 randn20M.csv '_tmp';
ls -1 _tmp* | while read FILE; do sort $FILE -o $FILE & done;
sort -m _tmp* -o data.tsv.sorted
joshua10.15> time ./par_srt.sh
1.516u 0.344s 0:05.85 31.6% 0+0k 0+522584io 0pf+0w
joshua10.16> time sort randn20M.csv -o dataone.sorted
21.461u 0.596s 0:24.08 91.5% 0+0k 0+318752io 0pf+0w
备注:如果您受I / O限制(例如20行的20g文件),那么这根本没有帮助。
答案 5 :(得分:1)
另一个优化:
LC_ALL=C sort ...
例如,如果您有带引号的 csv 文件并且需要在 col2、col1 上按数字排序,则使用:
-t',' -k2.2,2n -k1.2,1n
并在将文件合并 (sort -m) 到单个输出文件之前对文件进行预排序,请使用
LC_ALL=C ls -1 *.csv |
xargs -d'\n' -n1 -P4 -I'{}' sort ... -o '{}' '{}'
LC_ALL sort -m --parallel=20 --buffer-size=30% ... -o sort.out *.csv