Là dấu gạch ngang hoặc một số shell khác khác nhanh hơn so với bash?


57

Tôi luôn nghĩ rằng lợi ích duy nhất của việc sử dụng dấu gạch ngang thay vì bash là dấu gạch ngang nhỏ hơn và do đó, nhiều trường hợp dấu gạch ngang sẽ bắt đầu nhanh hơn khi khởi động.

Nhưng tôi đã thực hiện một số nghiên cứu và thấy một số người chuyển tất cả các tập lệnh của họ sang dấu gạch ngang với hy vọng họ sẽ chạy nhanh hơn và tôi cũng tìm thấy điều này trong bài viết DashAsBinSh trong Ubuntu Wiki:

Lý do chính để chuyển đổi vỏ mặc định là hiệu quả . bash là một vỏ đầy đủ tính năng tuyệt vời thích hợp để sử dụng tương tác; Thật vậy, nó vẫn là shell đăng nhập mặc định. Tuy nhiên, nó khá lớn và chậm để khởi động và vận hành bằng cách so sánh với dấu gạch ngang.

Ngày nay tôi đã sử dụng rất nhiều tập lệnh bash cho nhiều thứ trên hệ thống của mình và vấn đề của tôi là tôi có một tập lệnh cụ thể mà tôi đang chạy liên tục 24/7, sinh ra khoảng 200 trẻ em, cùng nhau làm nóng máy tính của tôi 10 ° C nhiều hơn so với sử dụng bình thường.

Đó là một tập lệnh khá lớn với nhiều lỗi bashism, vì vậy việc chuyển chúng sang POSIX hoặc một số shell khác sẽ rất tốn thời gian (và POSIX không thực sự quan trọng đối với việc sử dụng cá nhân), nhưng sẽ rất đáng nếu tôi có thể giảm bớt một số điều này Sử dụng CPU. Tôi biết cũng có những thứ khác để xem xét, như gọi một nhị phân bên ngoài như sedcho một bashism đơn giản như ${foo/bar}, hoặc grepthay vì =~.

TL; DR thực sự bash chậm hơn để khởi động và hoạt động so với dấu gạch ngang? Có shell Unix nào khác hiệu quả hơn bash không?


12
Nếu bạn định chuyển nó cho hiệu suất, bạn có nghĩ rằng nó sẽ được thực hiện tốt hơn trong một số ngôn ngữ khác (perl, python, ruby) hoàn toàn không? Chúng thường hiệu quả hơn nhiều, tôi nghĩ, mặc dù nó sẽ phụ thuộc vào bản chất chính xác của nhiệm vụ.
goldilocks

Điểm nhỏ: [cũng nên là một nội dung.
Mikel

2
Hãy nhớ rằng không giống như bạn lo lắng về việc sử dụng bộ nhớ, sự khác biệt chủ yếu thể hiện nếu bạn thực hiện tính toán trong trình bao thay vì trong các chương trình bên ngoài (nghĩa là bạn đang sử dụng trình bao sai cách!); ví dụ: trên máy tính của tôi, tập lệnh sử dụng vòng lặp while để đếm đến một triệu (không làm gì khác) nhanh hơn ~ 2 lần trong mksh / zsh và> gấp 2 lần trong dấu gạch ngang, nhưng trong một tập lệnh thực sự tôi sẽ giảm tải hết mức có thể cho người khác Chương trình.
truyền thuyết

3
bashđã từng rất chậm. Nó đã đạt được rất nhiều tiến bộ gần đây, nhưng đối với hầu hết mọi thứ, nó vẫn chậm hơn so với hầu hết các vỏ khác.
Stéphane Chazelas

1
Đừng sử dụng bashism đơn giản . [ "$foo" != "${foo#*bar}" ]xử lý điều grep của bạn. Và sedđiều : while [ "$foo" != "${foo#*bar}" ]; do s=$s${foo%%bar*} foo=${foo#*bar} ; done ; foo=$s$foo. Bạn có thể đặt một trong hai điều vào một chức năng.
mikeerv

Câu trả lời:


39

CHIA SẺ SEQ:

Có lẽ một phương tiện hữu ích để đánh dấu hiệu suất của vỏ là thực hiện nhiều đánh giá rất nhỏ, đơn giản lặp đi lặp lại. Điều quan trọng, tôi nghĩ, không chỉ là lặp, mà còn lặp qua đầu vào , bởi vì một vỏ cần phải đọc <&0.

Tôi nghĩ rằng điều này sẽ bổ sung cho các bài kiểm tra @cuonglm đã được đăng bởi vì nó cho thấy hiệu suất của một quá trình shell duy nhất được gọi, trái ngược với thử nghiệm của nó cho thấy quá trình shell tải nhanh như thế nào khi được gọi. Theo cách này, giữa chúng tôi, chúng tôi bao gồm cả hai mặt của đồng tiền.

Đây là một chức năng để tạo điều kiện cho bản 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

Nó có thể tăng một biến một lần cho mỗi lần đọc dòng mới hoặc, như một sự tối ưu hóa nhẹ, nếu có thể, nó sẽ tăng 50 lần cho mỗi lần đọc dòng mới. Mỗi khi biến được tăng lên, nó được in ra stdout. Nó hành xử rất giống như một loại seqchéo nl.

Và chỉ để làm cho nó rõ ràng những gì nó làm - đây là một số set -x;đầu ra bị cắt sau khi chèn nó ngay trước đó timetrong chức năng trên:

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

Vì vậy, mỗi vỏ được gọi đầu tiên như:

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

... Để tạo đầu vào mà nó sẽ cần lặp lại khi nó đọc vào 3<<\SCRIPT- hoặc khi catnào, dù thế nào đi nữa. Và ở phía bên kia, |pipenó lại tự gọi mình như sau:

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

Vì vậy, ngoài cuộc gọi ban đầu đến env (vì catthực sự được gọi trong dòng trước) ; không có quá trình nào khác được gọi từ khi nó được gọi cho đến khi nó thoát. Ít nhất, tôi hy vọng đó là sự thật.

Trước những con số ...

Tôi nên làm một số lưu ý về tính di động.

  • poshkhông thích $((n=n+1))và khăng khăng$((n=$n+1))

  • mkshkhông có printfnội dung trong hầu hết các trường hợp. Các thử nghiệm trước đó đã khiến nó bị tụt lại rất nhiều - nó được gọi /usr/bin/printfcho mỗi lần chạy. Do đó, echo -nở trên.

  • có lẽ nhiều hơn khi tôi nhớ nó ...

Dù sao, với những con số:

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

Điều đó sẽ giúp họ có tất cả trong một lần ...

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

KIẾN TRÚC = CÓ THỂ OK?

Tuy nhiên, đây là một thử nghiệm khá độc đoán, nhưng nó kiểm tra đọc đầu vào, đánh giá số học và mở rộng biến. Có thể không toàn diện, nhưng có thể gần đó.

EDIT của Teresa e Junior : @mikeerv và tôi đã thực hiện nhiều bài kiểm tra khác (xem phần trò chuyện của chúng tôi để biết chi tiết) và chúng tôi thấy kết quả có thể được tóm tắt như thế này:

  • Nếu bạn cần tốc độ, hãy đi chắc chắn với dấu gạch ngang , nó nhanh hơn nhiều so với bất kỳ vỏ nào khác và nhanh hơn khoảng 4 lần so với bash .
  • Trong khi busybox vỏ 's có thể chậm hơn nhiều so với gạch ngang , trong một số xét nghiệm đó có thể là nhanh hơn, bởi vì nó có rất nhiều tiện ích Userland riêng của mình, giống như grep, sed, sort, vv, mà không có nhiều tính năng như GNU thường được sử dụng tiện ích, nhưng có thể hoàn thành công việc nhiều như vậy.
  • Nếu tốc độ không phải là tất cả những gì bạn quan tâm, ksh (hoặc ksh93 ) có thể được coi là sự thỏa hiệp tốt nhất giữa tốc độ và tính năng. Tốc độ của nó so với mksh nhỏ hơn , nhanh hơn bash và nó cũng có một số tính năng độc đáo, như số học dấu phẩy động .
  • Mặc dù bash nổi tiếng vì sự đơn giản, ổn định và chức năng của nó, nó là loại chậm nhất trong tất cả các loại đạn trong phần lớn các thử nghiệm của chúng tôi và bởi một biên độ lớn.

Tôi không thể lấy mã này để làm việc trong bash (và cả ksh và zsh), chỉ trong dash, mksh và pdksh. Bash Tôi đã thử 4.2.37(1)-releasetừ Debian và 4.2.45(2)-releasetừ Porteus LiveCD (Slackware). Không null=, thay vì xuất ra các số, nó hoạt động như thể tôi nhấn Return liên tục, sau đó tôi phải giết bash bằng SIGKILL .
Teresa e Junior

Và tôi cũng đã cố gắng bash --posix, vô ích.
Teresa e Junior

@TeresaeJunior - điều đó có thể có thể - mặc dù tôi không tin rằng nó sẽ hoạt động với zsh. zshsẽ chiếm quyền điều khiển ttyvà tốt, nó sẽ khởi chạy một lớp vỏ tương tác. Tôi hy vọng bashsẽ làm như vậy - đó là lý do tại sao tôi cẩn thận chỉ gọi --posixliên kết của nó . Tôi có thể làm cho nó làm như bạn mong đợi đối với hầu hết mọi người trong số họ, nhưng nó có thể là công việc nhiều hơn giá trị của nó. Bạn đang gọi bashhay bạn đang gọi sh?
mikeerv

@TeresaeJunior Bạn có thể vào đây và đăng đầu ra không? Tôi chỉ muốn có được một ý tưởng tốt hơn về những gì đang xảy ra.
mikeerv

Tôi không nên thêm văn bản câu trả lời của tôi vào dưới cùng của bạn, để bổ sung cho nó, và sau đó xóa của tôi?
Teresa e Junior

20

Hãy làm một điểm chuẩn.

Với 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

Với 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

Mỗi lần lặp chỉ bắt đầu một shell và không làm gì với toán tử no-op - dấu hai chấm , sau đó thoát.

Như kết quả cho thấy, dashcực kỳ nhanh hơn so với bashlúc khởi động. dashnhỏ hơn và phụ thuộc vào thư viện chia sẻ ít hơn 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)

Đây là về thời gian khởi động, làm thế nào về hoạt động. Hãy làm một điểm chuẩn khác:

$ 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

Với thử nghiệm đơn giản 1 = 1, dashvẫn nhanh hơn nhiều bash.


Câu trả lời của bạn rất được đánh giá cao, nhưng có vẻ như bạn chỉ đang đo tốc độ khởi động của vỏ, chứ không thực sự là nó hoạt động nhanh như thế nào, phải không?
Teresa e Junior

1
@TeresaeJunior: Vâng, tôi chỉ đề cập đến thời gian khởi động.
cuonglm

Tôi cho là seq 1 100000nên seq 1 1000?
Mikel

1
Nhưng đối với dashtrường hợp thử nghiệm của bạn, nó chỉ seq 1 1000?
Mikel

Ồ, xin lỗi, nó là 1000để khởi động và 1000000vận hành, đã sửa.
cuonglm

7

Đây là một số thời gian khởi động của các shell khác nhau trong một UNIX được chứng nhận (Mac OS X 10.10.3). Tôi viết lại bài kiểm tra để sử dụng tcsh để điều khiển các vòng lặp để lớp vỏ được kiểm tra không phải là lớp kiểm soát các vòng lặp. Đối với mỗi shell, vòng lặp được thực thi năm lần trước thời gian, để đảm bảo rằng shell thực thi và các tập lệnh nằm trong bộ đệm.

Như bạn có thể thấy, không có người chiến thắng rõ ràng, nhưng có một người thua cuộc dứt khoát. Dù sao đi nữa, bash 4 chậm hơn rõ rệt so với bash 3. Dash hoạt động tốt, nhưng với điều kiện ksh93 hiện là nguồn mở, không có lý do thực sự nào để không sử dụng nó cho mọi thứ (xin lỗi nếu tôi hiểu nhầm bất kỳ bản quyền nào) và một tiêu chuẩn thực tế trong vùng đất UNIX (nếu không có trong vùng đất GNU / Linux); nó cung cấp một siêu bộ chức năng vỏ POSIX (theo như tôi hiểu, vỏ POSIX dựa trên ksh88); nó tương đương với bash như một vỏ tương tác, mặc dù tụt lại so với tcsh. Và người thua tất nhiên là 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

Kết luận của tôi là sử dụng ksh93 quá. Nó thuộc Giấy phép Công cộng Chung, đã được FSF phê duyệt.
Teresa e Junior

0

Có quá nhiều trường hợp kiểm tra không công bằng trong nhiều câu trả lời ở đây. Nếu kiểm tra hai shell thì sử dụng cú pháp đúng cho mỗi shell. Và trong bash doublebrackets nhanh hơn và đáng tin cậy hơn nhiều so với doublebrackets, do đó có sự khác biệt về tốc độ ít hơn nhiều. Cũng sử dụng bashism được tối ưu hóa và sau đó những khác biệt về tốc độ cũng ít hơn. Trên hệ thống của tôi bash chạy như địa ngục, với việc sử dụng nhiều bashism. Và tương đương posix trong dash là chậm hơn ở đây. Điều này không chính xác rằng dấu gạch ngang luôn nhanh hơn nhiều lần so với bash. Thực sự thật không công bằng khi so sánh các dòng lệnh posix trong cả hai, người luôn luôn có thể là người nhanh nhất. Theo quan điểm của tôi posix là lỗi thời nặng nề. Và về khả năng tương thích, thực sự rất khó để tìm thấy các hệ thống có liên quan ngày nay, họ đã không sử dụng bash shell.

Một so sánh tốt là: sử dụng dòng lệnh tốt nhất có thể trong mỗi shell, để hoàn thành một công việc cụ thể. Không chỉ chính xác cùng một dòng lệnh, khi chỉ có một shell thực sự có lợi thế ở đây. So sánh như thế này là không đáng tin cậy và không cho thấy hiệu suất thực sự của các đối thủ cạnh tranh. Tôi thấy trong công việc hàng ngày của mình, vỏ nào nhanh hơn trong nhiều trường hợp sử dụng.

Ví dụ: để thay thế tất cả các aký tự trong chuỗi bằng các bký tự, trong bash bạn có thể viết "${varname//a/b}"trong khi ở dấu gạch ngang, bạn phải gọi công cụ bên ngoài như thế này : "$(echo "$varname" | sed 's/a/b/g')". Nếu bạn phải lặp lại vài trăm lần - sử dụng bashism có thể giúp bạn tăng tốc gấp 2 lần.


3
Bạn có bất kỳ ví dụ nào bạn có thể cập nhật câu trả lời của mình để cho thấy cách bash có thể thu hẹp khoảng cách hiệu suất hoặc thậm chí nhanh hơn trong các tác vụ tương đương không? Câu trả lời của bạn sẽ mạnh mẽ hơn rất nhiều với một số ví dụ cụ thể.
Eric Renouf
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.