¿Cómo utilizar "paralelo" para acelerar la "ordenación" de archivos grandes que caben en la RAM?

¿Cómo utilizar "paralelo" para acelerar la "ordenación" de archivos grandes que caben en la RAM?

Tengo un archivo de 100 M de líneas que cabe en la RAM en un sistema GNU/Linux.

Esto es bastante lento:

sort bigfile > bigfile.sorted

y no utiliza los 48 núcleos de mi máquina.

¿Cómo clasifico ese archivo rápidamente?

Respuesta1

Supongamos que tiene 48 núcleos, 500 GB de RAM libre y el archivo tiene 100 M de líneas y cabe en la memoria.

Si usa la clasificación normal, es bastante lento:

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

Puedes hacerlo un poco más rápido ignorando tu ubicación:

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

Puedes hacerlo más rápido diciéndole a sort que use más núcleos:

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

También puedes intentar darle más memoria de trabajo a la clasificación (esto no ayuda si la clasificación ya tiene suficiente memoria):

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

Pero parece que a este tipo realmente le gusta hacer muchos subprocesos únicos. Puedes forzarlo a que se paralelice más con:

$ 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

Corta el archivo en 48 bloques sobre la marcha (un bloque por núcleo) y los clasifica en paralelo. Luego hacemos una especie de fusión de un par de esos. Luego hacemos una especie de fusión de un par de esos. Luego hacemos una especie de fusión de un par de esos. Luego hacemos una especie de fusión de un par de esos. Luego hacemos una especie de fusión de un par de esos. Y así sucesivamente, hasta que solo tengamos una entrada. Todo esto se hace en paralelo cuando sea posible.

Para un archivo de 100 GB con líneas 4 G, los tiempos son:

$ 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

Entonces, usar la paralelización acelera alrededor de un factor de 4.

Para hacerlo más fácil de usar, lo he convertido en una pequeña herramienta: parsortque ahora forma parte de GNU Parallel.

También admite sortopciones y lectura desde stdin ( parsort -k2rn < bigfile).

información relacionada