대시 나 다른 쉘이 bash보다 “빠른”가요? 했으며 우분투 위키의 DashAsBinSh

나는 항상 bash 대신 dash를 사용하는 유일한 이점은 dash가 더 작기 때문에 부팅시 대시의 많은 인스턴스가 더 빨리 시작된다는 것입니다.

그러나 나는 약간의 연구를 해왔고 어떤 사람들은 더 빨리 달릴 것이라는 희망으로 모든 스크립트를 마이그레이션하는 것을 발견 했으며 우분투 위키의 DashAsBinSh 기사에서도 이것을 발견했습니다 .

기본 쉘을 전환하는 주요 이유는 효율성 이었습니다 . bash는 대화식 사용에 적합한 뛰어난 기능을 갖춘 뛰어난 쉘입니다. 실제로 여전히 기본 로그인 쉘입니다. 그러나 대시와 비교 하여 시작 하고 작동 하는 것은 다소 크고 느립니다 .

요즘 나는 시스템에서 많은 것들을 위해 많은 bash 스크립트를 사용하고 있으며, 내 문제는 24/7 지속적으로 실행되는 특정 스크립트를 가지고 있으며 약 200 명의 어린이가 생성되어 컴퓨터를 10 °로 가열합니다. 일반적인 사용법보다 C가 더 많습니다.

많은 bashism을 가진 다소 큰 스크립트이므로 POSIX 또는 다른 쉘로 이식하는 데 시간이 많이 걸리고 POSIX는 실제로 개인적인 용도로는 중요하지 않습니다. 그러나이 중 일부를 줄일 수 있다면 가치가 있습니다. CPU 사용량. 내가 좋아하는 외부 바이너리 호출처럼, 고려해야 할 다른 일들도 알고 sed같은 간단한 bashism에 대한 ${foo/bar}, 또는 grep대신에가 =~.

TL; DR 은 대시와 비교 하여 시작 하고 작동하는 데 실제로 bash가 더 느립니까? bash보다 더 효율적인 다른 유닉스 쉘이 있습니까?



답변

쉘 시퀀스 :

아마도 쉘의 성능을 벤치마킹하는 유용한 방법은 매우 작고 간단한 평가를 반복적으로 수행하는 것입니다. 쉘은 읽어야하기 때문에 루프하는 것이 아니라 입력 을 반복하는 것이 중요하다고 생각 합니다 <&0.

나는 @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

개행 읽기마다 변수를 한 번 증가 시키거나, 약간의 최적화로 가능하면 개행 읽기마다 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'

따라서 각 쉘은 먼저 다음과 같이 호출됩니다.

 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))

  • mkshprintf대부분의 경우 내장 기능 이 없습니다 . 초기 테스트에서는 많은 지연이있었습니다 /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 그리고 다른 많은 테스트를 수행했으며 (자세한 내용 은 채팅 참조) 결과는 다음과 같이 요약 될 수 있습니다.

  • 속도가 필요하다면 dash 로 확실히 가십시오. 다른 쉘보다 훨씬 빠르며 bash 보다 약 4 배 빠릅니다 .
  • 하지만 비지 박스 의 쉘보다 훨씬 속도가 느려질 수 있습니다 대시 는 자신의 유저 랜드 유틸리티의 대부분을 가지고 있기 때문에, 몇 가지 테스트에서는 빠를 수와 같은 grep, sed, sort, 등, 일반적으로 사용되는 GNU만큼 많은 기능을 가지고 있지 않는 유틸리티, 그러나 작업을 많이 얻을 수 있습니다.
  • 속도가 관심있는 모든 것이 아닌 경우 ksh (또는 ksh93 )는 속도와 기능 사이의 최상의 타협으로 간주 될 수 있습니다. 속도는 작은 mksh 와 비교할 때 bash 보다 빠르며 부동 소수점 산술 과 같은 고유 한 기능도 있습니다 .
  • bash 는 단순성, 안정성 및 기능성으로 유명 하지만 대부분의 테스트에서 모든 쉘 중 가장 느리고 마진이 큽니다.

답변

벤치 마크를 해보자.

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 operator- 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 번 실행되어 쉘 실행 파일과 스크립트가 캐시에 있는지 확인합니다.

보시다시피, 확실한 승자는 없지만 확실한 패자가 있습니다. 어쨌든 bash 4는 bash 3보다 분명히 느리다. 대시는 잘 수행하지만 ksh93이 이제 오픈 소스이기 때문에 모든 것을 위해 그것을 사용하지 않을 이유가 없습니다 (라이센스를 오해하는 경우 사과) : ksh93은 빠르고 견고합니다. 그리고 UNIX-land (GNU / Linux-land가 아닌 경우)의 사실상의 표준; 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


답변

여기에 많은 답변에 불공평 한 테스트 사례가 너무 많습니다. 두 개의 쉘을 테스트하는 경우 각각에 대해 올바른 구문을 사용하십시오. 그리고 bash에서 더블 브라켓은 싱글 브라켓보다 훨씬 빠르고 안정적이므로 속도 차이가 훨씬 적습니다. 또한 최적화 된 bashism을 사용하면 이러한 속도 차이도 줄어 듭니다. 내 시스템에서 bash는 bashism을 많이 사용하여 지옥처럼 실행됩니다. 그리고 대시의 posix 등가가 여기에서 느려집니다. 대시가 항상 bash보다 여러 배 빠르다는 것은 올바르지 않습니다. 실제로 누가 대시가 가장 빠를 수 있는지에 대해 posix 명령 행을 비교하는 것은 상당히 불공평합니다. 내 견해로는 posix는 구식입니다. 호환성 측면에서 요즘 관련 시스템을 찾기가 어렵고 bash 쉘을 사용하지 않았습니다.

각 쉘에서 최상의 명령 행을 사용하여 특정 작업을 완료하는 것이 좋은 비교입니다. 하나의 쉘만이 실제로 이점을 가질 때 정확히 동일한 명령 행일뿐만 아니라. 이와 같은 비교는 신뢰할 수 없으며 경쟁사의 실제 성능을 보여주지 못했습니다. 나는 매일 사용하는 작업에서 많은 사용 사례에서 어떤 쉘이 더 빠르다는 것을 알았습니다.

예를 들어 a문자열의 모든 문자를 문자 로 바꾸려면 bbash "${varname//a/b}"에서 대시로 쓰는 동안 다음과 같이 외부 도구를 호출해야합니다 "$(echo "$varname" | sed 's/a/b/g')". 수백 번 반복 해야하는 경우 bashism을 사용하면 속도가 2 배 빨라질 수 있습니다.


답변