Bashの代わりにdashを使用する唯一の利点は、dashが小さいことであり、したがって、多くのdashのインスタンスは起動時により速く起動するだろうといつも思っていました。
しかし、いくつかの調査を行ったところ、すべてのスクリプトを移行してダッシュが速くなることを期待してダッシュする人がいることがわかりました。また、Ubuntu Wikiの記事 DashAsBinSh でも見つかりました。
デフォルトのシェルを切り替える主な理由は 効率。 bashは、インタラクティブな使用に適した優れたフル機能のシェルです。実際、それはまだデフォルトのログインシェルです。しかし、それはかなり大きく、 起動が遅い そして操作する ダッシュと比較して。
この頃私は私のシステムの多くのもののために多くのbashスクリプトを使用してきました、そして私の問題は私が24時間年中無休で実行している特定のスクリプトがあり、それが一緒に私のコンピューターを10°加熱する約200の子供を生み出すことです通常の使用よりもCが多い。
これは多くのバシズムを持つかなり大きなスクリプトなので、それらをPOSIXや他のシェルに移植するのは非常に時間がかかります(そしてPOSIXは実際には個人的な使用では問題になりません)が、これを減らすことができれば価値がありますCPU使用率。 _${foo/bar}
_のような単純なバシズムのためにsed
のような外部バイナリを呼び出す、または_=~
_の代わりにgrep
を呼び出すなど、他にも考慮すべき点があることを知っています。
TL; DRは、起動するのが本当に遅いです そして操作する ダッシュと比較して? bashよりも効率的なUnixシェルは他にありますか?
おそらく、シェルのパフォーマンスをベンチマークする便利な方法は、非常に小規模で単純な評価を繰り返し実行することです。シェルは_<&0
_を読み取る必要があるため、ループするだけでなくinputをループすることが重要だと思います。
これはテストを補完するものだと思いました @ 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
cross _nl
とよく似ています。
そして、それが何をするかを非常に明確にするために-上記の関数のtime
の直前に挿入した後の一部の切り捨てられた_set -x;
_出力があります:
_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))
を主張します
mksh
には、ほとんどの場合printf
が組み込まれていません。以前のテストでは、大幅な遅延がありました-すべての実行で_/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
_
それでも、これはかなり恣意的なテストですが、入力の読み取り、算術評価、変数展開をテストします。多分包括的ではないかもしれませんが、おそらくそこに近いでしょう。
EDITE by Teresa e Junior:@mikeservと私は他の多くのテストを実行しました(詳細は 私たちのチャット を参照してください)。結果は次のように要約できることがわかりました:
grep
、sed
、sort
などの独自のユーザーランドユーティリティの多くを備えているため、一般的に使用されているGNUユーティリティ。ただし、同じくらいの作業を行うことができます。ベンチマークをしましょう。
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
は起動時にbash
よりも非常に高速です。 dash
は小さく、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 = 1
、dash
はbash
よりもはるかに高速です。
以下は、認定されたUNIX(Mac OS X 10.10.3)でのさまざまなシェルの起動タイミングです。 tcshを使用してループを制御するようにテストを書き直したので、テストされているシェルがループを制御するシェルではありませんでした。各シェルに対して、タイミングの前に5回ループが実行され、シェル実行可能ファイルとスクリプトがキャッシュにあることを確認します。
ご覧のとおり、明確な勝者はありませんが、決定的な敗者が1人います。とにかく、bash 4はbash 3よりも明らかに低速です。Dashは十分に機能しますが、ksh93がオープンソースになったことを考えると、これをすべてに使用しないという本当の理由はありません(ライセンスの細かい点を誤解した場合はお詫び):ksh93は高速で堅実です、およびUNIXランドの事実上の標準(GNU/Linuxランドでない場合)。 POSIXシェル機能のスーパーセットを提供します(私が理解している限り、POSIXシェルはksh88に基づいていました)。 tcshに比べると遅れますが、対話型シェルとしてのbashと同じです。そして敗者はもちろん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
ここの多くの回答には、不当なテストケースが多すぎます。 2つのシェルをテストする場合は、それぞれに正しい構文を使用してください。また、bashでは、ダブルブラケットはシングルブラケットよりもはるかに高速で信頼性が高いため、速度の違いはまったくありません。また、最適化されたバシズムを使用すると、これらの速度の違いも少なくなります。私のシステムでは、bashは地獄のように動作し、bashismを多用しています。そして、ダッシュでのposixの同等物はここでは遅くなります。これは正しくありません。ダッシュは常にbashよりも数倍高速です。実際、両方のposixコマンドラインを比較するのはかなり不公平です。ダッシュは常に最速です。私の見解では、posixはかなり古くなっています。そして、互換性の面では、今日、関連するシステムを見つけるのは非常に難しく、bash Shellを使用していませんでした。
適切な比較は、各シェルで可能な限り最高のコマンドラインを使用して、特定のジョブを完了することです。 1つのシェルだけが実際にここで利点を持っている場合、まったく同じコマンドラインだけでなく、このような比較は信頼できず、競合他社の実際のパフォーマンスを示していませんでした。私は毎日の仕事を見ていますが、シェルは多くのユースケースでより高速です。
たとえば、文字列内のすべてのa
文字をb
文字に置き換えるには、bashでは_"${varname//a/b}"
_と記述し、ダッシュでは次のような外部ツールを呼び出す必要があります。"$(echo "$varname" | sed 's/a/b/g')"
。数百回繰り返す必要がある場合、bashismを使用すると2倍のスピードアップが得られます。
アルゴリズムのソリューションを必要とする合理的に効率的な素朴な問題とは対照的に、すべてのパフォーマンスの問題が効率の問題であるとは限りません。全体でどちらか一方を修正する場合、最も効率的なソリューションが最高のパフォーマンスを発揮するのは、その数倍です。
問題を修正するための適切な方法は誰にも教えられませんが、POSIXシェルが解決できる特定の効率問題が1つあります。これが、すべての起動スクリプトがBashからDashに移植された理由です。
多数のスクリプトが同時に起動するためにパフォーマンスの問題が発生しているが、それらのスクリプトを単独で使用するとかなり効率的である場合は、それらすべてをPOSIXシェルに移植することをお勧めします。
ただし、実際には一度に非常に多くのプロセスを生成する必要があること、およびスクリプトの特定の部分を別の方法で書き換えることができないことを確認するために、最初にダブルチェックを行う可能性があります。生成された200の子プロセスが他のスクリプトであるかどうかは言及していませんが、より大きな親スクリプトの代わりにそれらを単に移植することが可能かもしれません。