Como o comando sort do UNIX pode classificar um arquivo muito grande?


104

O sortcomando UNIX pode classificar um arquivo muito grande como este:

sort large_file

Como o algoritmo de classificação é implementado?

Como é que não causa consumo excessivo de memória?


Isto é interessante. Não sei realmente como funciona, mas tenho um palpite. Provavelmente, ele coloca o primeiro caractere de cada chave em uma árvore binária e, quando há uma colisão, ele usa o próximo caractere da chave também, de modo que não salva mais da chave do que o necessário. Em seguida, ele pode salvar um deslocamento no arquivo com cada tecla para que possa retornar e imprimir cada linha em ordem.
Zifre

Na verdade, @ayaz é mais interessante se você não estiver classificando um arquivo no disco, mas sim em um pipe, pois torna óbvio que você não pode simplesmente fazer várias passagens sobre os dados de entrada.
tvanfosson

3
Por que todos no SO se sentem tão impelidos a adivinhar o tempo todo?

Você pode fazer várias passagens na entrada - você só precisa ler todas as entradas, gravá-las no disco e, em seguida, classificar o arquivo do disco.

2
@Neil - a partir do contexto, parecia óbvio que ele estava tentando classificar o conteúdo do arquivo, não o nome do arquivo (que para um nome não tem sentido). Eu só queria melhorar a pergunta sem mudar muito o contexto para que ela pudesse obter respostas em vez de votos negativos por causa de um simples erro.
tvanfosson

Respostas:


111

Os detalhes do algoritmo do comando UNIX Sort diz que o Unix Sort usa um algoritmo de classificação de mesclagem R-Way externo. O link fornece mais detalhes, mas basicamente divide a entrada em porções menores (que cabem na memória) e, em seguida, mescla cada porção no final.


42

O sortcomando armazena dados de trabalho em arquivos de disco temporários (geralmente em /tmp).


20
use -Tpara especificar a temperatura dir
glenn jackman

12

AVISO: este script inicia um shell por bloco, para arquivos realmente grandes, isso pode ser centenas.


Aqui está um roteiro que escrevi para esse propósito. Em uma máquina de 4 processadores, ele melhorou o desempenho de classificação em 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 também: " Classificando arquivos grandes mais rapidamente com um script de shell "


35
Você pode apenas usar sort --parallel N a partir do GNU sort versão 8.11
jhclark

5
GNU coreutils 8.6 na verdade
bdeonovic

1
Este fez o truque para mim. Eu tenho a versão 8.4 do tipo. Usar a classificação diretamente no arquivo (190 milhões de linhas) não levaria a lugar nenhum. Este programa fez isso com pouco menos de 4 minutos
Sunil B

novamente, essa resposta não tem nada a ver com a pergunta
WattsInABox

2
Este script é perigoso. Minha máquina Linux perdeu resposta depois de lançar centenas de processos de classificação ...
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

Isto e excelente. Não sabia que havia um pacote paralelo! O tempo de classificação melhorou em mais de 50% após o uso do acima. Obrigado.
xbsd

Eu tentei usar comm para diff nos arquivos gerados por isso e está me avisando que os arquivos não estão classificados.
ashishb

7

Observe cuidadosamente as opções de classificação para acelerar o desempenho e entenda seu impacto em sua máquina e no problema. Os principais parâmetros do Ubuntu são

  • Localização dos arquivos temporários -T directory_name
  • Quantidade de memória a ser usada -SN% (N% de toda a memória a ser usada, quanto mais, melhor, mas evite o excesso de assinatura que causa a troca para o disco. Você pode usar como "-S 80%" para usar 80% da RAM disponível, ou "-S 2G" para 2 GB de RAM.)

O questionador pergunta "Por que não há alto uso de memória?" A resposta para isso vem da história, as máquinas Unix mais antigas eram pequenas e o tamanho da memória padrão é pequeno. Ajuste isso o maior possível para sua carga de trabalho para melhorar muito o desempenho de classificação. Defina o diretório de trabalho para um local em seu dispositivo mais rápido que tenha espaço suficiente para armazenar pelo menos 1,25 * o tamanho do arquivo que está sendo classificado.


tentando fazer isso em um arquivo de 2,5 GB, em uma caixa com 64 GB de RAM com -S 80%, na verdade está usando essa porcentagem total, embora o arquivo inteiro seja menor do que isso. por que é que? mesmo que não use uma classificação local que pareça gratuita
Joseph Garvin

Provavelmente sort -S pré-aloca a memória para o processo de ordenação antes mesmo de ler o conteúdo do arquivo.
Fred Gannett

-3

A memória não deve ser um problema - o sort já cuida disso. Se você quiser fazer o uso ideal de sua CPU multi-core, eu implementei isso em um pequeno script (semelhante a alguns que você pode encontrar na rede, mas mais simples / mais limpo do que a maioria deles;)).

#!/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*

4
Roteiro interessante, mas não faz nada para responder a essa pergunta.
Joachim Sauer

5
split -b será dividido por bytes, truncando assim as linhas em uma posição arbitrária
ithkuil
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.