La sort
commande UNIX peut trier un très gros fichier comme ceci:
sort large_file
Comment l'algorithme de tri est-il implémenté?
Comment se fait-il que cela ne cause pas une consommation excessive de mémoire?
La sort
commande UNIX peut trier un très gros fichier comme ceci:
sort large_file
Comment l'algorithme de tri est-il implémenté?
Comment se fait-il que cela ne cause pas une consommation excessive de mémoire?
Réponses:
Les détails algorithmiques de la commande UNIX Sort indiquent qu'Unix Sort utilise un algorithme de tri de fusion externe R-Way. Le lien entre dans plus de détails, mais en substance, il divise l'entrée en parties plus petites (qui s'insèrent dans la mémoire), puis fusionne chaque partie à la fin.
La sort
commande stocke les données de travail dans des fichiers de disque temporaires (généralement dans /tmp
).
-T
pour spécifier le
AVERTISSEMENT: ce script démarre un shell par bloc, pour les fichiers vraiment volumineux, cela peut être des centaines.
Voici un script que j'ai écrit à cet effet. Sur une machine à 4 processeurs, il a amélioré les performances de tri de 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
Voir aussi: " Tri plus rapide des fichiers volumineux avec un script shell "
Je ne suis pas familier avec le programme, mais je suppose que cela se fait au moyen d'un tri externe (la plupart du problème est conservé dans des fichiers temporaires tandis qu'une partie relativement petite du problème est conservée en mémoire à la fois). Voir The Art of Computer Programming de Donald Knuth , Vol. 3 Tri et recherche, section 5.4 pour une discussion très approfondie du sujet.
#!/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
Examinez attentivement les options de tri pour accélérer les performances et comprendre son impact sur votre machine et votre problème. Les paramètres clés sur Ubuntu sont
Le questionneur demande "Pourquoi pas d'utilisation élevée de la mémoire?" La réponse à cela vient de l'histoire, les anciennes machines Unix étaient petites et la taille de la mémoire par défaut est réduite. Ajustez cela aussi grand que possible pour votre charge de travail afin d'améliorer considérablement les performances de tri. Définissez le répertoire de travail sur un emplacement de votre appareil le plus rapide disposant de suffisamment d'espace pour contenir au moins 1,25 * la taille du fichier en cours de tri.
La mémoire ne devrait pas être un problème - le tri s'en charge déjà. Si vous voulez utiliser de manière optimale votre processeur multicœur, je l'ai implémenté dans un petit script (similaire à certains que vous pourriez trouver sur le net, mais plus simple / plus propre que la plupart de ceux-ci;)).
#!/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*