J'ai un fichier de ligne de 100 m qui correspond à RAM sur un système GNU/Linux.
Ceci est plutôt lent:
sort bigfile > bigfile.sorted
et n'utilise pas tous les 48 cœurs sur ma machine.
Comment puis-je trier ce fichier rapidement?
Supposons que vous ayez 48 cœurs, 500 gb free RAM et le fichier est de 100 m lignes et convient à la mémoire.
Si vous utilisez un tri normal, il est plutôt lent:
$ time sort bigfile > bigfile.sort
real 4m48.664s
user 21m15.259s
sys 0m42.184s
Vous pouvez en faire un peu plus vite en ignorant votre local:
$ export LC_ALL=C
$ time sort bigfile > bigfile.sort
real 1m51.957s
user 6m2.053s
sys 0m42.524s
Vous pouvez le rendre plus rapide en disant qu'il utilise plus de noyaux:
$ export LC_ALL=C
$ time sort --parallel=48 bigfile > bigfile.sort
real 1m39.977s
user 15m32.202s
sys 1m1.336s
Vous pouvez également essayer de donner de la plupart de la mémoire de travail (cela n'aider pas si le tri a déjà assez de mémoire):
$ export LC_ALL=C
$ time sort --buffer-size=80% --parallel=48 bigfile > bigfile.sort
real 1m39.779s
user 14m31.033s
sys 1m0.304s
Mais il semble que ce genre aime vraiment faire beaucoup de filetage unique. Vous pouvez le forcer à parallementer plus avec:
$ 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
Il côtelettes le fichier en 48 blocs à la volée (un bloc par noyau), trie ces blocs en parallèle. Ensuite, nous faisons une sorte de fusion d'une paire de celles-ci. Ensuite, nous faisons une sorte de fusion d'une paire de celles-ci. Ensuite, nous faisons une sorte de fusion d'une paire de celles-ci. Ensuite, nous faisons une sorte de fusion d'une paire de celles-ci. Ensuite, nous faisons une sorte de fusion d'une paire de celles-ci. Et ainsi de suite, jusqu'à ce que nous n'avions qu'une seule entrée. Tout cela est fait en parallèle lorsque cela est possible.
Pour un fichier de 100 Go avec 4 g de lignes, les horaires sont les suivants:
$ 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
Ainsi, l'utilisation de la parallélisation accélère autour d'un facteur de 4.
Pour faciliter l'utilisation, je l'ai fait dans un petit outil: https://gitlab.com/ole.Tange/TANGETOOLS/-/TREE/MASTER/PARSORT
Il prend en charge les options sort
et lire depuis STDIN (parsort -k2rn < bigfile
).