W jaki sposób polecenie sortowania systemu UNIX może posortować bardzo duży plik?


104

Polecenie UNIX sortmoże sortować bardzo duży plik w następujący sposób:

sort large_file

Jak jest zaimplementowany algorytm sortowania?

Dlaczego nie powoduje nadmiernego zużycia pamięci?


To jest interesujące. Naprawdę nie wiem, jak to działa, ale zgaduję. Prawdopodobnie umieszcza pierwszy znak każdego klucza w drzewie binarnym, a gdy występuje kolizja, używa również kolejnego znaku klucza, więc nie zapisuje więcej klucza niż to konieczne. Może następnie zapisać przesunięcie do pliku z każdym kluczem, aby móc wyszukiwać i drukować każdą linię w kolejności.
Zifre

Właściwie @ayaz jest bardziej interesujące, jeśli nie sortujesz pliku na dysku, ale raczej w potoku, ponieważ sprawia, że ​​oczywiste jest, że nie możesz po prostu wykonać wielu przejść przez dane wejściowe.
tvanfosson

3
Dlaczego wszyscy w SO czują się zmuszeni do zgadywania przez cały czas?

Możesz wykonać wiele przebiegów na wejściu - wystarczy przeczytać wszystkie dane wejściowe, zapisać je na dysku, a następnie posortować plik na dysku.

2
@Neil - z kontekstu wydawało się oczywiste, że próbował sortować zawartość pliku, a nie jego nazwę (co dla jednej nazwy jest bez znaczenia). Chciałem tylko poprawić pytanie, nie zmieniając zbytnio kontekstu, aby otrzymywało odpowiedzi zamiast głosów przeciwnych z powodu prostego błędu.
tvanfosson

Odpowiedzi:


111

Te dane algorytmiczne sort polecenia UNIX mówi Unix Sortuj wykorzystuje algorytm scalania sortowania zewnętrzny R-Way. Łącze zawiera więcej szczegółów, ale zasadniczo dzieli dane wejściowe na mniejsze części (które mieszczą się w pamięci), a następnie łączy każdą część razem na końcu.


42

W sortsklepach polecenie dane tymczasowe pliki dysków roboczych (zazwyczaj /tmp).


20
użyj, -Taby określić
katalog

12

OSTRZEŻENIE: Ten skrypt uruchamia jedną powłokę na porcję, w przypadku naprawdę dużych plików może to być setki.


Oto skrypt, który napisałem w tym celu. Na komputerze z 4 procesorami poprawiło to wydajność sortowania o 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

Zobacz też: „ Szybsze sortowanie dużych plików za pomocą skryptu powłoki


35
Możesz po prostu użyć sort --parallel N od wersji GNU sort 8.11
jhclark

5
Właściwie to GNU coreutils 8.6
bdeonovic

1
Ten załatwił mi sprawę. Mam wersję 8.4. Używanie sortowania bezpośrednio w pliku (190 milionów wierszy) nie miało sensu. Ten program zrobił to w niecałe 4 minuty
Sunil B

znowu ta odpowiedź nie ma nic wspólnego z pytaniem
WattsInABox

2
Ten skrypt jest niebezpieczny. Mój komputer z Linuksem stracił odpowiedź po uruchomieniu setek procesów sortowania…
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

To jest wspaniałe. Nie wiedziałem, że istnieje pakiet równoległy! Czas sortowania poprawił się o ponad 50% po zastosowaniu powyższego. Dzięki.
xbsd

Próbowałem użyć comm do diff na plikach wygenerowanych przez to i to daje mi ostrzeżenie, że pliki nie są posortowane.
ashishb

7

Przyjrzyj się uważnie opcjom sortowania, aby przyspieszyć działanie i zrozum, jak wpływa to na Twój komputer i problem. Kluczowe parametry w systemie Ubuntu to

  • Lokalizacja plików tymczasowych -T nazwa_katalogu
  • Ilość pamięci do wykorzystania -SN% (N% całej pamięci do wykorzystania, im więcej, tym lepiej, ale unikaj subskrypcji powodującej zamianę na dysk. Możesz użyć tego jak „-S 80%”, aby użyć 80% dostępnej pamięci RAM, lub „-S 2G” dla 2 GB pamięci RAM).

Pytający pyta „Dlaczego nie ma dużego użycia pamięci?” Odpowiedź na to pochodzi z historii, starsze komputery z systemem UNIX były małe, a domyślny rozmiar pamięci jest ustawiony na mały. Dostosuj to tak duże, jak to możliwe, aby znacznie poprawić wydajność sortowania. Ustaw katalog roboczy na miejsce na najszybszym urządzeniu, w którym jest wystarczająco dużo miejsca, aby pomieścić co najmniej 1,25 * rozmiaru sortowanego pliku.


wypróbowanie tego na pliku o pojemności 2,5 GB, na pudełku z 64 GB pamięci RAM z -S 80%, faktycznie wykorzystuje ten pełny procent, mimo że cały plik jest mniejszy. dlaczego? nawet jeśli nie używa sortowania na miejscu, które wydaje się nieuzasadnione
Joseph Garvin

Prawdopodobnie sort -S wstępnie alokuje pamięć dla procesu sortowania jeszcze przed odczytaniem zawartości pliku.
Fred Gannett

-3

Pamięć nie powinna być problemem - sort już się tym zajmuje. Jeśli chcesz optymalnie wykorzystać swój wielordzeniowy procesor, zaimplementowałem to w małym skrypcie (podobnym do niektórych, które możesz znaleźć w sieci, ale prostszym / czystszym niż większość z nich;)).

#!/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
Ciekawy scenariusz, ale nic nie odpowiada na to pytanie.
Joachim Sauer

5
split -b zostanie podzielone na bajty, a tym samym
obcięte
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.