我有一个 100 M 行文件,可放入 GNU/Linux 系统的 RAM 中。
这相当慢:
sort bigfile > bigfile.sorted
并且在我的机器上没有使用全部 48 个核心。
如何快速对该文件进行排序?
答案1
假设您有 48 个内核、500 GB 可用 RAM,文件有 100 M 行并且适合内存。
如果你使用普通排序,它会相当慢:
$ time sort bigfile > bigfile.sort
real 4m48.664s
user 21m15.259s
sys 0m42.184s
您可以通过忽略您的区域设置来使其更快一点:
$ export LC_ALL=C
$ time sort bigfile > bigfile.sort
real 1m51.957s
user 6m2.053s
sys 0m42.524s
您可以通过告诉排序使用更多核心来使其更快:
$ export LC_ALL=C
$ time sort --parallel=48 bigfile > bigfile.sort
real 1m39.977s
user 15m32.202s
sys 1m1.336s
您还可以尝试为 sort 提供更多工作内存(如果 sort 已经有足够的内存,这没有帮助):
$ export LC_ALL=C
$ time sort --buffer-size=80% --parallel=48 bigfile > bigfile.sort
real 1m39.779s
user 14m31.033s
sys 1m0.304s
但看起来确实很喜欢做很多单线程。您可以通过以下方式强制它更多地并行化:
$ merge() {
if [ $1 -le 1 ] ; then
parallel -Xj1 -n2 --dr 'sort -m <({=uq=}) | mbuffer -m 30M;'
else
parallel -Xj1 -n2 --dr 'sort -m <({=uq=}) | mbuffer -m 30M;' |
merge $(( $1/2 ));
fi
}
# Generate commands that will read blocks of bigfile and sort those
# This only builds the command - it does not run anything
$ parallel --pipepart -a bigfile --block -1 --dr -vv sort |
# Merge these commands 2 by 2 until only one is left
# This only builds the command - it does not run anything
merge $(parallel --number-of-threads) |
# Execute the command
# This runs the command built in the previous step
bash > bigfile.sort
real 0m30.906s
user 0m21.963s
sys 0m28.870s
它将文件动态切割成 48 个块(每个核心一个块),并对这些块进行并行排序。然后我们对其中的一对进行合并排序。然后我们对其中的一对进行合并排序。然后我们对其中的一对进行合并排序。然后我们对其中的一对进行合并排序。然后我们对其中的一对进行合并排序。依此类推,直到我们只有一个输入。如果可能的话,所有这些都是并行完成的。
对于具有 4 G 行的 100 GB 文件,时间为:
$ LC_ALL=C time sort --parallel=48 -S 80% --compress-program pzstd bigfile >/dev/null
real 77m22.255s
$ LC_ALL=C time parsort bigfile >/dev/null
649.49user 727.04system 18:10.37elapsed 126%CPU (0avgtext+0avgdata 32896maxresident)k
因此,使用并行化可以将速度提高大约 4 倍。
为了使其更易于使用,我将其制作成一个小工具:parsort
它现在是 GNU Parallel 的一部分。
它也支持sort
选项和从标准输入读取(parsort -k2rn < bigfile
)。