ダッシュまたは他のシェルはbashよりも「高速」ですか?


57

私はいつも、bashの代わりにdashを使用することの唯一の利点は、dashが小さいことであり、したがって、dashの多くのインスタンスがブート時に高速に起動するだろうと考えていました。

しかし、私はいくつかの研究を行い、より速く実行されることを期待して、すべてのスクリプトをダッシュ​​に移行する人を見つけました。また、Ubuntu Wikiの記事DashAsBinShでこれを見つけました。

デフォルトのシェルを切り替える主な理由は効率性です。bashは、インタラクティブな使用に適した優れたフル機能のシェルです。確かに、それはまだデフォルトのログインシェルです。ただし、ダッシュボードと比較する、起動と操作がかなり大きく、遅くなります。

最近、システム上の多くのことに多くのbashスクリプトを使用していますが、私の問題は、24時間年中無休で実行している特定のスクリプトがあることです。通常の使用法よりもC。

それは多くのバシズムを伴うかなり大きなスクリプトであるため、それらをPOSIXまたは他のシェルに移植するのは非常に時間がかかります(そしてPOSIXは個人的な使用にはあまり関係ありません)が、これのいくつかを減らすことができれば価値があるCPU使用率。のようsedな単純なbashismのような外部バイナリを呼び出す${foo/bar}、またはのgrep代わりに、考慮すべき他の事柄もあることを知っています=~

TL; DRはダッシュと比較してbashの起動と操作が本当に遅いですか?bash よりも効率的な他のUnixシェルはありますか?


12
パフォーマンスのために移植する場合、他の言語(perl、python、ruby)で完全に行う方が良いと思いますか?タスクの正確な性質に依存しますが、一般的にははるかに効率的です。
goldilocks 14

軽微な点:[ビルトインにする必要もあります。
ミケル14

2
メモリ使用量について心配しているのとは異なり、外部プログラムではなくシェルで計算を行っている場合(つまり、シェルを間違った方法で使用している場合)に違いがほとんどを示すことに注意してください。たとえば、私のコンピューターでは、whileループを使用して100万にカウントする(他に何もしない)スクリプトはmksh / zshで最大2倍、ダッシュで2倍以上高速ですが、実際のスクリプトでは可能な限り他のものにオフロードしますプログラム。
ロレブ14

3
bash以前は非常に遅かった。最近多くの進歩を遂げましたが、ほとんどの場合、他のほとんどのシェルよりも遅いです。
ステファンシャゼル14

1
単純なbashismを使用しないでください[ "$foo" != "${foo#*bar}" ]grepを処理します。そしてsed事:while [ "$foo" != "${foo#*bar}" ]; do s=$s${foo%%bar*} foo=${foo#*bar} ; done ; foo=$s$foo。いずれかを関数に入れることができます。
mikeserv

回答:


39

シェル配列:

おそらく、シェルのパフォーマンスをベンチマークでマークする便利な方法は、非常に小さく単純な評価を繰り返し行うことです。シェルが読み取る必要があるため、ループするだけでなく、入力をループすることが重要だと思います<&0

これは、シェルプロセスが呼び出されたときにどれだけ速くロードされるかを示すのに対して、@ cuonglmがすでに投稿されたテストを補完するものだと思いました。このようにして、私たちの間で、コインの両側を覆います。

デモを容易にする機能は次のとおりです。

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

改行の読み取りごとに1回変数をインクリメントするか、可能であれば少し最適化して、改行の読み取りごとに50インクリメントします。変数がインクリメントされるたびに、変数に出力されstdoutます。それは一種のseq十字架のようにふるまいnlます。

そして、それが何をするのかを非常に明確にするために- 上記の関数のset -x;直前に挿入した後の出力の一部を以下timeに示します。

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

したがって、各シェルは最初に次のように呼び出されます。

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

...読み込み時にループオーバーする必要がある入力を生成します3<<\SCRIPT- catとにかくそうする場合。そしてその反対側では、次の|pipeように再び自分自身を呼び出します:

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

だから、わきの最初の呼び出しからenv (ので、cat実際には前の行に呼ばれています)。呼び出されてから終了するまで、他のプロセスは呼び出されません。少なくとも、それが本当であることを願っています。

数字の前に...

移植性についていくつか注意する必要があります。

  • posh好き$((n=n+1))ではないし、主張します$((n=$n+1))

  • mkshprintfほとんどの場合、ビルトインはありません。以前のテストではかなり遅れていました/usr/bin/printf。実行ごとに呼び出されていました。したがって、echo -n上記。

  • 多分私はそれを覚えているように...

とにかく、数字に:

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

それはすべて一度にそれらを取得します...

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

任意=大丈夫ですか?

それでも、これはかなりarbitrary意的なテストですが、入力値の読み取り、算術評価、変数展開をテストします。包括的ではないかもしれませんが、おそらくそこに近いでしょう。

編集by Teresa e Junior:@mikeservと私は他にも多くのテストを行っています(詳細についてはチャットをご覧ください)。結果は次のように要約できることがわかりました。

  • 速度が必要な場合は、必ずdashを使用してください。他のシェルよりもはるかに高速で、bashよりも約4倍高速です。
  • 一方でbusyboxののシェルがよりはるかに遅くなることがダッシュそれは、独自のユーザランドユーティリティの多くを持っているので、いくつかのテストでは、速くなる可能性のようなgrepsedsort、など、一般的に使用されるGNUほど多くの機能を持ちませんユーティリティが、仕事をすることができます。
  • 速度が気になるすべてではない場合は、ksh(またはksh93)が速度と機能の最適な比較と見なすことができます。速度はbashよりもはるかに高速な、より小さなmkshと比較し、浮動小数点演算などのいくつかのユニークな機能も備えています。
  • が、bashは、そのシンプルさ、安定性、および機能のために有名である、それは、大きなマージンによって、我々のテストの大部分において、すべてのシェルの最も遅いでした。

このコードをbash(およびkshとzsh)で動作させることはできません。dash、mksh、およびpdkshでのみ動作します。Bash私は4.2.37(1)-releaseDebianと4.2.45(2)-releasePorteus LiveCD(Slackware)から試しました。なしではnull=、数値を出力する代わりに、Returnキーを押し続けたように動作し、SIGKILLで bashを削除する必要があります。
テレサeジュニア14

そして、私もを試しましたがbash --posix、役に立ちませんでした。
テレサeジュニア14

@TeresaeJunior-それは可能かもしれませんが-で動作するとは思わないがzshzshをハイジャックしtty、インタラクティブシェルを起動します。私bashは同じことをすることを期待しています-そのため、その--posixリンクのみを呼び出すように注意しています。私はあなたがそれらのほとんどについてあなたが期待するようにそれをさせることができます、しかし、それはその価値以上の仕事かもしれません。あなたは、呼び出しているbashか、あなたが求めていますかsh
mikeserv 14

@TeresaeJunior ここに来て、出力を投稿できますか?何が起こっているのかをより良く知りたいです。
mikeserv 14

私の答えのテキストをあなたの一番下に追加し、それを補完してから、私のものを削除してはいけませんか?
テレサeジュニア14

20

ベンチマークをしましょう。

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

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

各反復は、シェルを起動し、no-op演算子(colon)で何もせずに終了します。

結果が示すように、起動時dashよりも非常に高速ですbashdashより小さく、依存する共有ライブラリが以下よりも少ない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)

これは、起動時間、操作方法についてです。別のベンチマークをしましょう:

$ 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

単純なテストでは1 = 1dashまだよりもはるかに高速ですbash


あなたの答えは非常にありがたいのですが、実際に動作する速度ではなく、起動するシェルの速度だけを測定しているようです。
テレサeジュニア14

1
@TeresaeJunior:はい、起動時間についてのみ言及します。
cuonglm 14

あるseq 1 100000べきだと思うseq 1 1000
ミケル14

1
しかし、あなたのdashテストケースではそれだけseq 1 1000ですか?
ミケル14

ああ、申し訳ありませんが、それ1000はスタートアップと1000000運用のためです。
cuonglm

7

認定されたUNIX(Mac OS X 10.10.3)のさまざまなシェルの起動タイミングを次に示します。テストを書き直して、tcshを使用してループを制御し、テスト対象のシェルがループを制御するシェルではないようにしました。各シェルについて、タイミングの5回前にループが実行され、シェル実行可能ファイルとスクリプトがキャッシュ内にあることを確認します。

ご覧のとおり、明確な勝者はいませんが、決定的な敗者は1人います。いずれにせよ、bash 4はbash 3より明らかに遅いです。Dashは優れたパフォーマンスを発揮しますが、ksh93がオープンソースであるため、すべてに使用しない理由はありません(ライセンスの良さを誤解した場合の謝罪):ksh93は高速で安定しています、およびUNIXランドの事実上の標準(GNU / Linuxランドにない場合)。POSIXシェル機能のスーパーセットを提供します(私の知る限り、POSIXシェルはksh88に基づいていました)。対話型シェルとしてのbashと同等ですが、tcshと比較すると遅れています。そして、敗者はもちろん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

私の結論はksh93も使用していました。FSFによって承認されたCommon Public Licenseの下にあります。
テレサeジュニア

0

ここの多くの答えには不公平なテストケースが多すぎます。2つのシェルをテストする場合、それぞれに対して正しい構文を使用します。また、bashでは、ダブルブラケットはシングルブラケットよりもはるかに高速で信頼性が高いため、速度の差はまったくありません。また、最適化されたbashismsを使用すると、これらの速度の差も小さくなります。私のシステムでは、bashは地獄のように動作し、bashismを多用しています。そして、ダッシュのposix同等物はここでより遅くなります。これは、dashが常にbashよりも数倍速いということは正しくありません。両方のposixコマンドラインを比較するのは本当に不公平です。私の見解では、posixは時代遅れです。また、互換性の観点から、今日では関連するシステムを見つけるのは非常に難しく、bashシェルを使用していません。

適切な比較は、各シェルで可能な限り最適なコマンドラインを使用して、特定のジョブを完了することです。ここで実際に利点があるのは1つのシェルだけであるため、まったく同じコマンドラインだけではありません。このような比較は信頼性が低く、競合他社の実際のパフォーマンスを示していませんでした。私は毎日の仕事で、多くのユースケースでどのシェルが速いかを確認しています。

たとえばa、string内のすべての文字を文字で置き換えるにbは、bashで次の"${varname//a/b}"ように外部ツールを呼び出す必要があります"$(echo "$varname" | sed 's/a/b/g')"。数百回繰り返す必要がある場合-bashismを使用すると、2倍のスピードアップが得られます。


3
回答を更新して、bashがパフォーマンスのギャップをどのように埋めることができるか、または同等のタスクでさらに高速になるかを示す例はありますか?いくつかの具体的な例があれば、あなたの答えはずっと強くなるでしょう。
エリックルヌーフ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.