我们如何使用unix排序更快地排序?

时间:2011-08-16 06:38:00

标签: unix sorting

我们正在对包含37个字段的5GB文件进行排序,并使用5个键对其进行排序。大文件由1000个文件组成,每个文件5MB。

190分钟后仍未完成。

我想知道是否有其他方法可以加快排序速度。我们选择unix排序因为我们不希望它耗尽所有内存,所以任何基于内存的方法都不行。

独立排序每个文件的优点是什么,然后使用-m选项合并排序呢?

6 个答案:

答案 0 :(得分:39)

使用-S将其缓冲在内存中。例如,要使用(最多)50%的内存作为排序缓冲区:

sort -S 50% file

请注意,现代Unix sort可以并行排序。我的经验是它会自动使用尽可能多的内核。您可以使用--parallel直接设置它。要使用4个线程进行排序:

sort --parallel=4 file

总而言之,你应该将所有内容放在一个文件中并执行如下操作:

sort -S 50% --parallel=4 file

答案 1 :(得分:6)

  1. 分而治之。如果您首先对每个N进行排序,那么N种文件会更快 文件(并在多处理器上使用不同的CPU)。然后只需要合并文件(例如sort -m files ...; -m是POSIX,并且应该受到各种排序的支持;双关语意图)。对每个文件进行排序会消耗更少的资源。
  2. 给一个快速/ tmp目录排序
  3. 在框外思考:让创建文件的过程立即对数据进行排序
  4. 蛮力:在问题上投入更多硬件(内存,CPU周期): - )
  5. 了解external sorting
  6. 的概念

答案 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