it-swarm.com.de

Ist Dash oder eine andere Shell "schneller" als Bash?

Ich habe immer gedacht, dass der einzige Vorteil der Verwendung von Dash anstelle von Bash darin besteht, dass Dash kleiner ist und daher viele Instanzen von Dash beim Booten schneller starten.

Aber ich habe einige Nachforschungen angestellt und festgestellt, dass einige Leute alle ihre Skripte migrieren, um zu stürzen, in der Hoffnung, dass sie schneller laufen, und ich fand dies auch im Artikel DashAsBinSh im Ubuntu-Wiki:

Der Hauptgrund für den Wechsel der Standard-Shell war effizienz. bash ist eine exzellente Shell mit vollem Funktionsumfang, die für den interaktiven Einsatz geeignet ist. In der Tat ist es immer noch die Standard-Login-Shell. Es ist jedoch ziemlich groß und langsam zu starten und bedienen im Vergleich zu Bindestrich.

Heutzutage habe ich viele Bash-Skripte für viele Dinge auf meinem System verwendet, und mein Problem ist, dass ich ein bestimmtes Skript habe, das ich rund um die Uhr kontinuierlich ausführe und das ungefähr 200 Kinder hervorbringt, die zusammen meinen Computer um 10 ° heizen C mehr als im normalen Gebrauch.

Es ist ein ziemlich großes Skript mit vielen Bashismen, daher wäre das Portieren auf POSIX oder eine andere Shell sehr zeitaufwändig (und POSIX spielt für den persönlichen Gebrauch keine Rolle), aber es wäre wert, wenn ich etwas davon reduzieren könnte CPU auslastung. Ich weiß, dass es auch andere Dinge zu beachten gibt, wie das Aufrufen einer externen Binärdatei wie sed für einen einfachen Bashismus wie ${foo/bar} oder grep anstelle von =~.

TL; DR ist beim Starten wirklich langsamer und bedienen im Vergleich zu Bindestrich? Gibt es andere Unix-Shells, die effizienter sind als Bash?

57
Teresa e Junior

Shell SEQ:

Wahrscheinlich ist es ein nützliches Mittel, um die Leistung einer Shell zu bewerten, viele sehr kleine, einfache Bewertungen wiederholt durchzuführen. Ich denke, es ist wichtig, nicht nur eine Schleife zu erstellen, sondern eine Schleife über Eingabe, da eine Shell <&0 Lesen muss.

Ich dachte, dies würde die Tests ergänzen @ cuonglm bereits veröffentlicht , da es die Leistung eines einzelnen Shell-Prozesses nach dem Aufrufen demonstriert, im Gegensatz zu seiner, die zeigt, wie schnell ein Shell-Prozess beim Aufrufen geladen wird. Auf diese Weise decken wir zwischen uns beide Seiten der Medaille ab.

Hier ist eine Funktion zur Erleichterung der 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)'" -- "[email protected]"                 #[email protected] = 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

Entweder wird eine Variable einmal pro gelesenem Zeilenumbruch inkrementiert, oder als leichte Optimierung wird sie, wenn möglich, 50 Mal pro gelesenem Zeilenumbruch inkrementiert. Jedes Mal, wenn die Variable inkrementiert wird, wird sie auf stdout gedruckt. Es verhält sich sehr ähnlich wie eine Art seq cross nl.

Und um ganz klar zu machen, was es tut - hier ist eine abgeschnittene set -x; Ausgabe, nachdem sie kurz vor time in die obige Funktion eingefügt wurde:

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

Jede Shell heißt also zunächst wie folgt:

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

... um die Eingabe zu generieren, die beim Einlesen von 3<<\SCRIPT durchlaufen werden muss - oder wenn cat es trotzdem tut. Und auf der anderen Seite dieses |pipe Nennt es sich wieder wie folgt:

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

Also abgesehen vom ersten Aufruf von env (weil cat tatsächlich in der vorherigen Zeile aufgerufen wird); Ab dem Zeitpunkt des Aufrufs bis zum Beenden werden keine weiteren Prozesse aufgerufen. Zumindest hoffe ich, dass das stimmt.

Vor den Zahlen ...

Ich sollte einige Anmerkungen zur Portabilität machen.

  • posh mag $((n=n+1)) nicht und besteht auf $((n=$n+1))

  • mksh hat in den meisten Fällen kein printf eingebaut. Frühere Tests hatten eine große Verzögerung - es wurde bei jedem Lauf /usr/bin/printf Aufgerufen. Daher der obige echo -n.

  • vielleicht mehr, wie ich mich erinnere ...

Wie auch immer, zu den Zahlen:

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

Das bringt sie alle auf einmal ...

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 = Vielleicht in Ordnung?

Dies ist zwar ein eher willkürlicher Test, testet jedoch die Leseeingabe, die arithmetische Auswertung und die variable Erweiterung. Vielleicht nicht umfassend, aber möglicherweise in der Nähe.

EDIT von Teresa e Junior : @mikeserv und ich haben viele andere Tests durchgeführt (siehe nser Chat für Details), und wir Die Ergebnisse lassen sich folgendermaßen zusammenfassen:

  • Wenn Sie Geschwindigkeit brauchen, gehen Sie definitiv mit dash , es ist viel schneller als jede andere Shell und ungefähr 4x schneller als bash =.
  • Während die Shell von Busybox viel langsamer sein kann als dash, könnte sie in einigen Tests schneller sein, weil sie es hat Viele seiner eigenen Userland-Dienstprogramme wie grep, sed, sort usw., die nicht so viele Funktionen haben wie die häufig verwendeten GNU Dienstprogramme, kann aber die Arbeit genauso erledigen.
  • Wenn Geschwindigkeit nicht alles ist, was Sie interessiert, kann ksh (oder ksh93) als der beste Kompromiss zwischen Geschwindigkeit und angesehen werden Eigenschaften. Die Geschwindigkeit ist vergleichbar mit der kleineren mksh, die viel schneller ist als bash, und es hat auch einige einzigartige Funktionen, wie Gleitkomma-Arithmetik .
  • Obwohl bash für seine Einfachheit, Stabilität und Funktionalität bekannt ist, war es in den meisten unserer Tests die langsamste aller Schalen und mit großem Abstand .
39
mikeserv

Lassen Sie einen Benchmark machen.

Mit 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

Mit 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

Jede Iteration startet nur eine Shell und macht nichts mit dem No-Op-Operator - Doppelpunkt , dann beendet.

Wie das Ergebnis zeigt, ist dash beim Start extrem schneller als bash. dash ist kleiner und hängt von weniger gemeinsam genutzten Bibliotheken ab als 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)

Hier geht es um die Startzeit, wie wäre es mit Betrieb. Lassen Sie uns einen weiteren Benchmark durchführen:

$ 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

Mit einfachem Test 1 = 1, dash immer noch viel schneller als bash.

20
cuonglm

Hier sind einige Startzeiten verschiedener Shells unter einem zertifizierten UNIX (Mac OS X 10.10.3). Ich habe den Test umgeschrieben, um die Schleifen mit tcsh zu steuern, sodass die getestete Shell nicht diejenige ist, die die Schleifen steuert. Für jede Shell wird die Schleife fünfmal vor dem Timing ausgeführt, um sicherzustellen, dass sich die ausführbare Shell-Datei und die Skripte im Cache befinden.

Wie Sie sehen, gibt es keinen eindeutigen Gewinner, aber einen endgültigen Verlierer. Wie auch immer, Bash 4 ist deutlich langsamer als Bash 3. Dash funktioniert gut, aber da ksh93 jetzt Open Source ist, gibt es keinen wirklichen Grund, es nicht für alles zu verwenden (Entschuldigung, wenn ich Lizenzierungsprobleme falsch verstehe): ksh93 ist schnell, solide und ein De-facto-Standard in UNIX-Land (wenn nicht in GNU/Linux-Land); Es bietet eine Obermenge der POSIX-Shell-Funktionalität (soweit ich weiß, basierte die POSIX-Shell auf ksh88). es ist bash als interaktive Shell, obwohl es im Vergleich zu tcsh zurückbleibt. Und der Verlierer ist natürlich 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
7
Alun Carr

Es gibt zu viele unfaire Testfälle in vielen Antworten hier. Wenn Sie zwei Shells testen, verwenden Sie für jede die richtige Syntax. Und in Bash sind Doppelklammern viel schneller und zuverlässiger als Einzelklammern, sodass es überhaupt einen viel geringeren Geschwindigkeitsunterschied gibt. Verwenden Sie auch optimierte Bashismen, und dann sind diese Geschwindigkeitsunterschiede auch geringer. Auf meinem System läuft Bash wie die Hölle, mit starkem Einsatz von Bashismen. Und Posix-Äquivalente im Bindestrich sind hier langsamer. Dies ist nicht korrekt, da der Bindestrich immer um ein Vielfaches schneller ist als Bash. Wirklich ist es ziemlich unfair, Posix-Befehlszeilen in beiden zu vergleichen, die immer die schnellsten sein können. Meiner Meinung nach ist posix stark veraltet. Und in Bezug auf die Kompatibilität ist es heutzutage wirklich schwierig, relevante Systeme zu finden, sie haben keine Bash-Shell verwendet.

Ein guter Vergleich ist: Verwenden der bestmöglichen Befehlszeile in jeder Shell, um einen bestimmten Auftrag abzuschließen. Nicht nur genau die gleiche Kommandozeile, wenn hier nur eine Shell wirklich einen Vorteil hat. Vergleiche wie diese sind unzuverlässig und zeigten nicht die tatsächliche Leistung der Wettbewerber. Ich sehe bei meiner täglichen Arbeit, welche Shell in vielen Anwendungsfällen schneller ist.

Um beispielsweise alle a -Zeichen in der Zeichenfolge durch b -Zeichen zu ersetzen, können Sie in bash "${varname//a/b}" Schreiben, während Sie im Bindestrich ein externes Tool wie folgt aufrufen müssen: "$(echo "$varname" | sed 's/a/b/g')". Wenn Sie es einige hundert Mal wiederholen müssen, kann die Verwendung von Bashism zu einer zweifachen Beschleunigung führen.

0
jeff

Nicht alle Leistungsprobleme sind Effizienzprobleme, nur die meisten von ihnen sind es, im Gegensatz zu einigermaßen effizienten naiven Problemen, die algorithmische Lösungen erfordern. Wenn wir das eine oder andere in seiner Gesamtheit reparieren würden, hätte die effizienteste Lösung mehrmals die beste Leistung.

Niemand kann Ihnen den richtigen Weg zur Behebung des Problems nennen, aber es gibt ein spezifisches Effizienzproblem, das POSIX Shell lösen kann. Aus diesem Grund wurden alle Startskripte von Bash nach Dash portiert.

Wenn Sie Leistungsprobleme haben, weil eine große Anzahl von Skripten gleichzeitig gestartet wird, diese jedoch alle für sich genommen ziemlich effizient sind, ist es eine bevorzugte Lösung, sie alle auf POSIX Shell zu portieren.

Ich würde jedoch wahrscheinlich zuerst überprüfen, ob Sie tatsächlich so viele Prozesse gleichzeitig erzeugen müssen und ob der spezifische Teil des Skripts nicht anders neu geschrieben werden kann. Sie erwähnen nicht, ob es sich bei den 200 erzeugten untergeordneten Prozessen um andere Skripte handelt. Möglicherweise können sie nur anstelle des größeren übergeordneten Skripts portiert werden.

0
J. M. Becker