¿Cómo podría el comando sort de UNIX ordenar un archivo muy grande?

104

El sortcomando UNIX puede ordenar un archivo muy grande como este:

sort large_file

¿Cómo se implementa el algoritmo de clasificación?

¿Por qué no provoca un consumo excesivo de memoria?

yjfuk
fuente
Esto es interesante. Realmente no sé cómo funciona, pero supongo. Probablemente coloca el primer carácter de cada clave en un árbol binario, y cuando hay una colisión, también usa el siguiente carácter de la clave, por lo que no guarda más de la clave de la necesaria. Luego, puede guardar un desplazamiento en el archivo con cada tecla para poder buscar e imprimir cada línea en orden.
Zifre
En realidad, @ayaz es más interesante si no está ordenando un archivo en el disco sino en una tubería, ya que hace obvio que no puede simplemente hacer varias pasadas sobre los datos de entrada.
tvanfosson
3
¿Por qué todos en SO se sienten tan impulsados ​​a adivinar todo el tiempo?
Puede hacer varias pasadas en la entrada; solo necesita leer toda la entrada, escribirla en el disco y luego ordenar el archivo de disco.
2
@Neil: por el contexto, parecía obvio que estaba tratando de ordenar el contenido del archivo, no el nombre del archivo (que para un nombre no tiene sentido). Solo quería mejorar la pregunta sin cambiar demasiado el contexto para que obtuviera respuestas en lugar de votos negativos debido a un simple error.
tvanfosson

Respuestas:

111

Los detalles algorítmicos del comando UNIX Sort indican que Unix Sort usa un algoritmo de clasificación de fusión R-Way externo. El enlace entra en más detalles, pero en esencia divide la entrada en porciones más pequeñas (que encajan en la memoria) y luego fusiona cada porción al final.

Mateo
fuente
42

El sortcomando almacena datos de trabajo en archivos de disco temporales (generalmente en /tmp).

user1686
fuente
20
utilizar -Tpara especificar el directorio temporal
glenn jackman
12

ADVERTENCIA: Este script inicia un shell por fragmento, para archivos realmente grandes, esto podría ser cientos.


Aquí hay un guión que escribí para este propósito. En una máquina de 4 procesadores, mejoró el rendimiento de clasificación en un 100%.

#! /bin/ksh

MAX_LINES_PER_CHUNK=1000000
ORIGINAL_FILE=$1
SORTED_FILE=$2
CHUNK_FILE_PREFIX=$ORIGINAL_FILE.split.
SORTED_CHUNK_FILES=$CHUNK_FILE_PREFIX*.sorted

usage ()
{
     echo Parallel sort
     echo usage: psort file1 file2
     echo Sorts text file file1 and stores the output in file2
     echo Note: file1 will be split in chunks up to $MAX_LINES_PER_CHUNK lines
     echo  and each chunk will be sorted in parallel
}

# test if we have two arguments on the command line
if [ $# != 2 ]
then
    usage
    exit
fi

#Cleanup any lefover files
rm -f $SORTED_CHUNK_FILES > /dev/null
rm -f $CHUNK_FILE_PREFIX* > /dev/null
rm -f $SORTED_FILE

#Splitting $ORIGINAL_FILE into chunks ...
split -l $MAX_LINES_PER_CHUNK $ORIGINAL_FILE $CHUNK_FILE_PREFIX

for file in $CHUNK_FILE_PREFIX*
do
    sort $file > $file.sorted &
done
wait

#Merging chunks to $SORTED_FILE ...
sort -m $SORTED_CHUNK_FILES > $SORTED_FILE

#Cleanup any lefover files
rm -f $SORTED_CHUNK_FILES > /dev/null
rm -f $CHUNK_FILE_PREFIX* > /dev/null

Consulte también: " Ordenar archivos grandes más rápido con un script de shell "

Adrian
fuente
35
Puede usar sort --parallel N a partir de GNU sort versión 8.11
jhclark
5
GNU coreutils 8.6 en realidad
bdeonovic
1
Este me sirvió. Tengo una versión tipo 8.4. Usar ordenar directamente en el archivo (190 millones de líneas) no iba a ninguna parte. Este programa lo hizo en poco menos de 4 minutos
Sunil B
nuevamente, esta respuesta no tiene nada que ver con la pregunta
WattsInABox
2
Este guión es peligroso. Mi máquina Linux perdió la respuesta después de iniciar cientos de procesos de ordenación…
Yongwei Wu
11
#!/bin/bash

usage ()
{
    echo Parallel sort
    echo usage: psort file1 file2
    echo Sorts text file file1 and stores the output in file2
}

# test if we have two arguments on the command line
if [ $# != 2 ]
then
    usage
    exit
fi

pv $1 | parallel --pipe --files sort -S512M | parallel -Xj1 sort -S1024M -m {} ';' rm {} > $2
Sergio
fuente
Esto es excelente. ¡No sabía que había un paquete paralelo! El tiempo de clasificación mejoró en más del 50% después de usar lo anterior. Gracias.
xbsd
Intenté usar comm para diff en los archivos generados por esto y me advierte que los archivos no están ordenados.
ashishb
7

Observe detenidamente las opciones de clasificación para acelerar el rendimiento y comprenda su impacto en su máquina y su problema. Los parámetros clave en Ubuntu son

  • Ubicación de los archivos temporales -T nombre_directorio
  • Cantidad de memoria para usar -SN% (N% de toda la memoria para usar, cuanto más, mejor, pero evite la suscripción excesiva que causa el intercambio al disco. Puede usarlo como "-S 80%" para usar el 80% de la RAM disponible, o "-S 2G" para 2 GB de RAM).

El interrogador pregunta "¿Por qué no hay un uso elevado de memoria?" La respuesta a eso viene de la historia, las máquinas Unix más antiguas eran pequeñas y el tamaño de memoria predeterminado es pequeño. Ajústelo lo más grande posible para su carga de trabajo para mejorar enormemente el rendimiento de clasificación. Configure el directorio de trabajo en un lugar de su dispositivo más rápido que tenga suficiente espacio para contener al menos 1,25 * el tamaño del archivo que se está ordenando.

Fred Gannett
fuente
probando esto en un archivo de 2.5GB, en una caja con 64GB de RAM con -S 80%, en realidad está usando ese porcentaje completo, aunque el archivo completo es más pequeño que eso. ¿porqué es eso? incluso si no usa un tipo en el lugar que parece gratuito
Joseph Garvin
Probablemente sort -S preasigna la memoria para el proceso de ordenación incluso antes de leer el contenido del archivo.
Fred Gannett
-3

La memoria no debería ser un problema, sort ya se encarga de eso. Si desea hacer un uso óptimo de su CPU de múltiples núcleos, he implementado esto en un pequeño script (similar a algunos que puede encontrar en la red, pero más simple / más limpio que la mayoría de esos;)).

#!/bin/bash
# Usage: psort filename <chunksize> <threads>
# In this example a the file largefile is split into chunks of 20 MB.
# The part are sorted in 4 simultaneous threads before getting merged.
# 
# psort largefile.txt 20m 4    
#
# by h.p.
split -b $2 $1 $1.part
suffix=sorttemp.`date +%s`
nthreads=$3
i=0
for fname in `ls *$1.part*`
do
    let i++
    sort $fname > $fname.$suffix &
    mres=$(($i % $nthreads))
    test "$mres" -eq 0 && wait
done
wait
sort -m *.$suffix 
rm $1.part*
hannes.p.
fuente
4
Interesante guión, pero no responde a esta pregunta.
Joachim Sauer
5
split -b se dividirá en bytes, truncando así las líneas en una posición arbitraria
ithkuil