Wie kann ich „parallel“ verwenden, um das „Sortieren“ großer Dateien zu beschleunigen, die in den RAM passen?

Wie kann ich „parallel“ verwenden, um das „Sortieren“ großer Dateien zu beschleunigen, die in den RAM passen?

Ich habe eine 100-MB-Zeilendatei, die in den RAM eines GNU/Linux-Systems passt.

Das ist ziemlich langsam:

sort bigfile > bigfile.sorted

und nutzt nicht alle 48 Kerne auf meinem Rechner.

Wie sortiere ich diese Datei schnell?

Antwort1

Nehmen wir an, Sie haben 48 Kerne, 500 GB freien RAM und die Datei umfasst 100 Millionen Zeilen und passt in den Speicher.

Wenn Sie die normale Sortierung verwenden, ist es ziemlich langsam:

$ time sort bigfile > bigfile.sort
real    4m48.664s
user    21m15.259s
sys     0m42.184s

Sie können es etwas schneller machen, indem Sie Ihr Gebietsschema ignorieren:

$ export LC_ALL=C
$ time sort bigfile > bigfile.sort
real    1m51.957s
user    6m2.053s
sys     0m42.524s

Sie können es beschleunigen, indem Sie sort anweisen, mehr Kerne zu verwenden:

$ export LC_ALL=C
$ time sort --parallel=48 bigfile > bigfile.sort
real    1m39.977s
user    15m32.202s
sys     1m1.336s

Sie können auch versuchen, sort mehr Arbeitsspeicher zuzuweisen (dies hilft nicht, wenn sort bereits über genügend Speicher verfügt):

$ export LC_ALL=C
$ time sort --buffer-size=80% --parallel=48 bigfile > bigfile.sort
real    1m39.779s
user    14m31.033s
sys     1m0.304s

Aber es scheint, dass sort wirklich viel Single-Threading mag. Sie können es zwingen, mehr zu parallelisieren mit:

$ 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

Es zerlegt die Datei im laufenden Betrieb in 48 Blöcke (ein Block pro Kern) und sortiert diese Blöcke parallel. Dann führen wir eine Art Zusammenführung von einem Paar davon durch. Dann führen wir eine Art Zusammenführung von einem Paar davon durch. Dann führen wir eine Art Zusammenführung von einem Paar davon durch. Dann führen wir eine Art Zusammenführung von einem Paar davon durch. Und so weiter, bis wir nur noch einen einzigen Input haben. All dies wird, wenn möglich, parallel durchgeführt.

Für eine 100 GB große Datei mit 4 G-Leitungen betragen die Zeitangaben:

$ 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

Die Parallelisierung beschleunigt also um etwa den Faktor 4.

Um die Verwendung zu vereinfachen, habe ich daraus ein kleines Tool gemacht, parsortdas jetzt Teil von GNU Parallel ist.

Es unterstützt sortauch Optionen und das Lesen von stdin ( parsort -k2rn < bigfile).

verwandte Informationen