我一直认为使用 dash 而不是 bash 的唯一好处是 dash 更小,因此 dash 的许多实例在启动时启动得更快。
但我做了一些研究,发现有些人将所有脚本迁移到 dash,希望它们运行得更快,我也在文章中发现了这一点达世币在 Ubuntu 维基百科中:
切换默认 shell 的主要原因是效率。 bash 是一个优秀的全功能 shell,适合交互式使用;事实上,它仍然是默认的登录 shell。不过,它的规模相当大,而且启动慢并操作与破折号相比。
如今,我在系统上的许多事情上使用了大量的 bash 脚本,我的问题是我有一个特定的脚本,我每天 24/7 连续运行,它会产生大约 200 个子脚本,它们一起将我的计算机加热 10° C 比正常使用时多。
这是一个相当大的脚本,有很多 bashism,因此将它们移植到 POSIX 或其他一些 shell 会非常耗时(POSIX 对于个人使用来说并不重要),但如果我可以减少其中的一些,那就值得了CPU使用率。我知道还有其他事情需要考虑,比如调用外部二进制文件来sed
调用简单的 bashism,例如${foo/bar}
, 或grep
代替=~
。
长话短说bash 启动速度确实较慢并操作与破折号相比?还有其他更强大的 Unix shell 吗?高效的比bash?
答案1
外壳序列:
也许对 shell 性能进行基准测试的一种有用方法是重复进行大量非常小的、简单的评估。我认为重要的是不仅要循环,还要循环输入,因为 shell 需要读取<&0
。
我认为这可以补充测试@cuonglm 已发布因为它演示了单个 shell 进程在调用后的性能,而不是他的演示了 shell 进程在调用时加载的速度。这样,我们之间就涵盖了硬币的两面。
这是一个方便演示的函数:
sh_bench() ( #don't 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
它要么在每次换行读取时递增变量一次,要么作为轻微优化(如果可以的话),在每次换行读取时递增 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'
所以每个 shell 首先被调用如下:
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
任意=也许可以?
尽管如此,这是一个相当随意的测试,但它确实测试了读取输入、算术评估和变量扩展。也许不全面,但可能接近那里。
编辑:Teresa e Junior:@mikeserv 和我做了很多其他测试(参见我们的聊天详细信息),我们发现结果可以总结如下:
- 如果你需要速度,一定要选择短跑,它比任何其他 shell 都快得多,大约比巴什。
- 尽管忙碌盒的 shell 可能比短跑,在某些测试中它可能会更快,因为它有许多自己的用户态实用程序,如
grep
、sed
、sort
等,它们没有常用的 GNU 实用程序那么多的功能,但可以完成同样多的工作。 - 如果速度不是你关心的一切克什(或者克什93)可以被认为是速度和功能之间的最佳折衷。它的速度与较小的相比姆克什,这比巴什,而且它还有一些独特的功能,比如浮点运算。
- 虽然巴什它以其简单性、稳定性和功能性而闻名,但在我们的大多数测试中,它是所有 shell 中最慢的,而且速度相差很大。
答案2
让我们做一个基准测试。
和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
每次迭代仅启动一个 shell,并且对无操作运算符不执行任何操作 -冒号,然后退出。
结果显示,比启动时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
。
答案3
以下是经过认证的 UNIX (Mac OS X 10.10.3) 中各种 shell 的一些启动时序。我重写了测试以使用 tcsh 来控制循环,以便被测试的 shell 不是控制循环的 shell。对于每个 shell,循环在计时之前执行五次,以确保 shell 可执行文件和脚本位于缓存中。
正如您所看到的,没有明确的赢家,但有一个明确的输家。无论如何,bash 4 明显比 bash 3 慢。Dash 表现良好,但考虑到 ksh93 现在是开源的,没有真正的理由不将它用于所有用途(如果我误解了任何许可细节,请道歉):ksh93 快速、稳定,以及 UNIX 领域事实上的标准(如果不在 GNU/Linux 领域的话);它提供了 POSIX shell 功能的超集(据我所知,POSIX shell 基于 ksh88);作为交互式 shell,它与 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
答案4
这里的很多答案中有太多不公平的测试用例。如果测试两个 shell,则为每个 shell 使用正确的语法。在 bash 中,双括号比单括号更快、更可靠,因此速度差异要小得多。还可以使用优化的 bashisms,然后这些速度差异也会更小。在我的系统上,bash 运行得非常糟糕,并且大量使用了 bashism。而 dash 中的 posix 等价物在这里速度较慢。这是不正确的,因为 dash 总是比 bash 快好几倍。确实,比较两者的 posix 命令行是相当不公平的,谁的 dash 总是最快的。在我看来 posix 已经严重过时了。而且在兼容性方面,现在确实很难找到相关的系统,他们没有使用bash shell。
一个很好的比较是:在每个 shell 中使用尽可能好的命令行来完成特定的工作。不仅是完全相同的命令行,只有一个 shell 在这里确实具有优势。这样的比较是不可靠的,并且不能显示竞争对手的真实表现。我在日常工作中看到,在许多用例中哪个 shell 更快。
例如,要将a
字符串中的所有字符替换为b
字符,在 bash 中您可以编写,"${varname//a/b}"
而在 dash 中则必须调用外部工具,如下所示:"$(echo "$varname" | sed 's/a/b/g')"
。如果你必须重复几百次 - 使用 bashism 可以给你 2 倍的加速。