Çizgi veya başka bir kabuk, bashtan daha hızlı mıdır?


57

Ben her zaman bash yerine kısa çizgi kullanmanın tek yararının kısa çizginin daha küçük olduğunu düşünmüştüm ve bu nedenle birçok kısa çizgi örneği açılışta daha hızlı başlayacaktı.

Ancak bazı araştırmalar yaptım ve bazı kişilerin daha hızlı çalışacaklarını umuduyla tüm senaryolarını geçirdiklerini gördüm ve bunu Ubuntu Wiki'deki DashAsBinSh makalesinde de buldum :

Varsayılan kabuğu değiştirmenin ana nedeni verimlilikti . bash etkileşimli kullanım için uygun mükemmel bir tam özellikli kabuktur; Gerçekten de, hala varsayılan giriş kabuğu. Ancak, başlatmak ve çalıştırmak için tire ile karşılaştırıldığında oldukça büyük ve yavaştır .

Bugünlerde sistemimdeki birçok şey için çok sayıda basik komut dosyası kullanıyorum ve benim sorunum, bilgisayarımı 10 ° 'de ısıtan yaklaşık 200 çocuğun doğurduğu, sürekli olarak 7 gün 24 saat çalıştığım bir senaryomun olması. C normal kullanımdan daha fazla.

Çok sayıda temel dosya içeren oldukça büyük bir komut dosyasıdır, bu nedenle bunları POSIX'e veya başka bir kabuğa taşımak çok zaman alır (ve POSIX kişisel kullanım için gerçekten önemli değil), ancak bazılarını azaltabilirsem buna değer. CPU kullanımı. Ben gibi harici ikili demek gibi, dikkate alınması gereken başka şeyler de vardır biliyor sedgibi basit bashism için ${foo/bar}veya grepyerine =~.

TL; DR , çizgi ile karşılaştırıldığında başlatmak ve çalıştırmak için gerçekten çok yavaş bash ? Bash'tan daha verimli başka Unix kabukları var mı?


12
Performans için taşıyacaksanız, başka bir dilde (perl, python, ruby) tamamen yapılacağını mı düşünüyorsunuz? Genelde çok daha verimlidirler, bence, görevin doğasına bağlı olmasına rağmen.
goldilocks

Küçük nokta: [ayrıca bir yerleşik olmalıdır.
Mikel

2
Bellek kullanımı ile ilgili endişelerinizden farklı olarak, farkın çoğunlukla dış programlardan ziyade kabukta hesaplamalar yapıyorsanız gösterir (örneğin, kabuğu yanlış şekilde kullanıyorsunuz!); örneğin bilgisayarımda bir milyonu saymak için bir süre döngüsü kullanan bir komut dosyası (başka bir şey yapmamak) mksh / zsh'da ~ 2x, dash'de ise> 2x daha hızlıdır, ancak gerçek bir komut dosyasında diğerlerine mümkün olduğunca fazla yüklerim programları.
loreb

3
bashçok yavaş kullanılırdı. Son zamanlarda çok ilerleme kaydetti, ancak çoğu şey için, diğer kabuklardan çok daha yavaş.
Stéphane Chazelas

1
Basit bashism's kullanmayın . [ "$foo" != "${foo#*bar}" ]grep işini halleder. Ve sedşey: while [ "$foo" != "${foo#*bar}" ]; do s=$s${foo%%bar*} foo=${foo#*bar} ; done ; foo=$s$foo. Her iki şeyi de bir fonksiyona koyabilirsiniz.
mikeserv

Yanıtlar:


39

KABUK DİZİ:

Muhtemelen bir kabuğun performansını ölçmek için yararlı bir araç, tekrarlayan çok küçük, basit değerlendirmeler yapmaktır. Bir döngü okumak gerekiyor, çünkü sadece döngü değil, aynı zamanda girdi üzerinden döngü önemlidir <&0.

Bunun, daha önce bir kabuk işleminin ne kadar hızlı bir şekilde yüklendiğini gösteren bir kabuk işleminin performansını gösterdiği için zaten yayınlanan , @cuonglm testlerini tamamlayacağını düşündüm . Bu sayede aramızdaki madalyonun iki tarafını da kapsıyoruz.

İşte gösteriyi kolaylaştıran bir işlev:

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

Ya yeni satır başına okuma başına bir değişkeni arttırır ya da eğer yapabiliyorsa, yeni satır başına okuma başına 50 katı arttırır. Değişken her artırıldığında, basılır stdout. Bir çeşit seqhaç gibi davranıyor nl.

Ve sadece ne yaptığını açıkça belirtmek için - işte yukarıdaki fonksiyona set -x;hemen önce yerleştirdikten sonra kesilmiş bir çıktı time:

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

Böylece her kabuk önce şöyle denir:

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

... içeri 3<<\SCRIPT- ya da ne zaman cat, ne zaman okursa tekrar yazması gereken girişi oluşturmak için . Ve bunun diğer tarafında |pipekendini tekrar şöyle çağırıyor:

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

Böylece ilk çağrıdan yana env (çünkü cataslında önceki satırda denir) ; çağrıldığı andan çıkana kadar başka hiçbir işlem başlatılmaz. En azından, umarım bu doğrudur.

Rakamlardan önce ...

Taşınabilirlik hakkında bazı notlar almalıyım.

  • poshbeğenmez $((n=n+1))ve ısrar eder$((n=$n+1))

  • mkshprintfÇoğu durumda bir yerleşik yok . Daha önceki testlerde çok fazla gecikme yaşandı - /usr/bin/printfher koşuya neden oldu. Dolayısıyla echo -nyukarıdaki.

  • belki hatırladığım kadarıyla ...

Neyse, rakamlara:

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

Bu hepsini tek seferde alacak.

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 TAMAM.

Yine de, bu oldukça rastgele bir testtir, ancak okuma girdisini, aritmetik değerlendirmeyi ve değişken genişlemeyi test eder. Belki kapsamlı değil, ama muhtemelen oraya yakın.

Teresa e Junior'dan EDIT : @mikeserv ve ben birçok test yaptık (ayrıntılar için sohbetimize bakın) ve sonuçların şöyle özetlenebileceğini gördük:

  • Hıza ihtiyacınız varsa, kesinlikle tire ile gidin , diğer tüm kabuklardan çok daha hızlı ve bash değerinden yaklaşık 4x daha hızlıdır .
  • İken busybox 'ın kabuk çok daha yavaş olabilir çizgi kendi userland araçlardan birçok çünkü bazı testlerde o kadar hızlı olabilir gibi grep, sed, sortvb yaygın olarak kullanılan GNU kadar birçok özellik bizde olmayan yarar, ancak işi bu kadar halledebilir.
  • Hız, değer verdiğiniz her şey değilse, ksh (veya ksh93 ), hız ve özellikler arasında en iyi yöntem olarak kabul edilebilir. Bu hız küçük karşılaştırmasını mksh çok daha hızlıdır, bash ve benzerleri de bazı benzersiz özelliklere sahip kayar nokta aritmetik .
  • Her ne kadar bash basitliği, istikrarı ve işlevselliği ile ünlü olsa da, testlerimizin çoğunda ve büyük bir farkla tüm kabukların en yavaşıydı.

Bu kodun bash (ve ayrıca ksh ve zsh) 'da, sadece kısa çizgi, mksh ve pdksh'de çalışmasını sağlayamıyorum. Bash 4.2.37(1)-release, Debian ve 4.2.45(2)-releasePorteus LiveCD'den (Slackware) denedim . Olmadan null=, sayıların çıkışı yerine, sürekli Return tuşuna basıyormuşum gibi çalışır , sonra SIGKILL ile bash'ı öldürmem gerekir .
Teresa e Junior,

Ve ben de denedim bash --posix, boşuna.
Teresa e Junior,

@TeresaeJunior - bu belki de mümkün - işe yarayacağına inanmıyorum zsh. ve zshkaçacak, ttyetkileşimli bir kabuk başlatacak. Ben bekliyoruz bashBen sadece çağırmak dikkatli davranıyorum ki - aynısını yapacak --posixlinki. Onlardan herhangi biri için beklediğiniz gibi yapabilirim, ancak değerinden daha fazla iş olabilir. Arayor musun bashyoksa aradın shmı?
mikeserv

@TeresaeJunior içeri Can burada ve çıkış sonrası? Neler olduğu hakkında daha iyi bir fikir edinmek istiyorum.
mikeserv

Tamamlamak için cevabımın metnini sizinkilerin altına eklemek, sonra da benimkini silmek zorunda mıyım?
Teresa e Junior,

20

Bir kıyaslama yapalım.

İle 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

İle 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

Her yineleme sadece bir kabuk başlatmak ve no-op operatörü ile bir şey yapmak - kolon ve çıkar.

Sonuç olarak , başlangıçta dasholduğundan çok daha hızlıdır bash. dashdaha küçüktür ve şunlardan daha az paylaşılan kütüphaneye bağlıdır 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)

Bu başlangıç ​​zamanı, nasıl çalışılacağı ile ilgili. Başka bir kıyaslama yapalım:

$ 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

Basit bir testle 1 = 1, dashhala çok daha hızlı bash.


Cevabınız çok takdir ediliyor, ancak gerçekten de kabuğun ne kadar hızlı çalıştığını değil, sadece kabuğun ne kadar hızlı çalıştığını ölçüyorsunuz, değil mi?
Teresa e Junior,

1
@TeresaeJunior: Evet, yalnızca başlangıç ​​zamanından söz ediyorum.
cuonglm

Ben varsayalım seq 1 100000olmalıdır seq 1 1000?
Mikel

1
Ancak dashtest durumunuz için sadece bu seq 1 1000?
Mikel

Üzgünüm, 1000başlangıç ​​ve çalışma 1000000için sabit.
cuonglm

7

Sertifikalı bir UNIX'te çeşitli mermilerin başlangıç ​​zamanlamaları: (Mac OS X 10.10.3). Döngüleri kontrol etmek için tcsh kullanmak için testi yeniden yazdım, böylece test edilen kabuk döngüleri kontrol eden değildi. Her kabuk için döngü, kabuğun çalıştırılabilir ve komut dosyalarının önbellekte olduğundan emin olmak için zamanlamadan beş kez önce yürütülür.

Gördüğünüz gibi kesin bir kazanan yok, ancak kesin bir kaybeden var. Her neyse, bash 4, bash 3'ten belirgin bir şekilde yavaştır. ve UNIX ülkesinde fiili bir standart (GNU / Linux ülkesinde değilse); POSIX kabuk işlevselliğinin bir üstkümesini sağlar (anladığım kadarıyla POSIX kabuğu ksh88'e dayanıyordu); tcsh'ye kıyasla gecikmeli olsa da, etkileşimli bir kabuk olarak bash değerine eşittir. Ve kaybeden elbette 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

Benim sonucum da ksh93 kullanmaktı. FSF tarafından onaylanan Ortak Kamu Lisansı altındadır.
Teresa e Junior

0

Burada birçok cevapta çok fazla haksız test vakası var. Test iki kabuk varsa, her biri için doğru sözdizimini kullanın. Ve bash çift braketleri, tekli braketlerden çok daha hızlı ve daha güvenilirdir, bu nedenle, çok daha az bir hız farkı vardır. Ayrıca, optimize edilmiş temeller kullanın ve ardından bu hız farkları da daha azdır. Benim sistemimde bash cehennem gibi koşuyor, ağır bashismalar kullanıyor. Ve burada tire içindeki posix eşdeğerleri daha yavaştır. Bu doğru değil, çizgi her zaman bash daha çok kat daha hızlıdır. Her ikisi de posix komut satırlarını karşılaştırmak hakikaten haksızlık. Benim görüşüme göre posix ağır modası geçmiş. Ve uyumluluk açısından, bugünlerde ilgili sistemleri bulmak gerçekten zor, bir bash kabuğu kullanmadılar.

İyi bir karşılaştırma: Her bir kabukta mümkün olan en iyi komut satırını kullanmak, belirli bir işi bitirmek için. Sadece tam olarak aynı komut satırı değil, sadece bir kabuğun burada gerçekten bir avantajı olduğunda. Bunun gibi karşılaştırmalar güvenilmezdir ve rakiplerin gerçek performansını göstermedi. Her gün yaptığım işte görüyorum ki, birçok kullanım durumunda hangi kabuk daha hızlıdır.

Örneğin, tüm değiştirilmesi aile dizede karakterleri bbash yazabilir, karakterler "${varname//a/b}"çizgi içinde böyle bir harici araç aramak zorunda iken: "$(echo "$varname" | sed 's/a/b/g')". Birkaç yüz kere tekrar etmek zorundaysanız - bashism kullanarak size 2x hız verebilir.


3
Bash'ın performans farkını nasıl kapattığını veya eşdeğer görevlerde daha hızlı olabileceğini göstermek için cevabınızı güncelleyebileceğiniz herhangi bir örneğiniz var mı? Cevabınız, bazı özel örneklerle daha güçlü olacaktır.
Eric Renouf,
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.