Kinerja loop vs ekspansi

9

Perlu saran ahli tentang perbandingan di bawah ini:

Segmen kode menggunakan loop:

for file in `cat large_file_list`
do
    gzip -d $file
done

Segmen kode menggunakan ekspansi sederhana:

gzip -d `cat large_file_list`

Yang mana yang lebih cepat? Harus memanipulasi set data besar.

Leon
sumber
1
Jawaban yang benar akan tergantung pada berapa lama untuk memulai gzippada sistem Anda, jumlah file dalam daftar file dan ukuran file-file itu.
Kusalananda
Daftar file akan memiliki sekitar 1000 - 10.000 file. Ukuran bervariasi dari beberapa kilobyte hingga 500 MB. Saya tidak tahu berapa lama untuk memulai gzip di sistem saya. ada cara memeriksa?
Leon
1
Ok, maka itu mungkin juga tergantung pada panjang nama file . Jika nama file panjang, beberapa sistem mungkin menghasilkan kesalahan "daftar argumen terlalu panjang" jika Anda mencoba melakukannya tanpa loop karena substitusi perintah akan menghasilkan baris perintah terlalu lama untuk dieksekusi shell. Jika Anda tidak ingin bergantung pada jumlah file dalam daftar, cukup gunakan satu loop. Apakah Anda menghabiskan banyak waktu mendekompresi file-file ini dibandingkan dengan pemrosesan lain yang akan Anda lakukan pada mereka?
Kusalananda
Leon melihat hasil pengujian saya: "Argumen besar" 20x lebih cepat dari "loop" di pengaturan saya.
untuk media yang senang antara proses dimulai dan panjang baris perintah, gunakan sesuatu seperti xargs gzip -d < large_file_listtapi hati-hati dengan spasi dalam nama file, mungkin dengantr \\n \\0 large_file_list | xargs -0 gzip -d
w00t

Jawaban:

19

Komplikasi

Yang berikut ini hanya akan berfungsi kadang-kadang:

gzip -d `cat large_file_list`

Tiga masalah adalah (di bashdan sebagian besar kerang mirip Bourne):

  1. Ini akan gagal jika ada nama file yang memiliki tab spasi atau karakter baris baru di dalamnya (dengan asumsi $IFSbelum dimodifikasi). Ini karena pemisahan kata shell .

  2. Itu juga mungkin gagal jika ada nama file yang memiliki karakter glob-active di dalamnya. Ini karena shell akan menerapkan ekspansi pathname ke daftar file.

  3. Ini juga akan gagal jika nama file dimulai dengan -(jika POSIXLY_CORRECT=1itu hanya berlaku untuk file pertama) atau jika ada nama file -.

  4. Itu juga akan gagal jika ada terlalu banyak nama file di dalamnya untuk muat pada satu baris perintah.

Kode di bawah ini memiliki masalah yang sama dengan kode di atas (kecuali untuk yang keempat)

for file in `cat large_file_list`
do
    gzip -d $file
done

Solusi andal

Jika Anda large_file_listmemiliki tepat satu nama file per baris, dan file yang dipanggil -tidak ada di antara mereka, dan Anda menggunakan sistem GNU, maka gunakan:

xargs -rd'\n' gzip -d -- <large_file_list

-d'\n'memberitahu xargsuntuk memperlakukan setiap baris input sebagai nama file yang terpisah.

-rmemberitahu xargsuntuk tidak menjalankan perintah jika file input kosong.

--memberitahu gzipbahwa argumen berikut ini tidak diperlakukan sebagai opsi meskipun mereka mulai dengan -. -sendiri masih akan diperlakukan sebagai -pengganti file yang dipanggil -sekalipun.

xargsakan menempatkan banyak nama file pada setiap baris perintah tetapi tidak terlalu banyak sehingga melebihi batas baris perintah. Ini mengurangi berapa kali suatu gzipproses harus dimulai dan karenanya membuatnya cepat. Ini juga aman: nama file juga akan dilindungi dari pemisahan kata dan perluasan pathname .

John1024
sumber
Terima kasih atas balasan terinci. Saya mengerti 3 masalah yang Anda sebutkan. Nama file sederhana dan tidak akan menghadapi tantangan itu karena daftar akan menampung hingga 20.000. Dan pertanyaan saya pada dasarnya adalah kinerja kedua segmen tersebut. Terima kasih.
Leon
1
@Leon Loopnya forakan — paling jauh— paling lambat. Dua metode lainnya akan sangat dekat dalam kecepatan satu sama lain.
John1024
7
Selain itu, jangan abaikan potensi masalah: banyak pertanyaan di StackExchange di sini adalah karena pemisahan kata atau perluasan nama path terjadi pada orang yang tidak mengharapkannya.
John1024
5
Perhatikan juga bahwa ada variasi dalam membaca file dengan xargs: setidaknya versi GNU memiliki --arg-fileopsi (formulir pendek -a). Jadi orang bisa melakukannya xargs -a large_file_list -rd'\n' gzip -d . Secara efektif, tidak ada perbedaan, selain dari fakta bahwa <operator shell dan akan xargsmembaca dari stdin (yang shell "link" ke file), sementara -aakan membuat xargssecara eksplisit membuka file tersebut
Sergiy Kolodyazhnyy
2
terdon mencatat dalam komentar lain tentang menggunakan paralleluntuk menjalankan banyak salinan gzip, tetapi xargs(setidaknya satu GNU), memiliki -Psaklar untuk itu juga. Pada mesin multicore yang mungkin membuat perbedaan. Tapi itu juga mungkin bahwa dekompresi benar-benar terikat I / O.
ilkkachu
12

Saya ragu itu akan sangat berarti.

Saya akan menggunakan loop, hanya karena saya tidak tahu berapa banyak file yang terdaftar dalam file daftar, dan saya tidak (umumnya) tahu jika ada nama file yang memiliki spasi dalam namanya. Melakukan substitusi perintah yang akan menghasilkan daftar argumen yang sangat panjang dapat menghasilkan kesalahan "Daftar argumen terlalu panjang" ketika panjang daftar yang dihasilkan terlalu panjang.

Lingkaran saya akan terlihat seperti

while IFS= read -r name; do
    gunzip "$name"
done <file.list

Ini juga akan memungkinkan saya untuk memasukkan perintah untuk memproses data setelah gunzipperintah. Faktanya, tergantung pada apa sebenarnya data itu dan apa yang perlu dilakukan dengannya, bahkan mungkin untuk memprosesnya tanpa menyimpannya sama sekali:

while IFS= read -r name; do
    zcat "$name" | process_data
done <file.list

(di mana process_dataada beberapa pipa yang membaca data yang tidak terkompresi dari input standar)

Jika pemrosesan data membutuhkan waktu lebih lama daripada pengompresannya, pertanyaan apakah perulangan lebih efisien atau tidak menjadi tidak relevan.

Idealnya , saya lebih suka untuk tidak bekerja dari daftar nama file, dan alih-alih menggunakan pola globbing nama file, seperti pada

for name in ./*.gz; do
    # processing of "$name" here
done

di mana ./*.gzbeberapa pola yang cocok dengan file yang relevan. Dengan cara ini kita tidak tergantung pada jumlah file atau karakter yang digunakan dalam nama file (mereka mungkin berisi baris baru atau karakter spasi putih lainnya, atau mulai dengan tanda hubung, dll.)

Terkait:

Kusalananda
sumber
5

Dari keduanya, file dengan semua file yang diteruskan ke satu permintaan gzipkemungkinan akan lebih cepat, tepatnya karena Anda hanya perlu meluncurkan gzipsekali. (Yaitu, jika perintah bekerja sama sekali, lihat jawaban lain untuk peringatan.)

Tapi, saya ingin mengingatkan aturan emas optimasi : Jangan lakukan itu sebelum waktunya.

  1. Jangan mengoptimalkan hal semacam itu sebelum Anda tahu itu masalah.

    Apakah bagian dari program ini memakan waktu lama? Yah, dekompresi file besar mungkin, dan Anda harus tetap melakukannya, jadi itu mungkin tidak mudah untuk dijawab.

  2. Mengukur. Sungguh, ini cara terbaik untuk memastikan.

    Anda akan melihat hasilnya dengan mata kepala Anda sendiri (atau dengan stopwatch Anda sendiri), dan mereka akan berlaku untuk situasi Anda yang jawaban acaknya di Internet mungkin tidak. Masukkan kedua varian ke dalam skrip dan jalankan time script1.sh, dan time script2.sh. (Lakukan itu dengan daftar file terkompresi kosong untuk mengukur jumlah absolut overhead.)

ilkkachu
sumber
0

Seberapa cepat disk Anda?

Ini harus menggunakan semua CPU Anda:

parallel -X gzip -d :::: large_file_list

Jadi batas Anda kemungkinan akan menjadi kecepatan disk Anda.

Anda dapat mencoba menyesuaikan dengan -j:

parallel -j50% -X gzip -d :::: large_file_list

Ini akan menjalankan setengah dari pekerjaan secara paralel seperti perintah sebelumnya, dan akan membuat disk Anda lebih sedikit, jadi tergantung pada disk Anda, ini bisa lebih cepat.

Ole Tange
sumber