Bagaimana perintah sortir UNIX dapat mengurutkan file yang sangat besar?


104

sortPerintah UNIX dapat mengurutkan file yang sangat besar seperti ini:

sort large_file

Bagaimana algoritma sortir diimplementasikan?

Kenapa tidak menyebabkan konsumsi memory yang berlebihan?


Ini menarik. Saya tidak begitu tahu cara kerjanya, tapi saya punya tebakan. Ini mungkin menempatkan karakter pertama dari setiap kunci ke dalam pohon biner, dan ketika terjadi tabrakan, ia juga menggunakan karakter kunci berikutnya, sehingga tidak menyimpan lebih banyak kunci daripada yang diperlukan. Ini kemudian dapat menyimpan offset ke dalam file dengan setiap kunci sehingga dapat mencari kembali dan mencetak setiap baris secara berurutan.
Zifre

Sebenarnya, @ayaz lebih menarik jika Anda tidak menyortir file pada disk melainkan dalam pipa karena itu membuatnya jelas bahwa Anda tidak bisa begitu saja melakukan banyak lintasan pada data input.
tvanfosson

3
Mengapa semua orang di SO merasa terdorong untuk terus menebak?

Anda dapat melakukan beberapa langkah pada input - Anda hanya perlu membaca semua input, menulisnya ke disk, lalu mengurutkan file disk.

2
@Neil - dari konteksnya terlihat jelas bahwa dia mencoba mengurutkan isi file bukan nama file (yang untuk satu nama tidak ada artinya). Saya hanya ingin meningkatkan pertanyaan tanpa mengubah konteks terlalu banyak sehingga akan mendapatkan jawaban, bukan suara negatif karena kesalahan sederhana.
tvanfosson

Jawaban:


111

The rincian algorithmic dari UNIX perintah Sortir mengatakan Unix Urutkan menggunakan eksternal R-Way merge algoritma sorting. Tautan menjelaskan lebih detail, tetapi pada dasarnya itu membagi input menjadi bagian-bagian yang lebih kecil (yang sesuai dengan memori) dan kemudian menggabungkan setiap bagian bersama-sama di akhir.



12

PERINGATAN: Skrip ini memulai satu shell per bagian, untuk file yang sangat besar, ini bisa ratusan.


Berikut ini skrip yang saya tulis untuk tujuan ini. Pada mesin 4 prosesor, ini meningkatkan kinerja pengurutan sebesar 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

Lihat juga: " Mengurutkan file besar lebih cepat dengan skrip shell "


35
Anda cukup menggunakan sort --parallel N pada GNU sort versi 8.11
jhclark

5
GNU coreutils 8.6 sebenarnya
bdeonovic

1
Yang ini berhasil untuk saya. Saya memiliki semacam versi 8.4. Menggunakan sortir langsung pada file (190 juta baris) tidak akan berhasil. Program ini melakukannya hanya dalam waktu kurang dari 4 menit
Sunil B

sekali lagi, jawaban ini tidak ada hubungannya dengan pertanyaan
WattsInABox

2
Skrip ini berbahaya. Mesin Linux saya kehilangan respons setelah meluncurkan ratusan proses sortir…
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

Ini luar biasa. Tidak sadar bahwa ada paket paralel! Urutkan waktu ditingkatkan lebih dari 50% setelah menggunakan cara di atas. Terima kasih.
xbsd

Saya mencoba menggunakan comm untuk diff pada file yang dihasilkan oleh ini dan memberi saya peringatan bahwa file tidak diurutkan.
ashishb

7

Perhatikan baik-baik opsi semacam untuk mempercepat kinerja dan pahami pengaruhnya pada mesin dan masalah Anda. Parameter kunci di Ubuntu adalah

  • Lokasi file sementara -T nama_direktori
  • Jumlah memori yang akan digunakan -SN% (N% dari semua memori yang digunakan, lebih banyak lebih baik tetapi hindari berlangganan berlebihan yang menyebabkan pertukaran ke disk. Anda dapat menggunakannya seperti "-S 80%" untuk menggunakan 80% RAM yang tersedia, atau "-S 2G" untuk RAM 2 GB.)

Penanya bertanya "Mengapa tidak ada penggunaan memori yang tinggi?" Jawabannya berasal dari sejarah, mesin unix yang lebih lama berukuran kecil dan ukuran memori default disetel kecil. Sesuaikan ini sebesar mungkin untuk beban kerja Anda untuk sangat meningkatkan kinerja pengurutan. Setel direktori kerja ke tempat di perangkat tercepat Anda yang memiliki cukup ruang untuk menampung setidaknya 1,25 * ukuran file yang sedang diurutkan.


mencoba ini pada file 2.5GB, pada kotak dengan 64GB RAM dengan -S 80%, sebenarnya menggunakan persentase penuh itu, meskipun keseluruhan file lebih kecil dari itu. mengapa demikian? bahkan jika tidak menggunakan jenis di tempat yang tampaknya serampangan
Joseph Garvin

Mungkin sort -S pra-mengalokasikan memori untuk proses sortir bahkan sebelum membaca konten file.
Fred Gannett

-3

Memori seharusnya tidak menjadi masalah - semacam sudah mengurusnya. Jika Anda ingin memanfaatkan CPU multi-core secara optimal, saya telah mengimplementasikannya dalam skrip kecil (mirip dengan beberapa yang mungkin Anda temukan di internet, tetapi lebih sederhana / lebih bersih daripada kebanyakan;)).

#!/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
Skrip yang menarik, tetapi tidak melakukan apa pun untuk menjawab pertanyaan ini.
Joachim Sauer

5
split -b akan membagi byte, sehingga memotong garis pada posisi sewenang-wenang
ithkuil
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.