Apakah dash atau shell lain "lebih cepat" daripada bash?

57

Saya selalu berpikir bahwa satu-satunya manfaat menggunakan dasbor daripada bash adalah dasbor lebih kecil, dan karena itu banyak contoh dasbor akan mulai lebih cepat saat boot.

Tetapi saya telah melakukan beberapa penelitian, dan menemukan beberapa orang memigrasi semua skrip mereka ke dash dengan harapan mereka akan berjalan lebih cepat, dan saya juga menemukan ini dalam artikel DashAsBinSh di Wiki Ubuntu:

Alasan utama untuk mengganti shell default adalah efisiensi . bash adalah shell dengan fitur lengkap yang luar biasa yang cocok untuk penggunaan interaktif; memang, itu masih shell login default. Namun, ini agak besar dan lambat untuk memulai dan beroperasi dengan perbandingan dengan tanda hubung.

Saat ini saya telah menggunakan banyak skrip bash untuk banyak hal di sistem saya, dan masalah saya adalah saya memiliki skrip tertentu yang saya jalankan terus menerus 24/7, yang memunculkan sekitar 200 anak, yang bersama-sama memanaskan komputer saya 10 ° C lebih dari pada penggunaan normal.

Ini adalah skrip yang agak besar dengan banyak bashisme, jadi porting mereka ke POSIX atau shell lain akan sangat memakan waktu (dan POSIX tidak terlalu penting untuk penggunaan pribadi), tetapi akan sangat berharga jika saya dapat mengurangi sebagian dari ini Penggunaan CPU. Saya tahu ada juga hal-hal lain yang perlu dipertimbangkan, seperti memanggil binary eksternal seperti seduntuk bashism sederhana seperti ${foo/bar}, atau grepbukannya =~.

TL; DR benar-benar lebih lambat untuk memulai dan beroperasi dibandingkan dengan tanda hubung? Apakah ada cangkang Unix lain yang lebih efisien daripada bash?

Teresa e Junior
sumber
12
Jika Anda akan melakukan porting untuk kinerja, apakah Anda pikir itu akan lebih baik dilakukan dalam beberapa bahasa lain (perl, python, ruby) sepenuhnya? Mereka umumnya jauh lebih efisien, saya pikir, meskipun itu akan tergantung pada sifat tugas yang tepat.
goldilocks
Poin minor: [juga harus menjadi builtin.
Mikel
2
Ingatlah bahwa tidak seperti Anda khawatir tentang penggunaan memori, perbedaannya sebagian besar ditunjukkan jika Anda melakukan perhitungan di shell daripada di program eksternal (yaitu, Anda menggunakan shell dengan cara yang salah!); mis. di komputer saya, skrip yang menggunakan loop sementara untuk menghitung hingga satu juta (tidak melakukan apa-apa lagi) adalah ~ 2x lebih cepat di mksh / zsh dan> 2x lebih cepat di dasbor, tetapi dalam skrip nyata saya akan membongkar sebanyak mungkin ke yang lain program.
loreb
3
bashDulu sangat lambat. Ini membuat banyak kemajuan baru-baru ini, tetapi untuk sebagian besar hal, masih lebih lambat daripada kebanyakan shell lainnya.
Stéphane Chazelas
1
Jangan gunakan bashism yang sederhana . [ "$foo" != "${foo#*bar}" ]menangani hal grep Anda. Dan sedhal: while [ "$foo" != "${foo#*bar}" ]; do s=$s${foo%%bar*} foo=${foo#*bar} ; done ; foo=$s$foo. Anda dapat menempatkan salah satu dari fungsi tersebut.
mikeserv

Jawaban:

39

SHELL SEQ:

Mungkin cara yang berguna untuk menilai kinerja sebuah shell adalah dengan melakukan banyak evaluasi kecil yang sangat sederhana secara berulang-ulang. Penting, saya pikir, tidak hanya untuk mengulang, tetapi untuk mengulang masukan , karena shell perlu membaca <&0.

Saya pikir ini akan melengkapi tes @ cuonglm yang sudah diposting karena itu menunjukkan kinerja proses shell tunggal sekali dipanggil, sebagai lawannya yang menunjukkan seberapa cepat proses shell dimuat ketika dipanggil. Dengan cara ini, di antara kami, kami menutupi kedua sisi mata uang.

Inilah fungsi untuk memfasilitasi demo:

sh_bench() (                                               #dont copy+paste comments
    o=-c sh=$(command -v "$1") ; shift                     #get shell $PATH; toss $1
    [ -z "${sh##*busybox}" ] && o='ash -c'                 #cause its weird
    set -- "$sh" $o "'$(cat <&3)'" -- "$@"                 #$@ = invoke $shell
    time env - "$sh" $o "while echo; do echo; done|$*"     #time (env - sh|sh) AC/DC
) 3<<-\SCRIPT                                                                      
#Everything from here down is run by the different shells    
    i="${2:-1}" l="${1:-100}" d="${3:-                     
}"; set -- "\$((n=\$n\${n:++\$i}))\$d"                     #prep loop; prep eval
    set -- $1$1$1$1$1$1$1$1$1$1                            #yup
    while read m                                           #iterate on input
    do  [ $(($i*50+${n:=-$i})) -gt "$(($l-$i))" ] ||       #eval ok?
            eval echo -n \""$1$1$1$1$1"\"                  #yay!
        [ $((n=$i+$n)) -gt "$(($l-$i))" ] &&               #end game?
            echo "$n" && exit                              #and EXIT
        echo -n "$n$d"                                     #damn - maybe next time
    done                                                   #done 
#END
SCRIPT                                                     #end heredoc

Entah itu menambah variabel satu kali per baris membaca baru atau, sebagai sedikit optimasi, jika bisa, itu meningkat 50 kali per baris membaca baru. Setiap kali variabel bertambah itu dicetak ke stdout. Berperilaku seperti semacam seqsalib nl.

Dan hanya untuk membuatnya sangat jelas apa yang dilakukannya - inilah beberapa set -x;keluaran yang terpotong setelah memasukkannya tepat timepada fungsi di atas:

time env - /usr/bin/busybox ash -c '
     while echo; do echo; done |
     /usr/bin/busybox ash -c '"'$(
         cat <&3
     )'"' -- 20 5 busybox'

Jadi setiap shell pertama kali disebut seperti:

 env - $shell -c "while echo; do echo; done |..."

... untuk menghasilkan input yang diperlukan untuk mengulang ketika dibaca 3<<\SCRIPT- atau kapan cat, bagaimanapun. Dan di sisi lain |pipeitu menyebut dirinya lagi seperti:

"...| $shell -c '$(cat <<\SCRIPT)' -- $args"

Jadi selain dari panggilan awal ke env (karena catsebenarnya dipanggil di baris sebelumnya) ; tidak ada proses lain yang dipanggil sejak dipanggil sampai keluar. Setidaknya, saya harap itu benar.

Sebelum angka ...

Saya harus membuat beberapa catatan tentang portabilitas.

  • poshtidak suka $((n=n+1))dan bersikeras$((n=$n+1))

  • mkshtidak memiliki printfbuiltin dalam banyak kasus. Tes sebelumnya membuatnya tertinggal banyak - itu memohon /usr/bin/printfuntuk setiap lari. Karena itu di echo -natas.

  • mungkin lebih seperti yang saya ingat ...

Pokoknya, ke nomor:

for sh in dash busybox posh ksh mksh zsh bash
do  sh_bench $sh 20 5 $sh 2>/dev/null
    sh_bench $sh 500000 | wc -l
echo ; done

Itu akan membuat mereka semua sekaligus ...

0dash5dash10dash15dash20

real    0m0.909s
user    0m0.897s
sys     0m0.070s
500001

0busybox5busybox10busybox15busybox20

real    0m1.809s
user    0m1.787s
sys     0m0.107s
500001

0posh5posh10posh15posh20

real    0m2.010s
user    0m2.060s
sys     0m0.067s
500001

0ksh5ksh10ksh15ksh20

real    0m2.019s
user    0m1.970s
sys     0m0.047s
500001

0mksh5mksh10mksh15mksh20

real    0m2.287s
user    0m2.340s
sys     0m0.073s
500001

0zsh5zsh10zsh15zsh20

real    0m2.648s
user    0m2.223s
sys     0m0.423s
500001

0bash5bash10bash15bash20

real    0m3.966s
user    0m3.907s
sys     0m0.213s
500001

ARBITRARI = MUNGKIN OK?

Namun, ini adalah tes yang agak sewenang-wenang, tetapi tes input membaca, evaluasi aritmatika, dan ekspansi variabel. Mungkin tidak komprehensif, tetapi mungkin dekat dengan sana.

EDIT oleh Teresa e Junior : @mikeserv dan saya telah melakukan banyak tes lain (lihat detail obrolan kami ), dan kami menemukan hasilnya dapat diringkas seperti ini:

  • Jika Anda membutuhkan kecepatan, gunakan dash , pasti jauh lebih cepat daripada shell lain dan sekitar 4x lebih cepat dari bash .
  • Sementara busybox shell 's bisa jauh lebih lambat dari dasbor , di beberapa tes itu bisa lebih cepat, karena memiliki banyak utilitas sendiri userland, seperti grep, sed, sort, dll, yang tidak memiliki banyak fitur seperti GNU yang umum digunakan utilitas, tetapi bisa menyelesaikan pekerjaan sebanyak mungkin.
  • Jika kecepatan bukanlah segalanya yang Anda pedulikan, ksh (atau ksh93 ) dapat dianggap sebagai kompromi terbaik antara kecepatan dan fitur. Kecepatannya dibandingkan dengan mksh yang lebih kecil , yang jauh lebih cepat daripada bash , dan juga memiliki beberapa fitur unik, seperti aritmatika floating point .
  • Meskipun bash terkenal dengan kesederhanaan, stabilitas, dan fungsionalitasnya, bash adalah yang paling lambat dari semua cangkang di sebagian besar pengujian kami, dan dengan margin yang besar.
mikeserv
sumber
Saya tidak bisa mendapatkan kode ini bekerja di bash (dan juga ksh dan zsh), hanya di dash, mksh, dan pdksh. Bash Saya sudah mencoba 4.2.37(1)-releasedari Debian dan 4.2.45(2)-releasedari Porteus LiveCD (Slackware). Tanpa null=, alih-alih mengeluarkan angka, itu berfungsi seolah-olah saya menekan Return terus menerus, maka saya harus membunuh bash dengan SIGKILL .
Teresa e Junior
Dan saya juga telah mencoba bash --posix, tetapi tidak berhasil.
Teresa e Junior
@TeresaeJunior - itu mungkin mungkin - meskipun saya tidak percaya itu akan berhasil zsh. zshakan membajak ttydan baik, itu akan meluncurkan shell interaktif. Saya berharap bashakan melakukan hal yang sama - itulah sebabnya saya berhati-hati untuk hanya memanggil --posixtautannya. Saya bisa membuatnya seperti yang Anda harapkan untuk sebagian besar dari mereka, tetapi mungkin lebih banyak pekerjaan daripada nilainya. Apakah Anda menelepon bashatau menelepon sh?
mikeserv
@TeresaeJunior Bisakah Anda datang ke sini dan memposting output? Saya hanya ingin mendapatkan ide yang lebih baik tentang apa yang terjadi.
mikeserv
Tidakkah saya harus menambahkan teks jawaban saya ke bagian bawah Anda, untuk melengkapinya, dan kemudian menghapus saya?
Teresa e Junior
20

Mari lakukan benchmark.

Dengan bash:

$ strace -cf bash -c 'for i in $(seq 1 1000); do bash -c ":"; done'

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 99.12    0.376044         188      2004      1002 wait4
  0.74    0.002805           3      1002           clone
  0.03    0.000130           0      4037           read
  0.03    0.000119           0     15026           rt_sigprocmask
  0.03    0.000096           0     15040      6017 stat
  0.01    0.000055           0      8011           open
  0.01    0.000024           0      5013           getegid
  0.01    0.000021           0     16027           rt_sigaction
  0.00    0.000017           0      9020      5008 access
  0.00    0.000014           0      1001      1001 getpeername
  0.00    0.000013           0      1001           getpgrp
  0.00    0.000012           0      5013           geteuid
  0.00    0.000011           0     15025           mmap
  0.00    0.000011           0      1002           rt_sigreturn
  0.00    0.000000           0         1           write
  0.00    0.000000           0      8017           close
  0.00    0.000000           0      7011           fstat
  0.00    0.000000           0      8012           mprotect
  0.00    0.000000           0      2004           munmap
  0.00    0.000000           0     18049           brk
  0.00    0.000000           0         1           pipe
  0.00    0.000000           0         1           dup2
  0.00    0.000000           0      1001           getpid
  0.00    0.000000           0      1002           execve
  0.00    0.000000           0      1001           uname
  0.00    0.000000           0      1001           getrlimit
  0.00    0.000000           0      5013           getuid
  0.00    0.000000           0      5013           getgid
  0.00    0.000000           0      1001           getppid
  0.00    0.000000           0      1002           arch_prctl
  0.00    0.000000           0      1001           time
------ ----------- ----------- --------- --------- ----------------
100.00    0.379372                158353     13028 total

Dengan dash:

$ strace -cf bash -c 'for i in $(seq 1 1000); do dash -c ":"; done'
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 73.88    0.008543           4      2004      1002 wait4
 25.35    0.002932           3      1002           clone
  0.62    0.000072           0      9026           rt_sigprocmask
  0.10    0.000011           0      1002           rt_sigreturn
  0.05    0.000006           0     15027           rt_sigaction
  0.00    0.000000           0      1037           read
  0.00    0.000000           0         1           write
  0.00    0.000000           0      2011           open
  0.00    0.000000           0      2017           close
  0.00    0.000000           0      2040        17 stat
  0.00    0.000000           0      2011           fstat
  0.00    0.000000           0      8025           mmap
  0.00    0.000000           0      3012           mprotect
  0.00    0.000000           0      1004           munmap
  0.00    0.000000           0      3049           brk
  0.00    0.000000           0      3020      3008 access
  0.00    0.000000           0         1           pipe
  0.00    0.000000           0         1           dup2
  0.00    0.000000           0      1001           getpid
  0.00    0.000000           0         1         1 getpeername
  0.00    0.000000           0      1002           execve
  0.00    0.000000           0         1           uname
  0.00    0.000000           0         1           getrlimit
  0.00    0.000000           0        13           getuid
  0.00    0.000000           0        13           getgid
  0.00    0.000000           0      1013           geteuid
  0.00    0.000000           0        13           getegid
  0.00    0.000000           0      1001           getppid
  0.00    0.000000           0         1           getpgrp
  0.00    0.000000           0      1002           arch_prctl
  0.00    0.000000           0         1           time
------ ----------- ----------- --------- --------- ----------------
100.00    0.011564                 60353      4028 total

Setiap iterasi hanya memulai shell dan tidak melakukan apa-apa dengan operator tanpa op - titik dua , lalu berhenti.

Seperti yang diperlihatkan hasil, dashsangat cepat daripada bashsaat startup. dashlebih kecil, dan bergantung pada pustaka yang kurang dibagi daripada bash:

$ du -s /bin/bash 
956 /bin/bash

$ du -s /bin/dash 
108 /bin/dash

$ ldd /bin/bash
    linux-vdso.so.1 =>  (0x00007fffc7947000)
    libtinfo.so.5 => /lib/x86_64-linux-gnu/libtinfo.so.5 (0x00007f5a8110d000)
    libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f5a80f09000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5a80b7d000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f5a81352000)

$ ldd /bin/dash
    linux-vdso.so.1 =>  (0x00007fff56e5a000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb24844c000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fb2487f3000)

Ini tentang waktu startup, bagaimana beroperasi. Mari lakukan patokan lain:

$ time dash -c 'for i in $(seq 1 1000000);do [ 1 = 1 ];done'

real    0m2.684s
user    0m2.728s
sys     0m0.100s

$ time bash -c 'for i in $(seq 1 1000000);do [ 1 = 1 ];done'

real    0m6.996s
user    0m6.820s
sys     0m0.376s

Dengan tes sederhana 1 = 1, dashmasih jauh lebih cepat daripada bash.

cuonglm
sumber
Jawaban Anda sangat dihargai, tetapi tampaknya Anda mengukur hanya seberapa cepat shell untuk startup, bukan seberapa cepat itu beroperasi, kan?
Teresa e Junior
1
@TeresaeJunior: Ya, saya menyebutkan tentang waktu startup saja.
cuonglm
Saya berasumsi seq 1 100000seharusnya seq 1 1000?
Mikel
1
Tetapi untuk dashtest case Anda hanya seq 1 1000?
Mikel
Oh, maaf, ini 1000untuk startup dan 1000000untuk operasi, diperbaiki.
cuonglm
7

Berikut ini beberapa pengaturan waktu startup dari berbagai shell di UNIX bersertifikat (Mac OS X 10.10.3). Saya menulis ulang tes untuk menggunakan tcsh untuk mengontrol loop sehingga shell yang diuji bukan yang mengendalikan loop. Untuk setiap shell, loop dieksekusi lima kali sebelum penghitungan waktu, untuk memastikan bahwa shell dieksekusi dan skrip berada dalam cache.

Seperti yang Anda lihat, tidak ada pemenang yang jelas, tetapi ada satu pecundang yang pasti. Bagaimanapun, bash 4 jelas lebih lambat dari bash 3. Dash berkinerja baik, tetapi mengingat bahwa ksh93 sekarang open-source, tidak ada alasan nyata untuk tidak menggunakannya untuk semuanya (permintaan maaf jika saya salah paham tentang segala jenis lisensi): ksh93 cepat, solid , dan standar de-facto di UNIX-land (jika tidak di GNU / Linux-land); ia menyediakan superset dari fungsi shell POSIX (sejauh yang saya mengerti, shell POSIX didasarkan pada ksh88); itu sama dengan bash sebagai shell interaktif, meskipun tertinggal dibandingkan dengan tcsh. Dan yang kalah tentu saja zsh.

/bin/bash is v3.2.57(1)
/usr/local/bin/bash is v4.3.33(1)
dash is v0.5.8
ksh is v93u+
mksh is vR50f
pdksh is v5.2.14
/opt/heirloom/5bin/sh is from SysV
yash is v2.37
zsh is v5.0.5

% cat driver.csh 
#!/bin/tcsh

foreach s ( $* )
    echo
    echo "$s"
    foreach i ( `seq 1 5` )
        ./simple_loop.csh "$s"
    end
    /usr/bin/time -p ./simple_loop.csh "$s"
end

% cat simple_loop.csh 
#!/bin/tcsh

set shell = `which ${1}`
foreach i ( `seq 1 1000` )
    ${shell} -c ":"
end

% ./driver.csh /bin/bash /usr/local/bin/bash dash ksh mksh pdksh /opt/heirloom/5bin/sh yash zsh 
/bin/bash
real         4.21
user         1.44
sys          1.94

/usr/local/bin/bash
real         5.45
user         1.44
sys          1.98

dash
real         3.28
user         0.85
sys          1.11

ksh
real         3.48
user         1.35
sys          1.68

mksh
real         3.38
user         0.94
sys          1.14

pdksh
real         3.56
user         0.96
sys          1.17

/opt/heirloom/5bin/sh
real         3.46
user         0.92
sys          1.11

yash
real         3.97
user         1.08
sys          1.44

zsh
real        10.88
user         3.02
sys          5.80
Alun Carr
sumber
Kesimpulan saya menggunakan ksh93 juga. Itu berada di bawah Lisensi Publik Umum, yang telah disetujui oleh FSF.
Teresa e Junior
0

Ada terlalu banyak kasus uji yang tidak adil dalam banyak jawaban di sini. Jika menguji dua shell maka gunakan sintaks yang benar untuk masing-masing. Dan dalam bash, doublebracket jauh lebih cepat dan lebih dapat diandalkan daripada singlebracket, jadi ada perbedaan kecepatan yang jauh lebih sedikit sama sekali. Juga gunakan bashism yang dioptimalkan dan kemudian perbedaan kecepatan ini juga lebih sedikit. Di sistem saya, bash berjalan seperti neraka, dengan banyak menggunakan bashism. Dan setara posix di dasbor lebih lambat di sini. Ini tidak benar bahwa tanda hubung selalu beberapa kali lebih cepat daripada bash. Benar-benar sangat tidak adil untuk membandingkan baris perintah posix di keduanya, yang berlari selalu menjadi yang tercepat. Dalam pandangan saya posix sudah usang. Dan dalam hal kompatibilitas, sangat sulit untuk menemukan sistem yang relevan saat ini, mereka tidak menggunakan bash shell.

Perbandingan yang baik adalah: menggunakan baris perintah terbaik di setiap shell, untuk menyelesaikan pekerjaan tertentu. Bukan hanya baris perintah yang persis sama, ketika hanya satu shell yang benar-benar memiliki keunggulan di sini. Perbandingan seperti ini tidak dapat diandalkan dan tidak menunjukkan kinerja nyata dari para pesaing. Saya melihat pada pekerjaan saya setiap hari, shell mana yang lebih cepat dalam banyak kasus penggunaan.

Misalnya, untuk mengganti semua akarakter dalam string dengan bkarakter, di bash Anda dapat menulis "${varname//a/b}"sementara di dasbor Anda harus memanggil alat eksternal seperti ini: "$(echo "$varname" | sed 's/a/b/g')". Jika Anda harus mengulanginya beberapa ratus kali - menggunakan bashism dapat memberi Anda 2x percepatan.

jeff
sumber
3
Apakah Anda memiliki contoh yang dapat Anda perbarui jawaban untuk menunjukkan bagaimana bash dapat menutup kesenjangan kinerja atau bahkan lebih cepat pada tugas yang setara? Jawaban Anda akan jauh lebih kuat dengan beberapa contoh spesifik.
Eric Renouf