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, parsort
das jetzt Teil von GNU Parallel ist.
Es unterstützt sort
auch Optionen und das Lesen von stdin ( parsort -k2rn < bigfile
).