web-dev-qa-db-fra.com

Comment utiliser "Parallel" pour accélérer "Tri" pour les gros fichiers ajustés dans la RAM?

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?

23
Ole Tange

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).

37
Ole Tange