web-dev-qa-db-ja.com

「並列」を使用して、RAMに収まる大きなファイルの「ソート」を高速化する方法

GNU/LinuxシステムでRAM=に適合する100 Mの行ファイルがあります。

これはかなり遅いです:

sort bigfile > bigfile.sorted

私のマシンでは48コアすべてを使用していません。

どうすればそのファイルをすばやくソートできますか?

21
Ole Tange

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

より多くのコアを使用するようにsortに指示することで、より速くすることができます。

$ 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ブロック(コアごとに1ブロック)にチョップし、それらのブロックを並列に並べ替えます。次に、それらのペアのマージソートを実行します。次に、それらのペアのマージソートを実行します。次に、それらのペアのマージソートを実行します。次に、それらのペアのマージソートを実行します。次に、それらのペアのマージソートを実行します。以下同様に、入力が1つになるまで続けます。これはすべて、可能な場合は並行して行われます。

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倍の速度になります。

使いやすくするために、小さなツールにしています https://gitlab.com/ole.tange/tangetools/-/tree/master/parsort

sortオプションとstdinからの読み取りもサポートしています(parsort -k2rn < bigfile)。

37
Ole Tange