Is dash or some other shell "faster" than bash?

Teresa e Junior

I always thought that the only benefit of using dash instead of bash was that dash was smaller, and therefore many instances of dash would start faster at boot time.

But I have done some research, and found some people migrating all their scripts to dash in the hope they would run faster, and I also found this in the article DashAsBinSh in the Ubuntu Wiki:

The major reason to switch the default shell was efficiency. bash is an excellent full-featured shell appropriate for interactive use; indeed, it is still the default login shell. However, it is rather large and slow to start up and operate by comparison with dash.

Nowadays I've been using lots of bash scripts for many things on my system, and my problem is that I have a particular script that I'm running continuously 24/7, that spawns around 200 children, which together heat my computer 10°C more than in normal usage.

It is a rather large script with lots of bashisms, so porting them to POSIX or some other shell would be very time consuming (and POSIX doesn't really matter for personal use), but it would be worth if I could reduce some of this CPU usage. I know there are also other things to consider, like calling an external binary like sed for a simple bashism like ${foo/bar}, or grep instead of =~.

TL;DR is really bash slower to start up and operate in comparison with dash? Are there other Unix shells which are more efficient than bash?

mikeserv

SHELL SEQ:

Probably a useful means of bench-marking a shell's performance is to do a lot of very small, simple evaluations repetitively. It is important, I think, not just to loop, but to loop over input, because a shell needs to read <&0.

I thought this would complement the tests @cuonglm already posted because it demonstrates a single shell process's performance once invoked, as opposed to his which demonstrates how quickly a shell process loads when invoked. In this way, between us, we cover both sides of the coin.

Here's a function to facilitate the 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

It either increments a variable once per newline read or, as a slight-optimization, if it can, it increments 50 times per newline read. Every time the variable is incremented it is printed to stdout. It behaves a lot like a sort of seq cross nl.

And just to make it very clear what it does - here's some truncated set -x; output after inserting it just before time in the function above:

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

So each shell is first called like:

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

...to generate the input that it will need to loop over when it reads in 3<<\SCRIPT - or when cat does, anyway. And on the other side of that |pipe it calls itself again like:

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

So aside from the initial call to env (because cat is actually called in the previous line); no other processes are invoked from the time it is called until it exits. At least, I hope that's true.

Before the numbers...

I should make some notes on portability.

  • posh doesn't like $((n=n+1)) and insists on $((n=$n+1))

  • mksh doesn't have a printf builtin in most cases. Earlier tests had it lagging a great deal - it was invoking /usr/bin/printf for every run. Hence the echo -n above.

  • maybe more as I remember it...

Anyway, to the numbers:

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

That'll get 'em all in one go...

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 = MAYBE OK?

Still, this is a rather arbitrary test, but it does test reading input, arithmetic evaluation, and variable expansion. Maybe not comprehensive, but possibly near to there.

EDIT by Teresa e Junior: @mikeserv and I have done many other tests (see our chat for details), and we found the results could be summarized like this:

  • If you need speed, go definitely with dash, it is much faster than any other shell and about 4x faster than bash.
  • While busybox's shell can be much slower than dash, in some tests it could be faster, because it has many of its own userland utilities, like grep, sed, sort, etc., which don't have as many features as the commonly used GNU utilities, but can get the work done as much.
  • If speed is not everything you care about, ksh (or ksh93) can be considered the best compromisse between speed and features. It's speed compares to the smaller mksh, which is way faster than bash, and it has also some unique features, like floating point arithmetic.
  • Although bash is famous for its simplicity, stability, and functionality, it was the slowest of all shells in the majority of our tests, and by a large margin.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

Configure nix-shell to use a shell other than Bash?

How to switch to bash shell from some other shell?

Why can Vim open large files faster than some other text editors?

are there any other shells than bash/dash which are used used as default in GNU/Linux distros?

What are some other possible sources Git Bash could be pulling from other than ~/.bashrc and ~/.bash_profile

Are some general purpose registers faster than others?

Insertion sort much faster than shell sort

Breeze going faster than the shell togglespinner

How to change shell to dash from bash

Replace user shell from dash to bash

Why one delegate is faster than the other?

Finding which code segment is faster than the other

Faster Regex other than .+ to match whole string

Any other process faster than sqldf in r

Why is one approach for backtracking faster than the other?

What are some cases that the number of spaces do matter in bash (or other shell) scripts

How do I run a command in bash from zsh (or some other shell) in one line?

Why setting a Label's text by using String.Format() runs 20 times faster with some Int32 variables than with other Int32 variables?

Why in Matlab .* operator is faster than * for a scalar in some case?

How to use more than once the stdin with a "-" (dash) in a bash script?

Bash getopt does not accept some of the double dash (--) character

Identify directory other than some selected directory

Can't change shell from dash to bash on Linux Mint VM

Why are the fast integer types faster than the other integer types?

elif versus else if; is one faster than the other and looking at python assembly

Why one code (matmul) is faster than the other (Python)

Why one code (matmul) is faster than the other (Python)

Why one code (matmul) is faster than the other (Python)

Python: why is one generator faster than other with `yield` inside?