Skrip Bash; optimalisasi kecepatan pemrosesan

10

Saya bertanya-tanya apakah ada pedoman umum untuk mengoptimalkan skrip Bash.

  • Sebagai contoh, lebih mudah untuk menulis loop daripada baris perintah, tetapi apakah lebih cepat untuk memproses sistem? Contoh:

    for i in a b c; do echo $i; done
    
    echo a
    echo b
    echo c
  • Terkadang orang menghadirkan solusi berbeda untuk masalah yang sama. Sebagai contoh, sed, cut, awk, dan echosemua mampu melucuti digit dari string. Saya bertanya-tanya apakah Anda dapat mengatakan bahwa semakin sedikit kode digit, semakin cepat jika Anda menggunakan:

    1. perintah yang sama, mis

      STRING=abc.def
      echo ${STRING} | sed 's/.def//g'
      echo ${STRING} | sed '$s/....$//'
    2. perintah yang berbeda, mis

      STRING=abc.def
      echo ${STRING} | cut -d . -f 1
      echo ${STRING} | sed 's/.def//g'
Vincent
sumber
2
Dalam kebanyakan kasus saya lebih suka keterbacaan daripada kecepatan.
Bernhard
1
a) Ya ada overhead dalam menggunakan for for loop tetapi tidak masalah b) mencoba meminimalkan perintah eksternal dan hanya mengganti hal-hal dari dalam bash. Dan jika Anda harus mengoptimalkan skrip shell Anda, Anda melakukan sesuatu yang salah dan harus mempertimbangkan menggunakan bahasa dengan dukungan profiler yang lebih baik.
Ulrich Dangel
Ekspresi reguler umumnya kinerja yang kurang efisien daripada pilihan lain, jadi jika Anda melihat cara untuk melakukan sesuatu tanpa satu (misalnya, memotong), gunakan itu. Tentu saja, itu dapat memengaruhi keterbacaan jika kemampuan membaca Anda terbatas pada ekspresi reguler;)
goldilocks

Jawaban:

8

Shells tidak melakukan reorganisasi kode yang mereka dapatkan, itu hanya ditafsirkan satu demi satu baris (tidak ada yang lebih masuk akal dalam penerjemah perintah). Sebagian besar waktu yang dihabiskan oleh shell digunakan untuk analisis leksikal / parsing / meluncurkan program yang disebut.

Untuk operasi sederhana (seperti string munging dalam contoh di akhir pertanyaan), saya akan terkejut jika waktu memuat program tidak membanjiri perbedaan kecepatan yang sangat kecil.

Moral dari cerita ini adalah bahwa jika Anda benar-benar membutuhkan lebih banyak kecepatan, Anda lebih baik dengan bahasa yang dikompilasi (semi) seperti Perl atau Python, yang lebih cepat dijalankan untuk memulai, di mana Anda dapat menulis banyak operasi yang disebutkan secara langsung dan tidak perlu memanggil program eksternal, dan memiliki opsi untuk memanggil program eksternal atau memanggil modul C (atau apa pun) yang dioptimalkan untuk melakukan banyak pekerjaan. Itulah alasan mengapa di Fedora "gula administrasi sistem" (GUI, pada dasarnya) ditulis dengan Python: Dapat menambahkan GUI yang bagus dengan tidak terlalu banyak upaya, cukup cepat untuk aplikasi seperti itu, memiliki akses langsung ke panggilan sistem. Jika itu tidak cukup cepat, ambil C ++ atau C.

Tetapi jangan pergi ke sana, kecuali jika Anda dapat membuktikan bahwa perolehan kinerja sepadan dengan hilangnya fleksibilitas dan waktu pengembangan. Skrip shell tidak terlalu buruk untuk dibaca, tetapi saya bergidik ketika saya ingat beberapa skrip yang digunakan untuk menginstal Ultrix Saya pernah mencoba menguraikan. Saya menyerah, terlalu banyak "optimasi skrip shell" telah diterapkan.

vonbrand
sumber
1
+1 tetapi banyak orang akan berpendapat bahwa ada kemungkinan lebih besar untuk mendapatkan fleksibilitas dan waktu pengembangan menggunakan sesuatu seperti python atau perl vs shell, bukan kerugian. Saya akan mengatakan hanya menggunakan skrip shell jika itu diperlukan, atau apa yang Anda lakukan melibatkan sejumlah perintah khusus shell.
goldilocks
22

Aturan optimasi pertama adalah: jangan optimalkan . Tes dulu. Jika tes menunjukkan bahwa program Anda terlalu lambat, cari kemungkinan optimasi.

Satu-satunya cara untuk memastikan adalah dengan melakukan benchmark untuk use case Anda. Ada beberapa aturan umum, tetapi mereka hanya berlaku untuk volume data khas dalam aplikasi tipikal.

Beberapa aturan umum yang mungkin atau mungkin tidak benar dalam keadaan tertentu:

  • Untuk pemrosesan internal di shell, ATT ksh adalah yang tercepat. Jika Anda melakukan banyak manipulasi string, gunakan ATT ksh. Dash menempati urutan kedua; bash, pdksh dan zsh tertinggal.
  • Jika Anda perlu sering menggunakan shell untuk melakukan tugas yang sangat singkat setiap kali, dash menang karena waktu startup yang rendah.
  • Memulai proses eksternal membutuhkan waktu, jadi lebih cepat untuk memiliki satu pipa dengan potongan yang rumit daripada pipa dalam satu lingkaran.
  • echo $foolebih lambat daripada echo "$foo", karena tanpa tanda kutip ganda, itu terbagi $foomenjadi kata-kata dan menafsirkan setiap kata sebagai pola wildcard nama file. Lebih penting lagi, bahwa perilaku membelah dan menggumpal jarang diinginkan. Jadi ingatlah untuk selalu menempatkan tanda kutip ganda di sekitar substitusi variabel dan substitusi perintah: "$foo", "$(foo)".
  • Alat khusus cenderung menang atas alat tujuan umum. Misalnya, alat suka cutatau headbisa ditiru sed, tetapi sedakan lebih lambat dan awkbahkan akan lebih lambat. Pemrosesan string shell lambat, tetapi untuk string pendek sebagian besar mengalahkan memanggil program eksternal.
  • Bahasa yang lebih maju seperti Perl, Python, dan Ruby sering membiarkan Anda menulis algoritma lebih cepat, tetapi mereka memiliki waktu startup yang jauh lebih tinggi sehingga mereka hanya layak untuk kinerja untuk sejumlah besar data.
  • Setidaknya di Linux, pipa cenderung lebih cepat daripada file sementara.
  • Sebagian besar penggunaan skrip shell ada di sekitar proses I / O-terikat, jadi konsumsi CPU tidak masalah.

Jarang bahwa kinerja menjadi perhatian dalam skrip shell. Daftar di atas adalah murni indikatif; itu baik-baik saja untuk menggunakan metode "lambat" dalam banyak kasus karena perbedaannya seringkali sepersekian persen.

Biasanya inti dari skrip shell adalah menyelesaikan sesuatu dengan cepat. Anda harus mendapatkan banyak dari optimasi untuk membenarkan menghabiskan menit tambahan menulis skrip.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
2
Sementara pythondan rubypasti lebih lambat untuk memulai, setidaknya pada sistem saya, perlsama cepatnya dengan memulai bashatau ksh. GNU awk secara signifikan lebih lambat daripada GNU sed terutama di utf-8 locales, tapi itu tidak benar untuk semua awk dan semua sed. bash ksh93> dash> pdksh> zsh> tidak selalu sejelas itu. Beberapa cangkang lebih baik dalam beberapa hal daripada yang lain, dan pemenangnya tidak selalu sama.
Stéphane Chazelas
2
Re "Anda harus mendapatkan banyak dari ..." : jika "Anda" menyertakan basis pengguna, benar. Dengan skrip shell dalam paket-paket Linux yang populer, seringkali pengguna secara kolektif menghabiskan beberapa kali lipat lebih banyak waktu daripada menghemat waktu yang dilakukan oleh programmer.
agc
2

Kami akan memperluas di sini pada contoh globbing kami di atas untuk menggambarkan beberapa karakteristik kinerja penerjemah skrip shell. Membandingkan bashdan dashjuru bahasa untuk contoh ini di mana suatu proses muncul untuk masing-masing 30.000 file, menunjukkan bahwa tanda hubung dapat memotong wcproses hampir dua kali lebih cepat daribash

bash-4.2$ time dash -c 'for i in *; do wc -l "$i"; done>/dev/null'
real    0m1.238s
user    0m0.309s
sys     0m0.815s


bash-4.2$ time bash -c 'for i in *; do wc -l "$i"; done>/dev/null'
real    0m1.422s
user    0m0.349s
sys     0m0.940s

Membandingkan kecepatan looping dasar dengan tidak menjalankan wcproses, menunjukkan bahwa loop loop hampir 6 kali lebih cepat!

$ time bash -c 'for i in *; do echo "$i">/dev/null; done'
real    0m1.715s
user    0m1.459s
sys     0m0.252s



$ time dash -c 'for i in *; do echo "$i">/dev/null; done'
real    0m0.375s
user    0m0.169s
sys     0m0.203s

Perulangan masih relatif lambat di kedua shell seperti yang ditunjukkan sebelumnya, jadi untuk skalabilitas kita harus mencoba dan menggunakan teknik yang lebih fungsional sehingga iterasi dilakukan dalam proses yang dikompilasi.

$ time find -type f -print0 | wc -l --files0-from=- | tail -n1
    30000 total
real    0m0.299s
user    0m0.072s
sys     0m0.221s

Sejauh ini di atas adalah solusi yang paling efisien dan mengilustrasikan poin dengan baik bahwa seseorang harus melakukan sesedikit mungkin dalam skrip shell dan bertujuan hanya untuk menggunakannya untuk menghubungkan logika yang ada yang tersedia dalam serangkaian utilitas yang tersedia pada sistem UNIX.

Dicuri Dari Kesalahan skrip shell umum oleh Pádraig Brady.

Rahul Patil
sumber
1
Aturan umum: penanganan deskriptor file juga membutuhkan biaya, jadi kurangi jumlah mereka. Alih-alih for i in *; do wc -l "$i">/dev/null; donemelakukannya lebih baik for i in *; do wc -l "$i"; done>/dev/null.
manatwork
@manatwork itu juga akan null output timecmd
Rahul Patil
@manatwork Bagus ... sekarang Tolong juga beri saya output tanpa meminta wc -l, periksa saya telah memperbarui dalam posting output Anda
Rahul Patil
Nah, pengukuran sebelumnya dilakukan pada direktori yang lebih kecil. Sekarang saya membuat satu dengan 30000 file dan mengulangi tes: pastebin.com/pCV6QKp2
manatwork
Tolok ukur tersebut gagal untuk memungkinkan waktu mulai yang berbeda dari setiap shell. Tingkatan yang dicapai dari masing - masing shell akan lebih baik.
agc