루프를 10억 번 실행하여 Bash와 Python의 속도를 테스트했습니다.
$ cat python.py
#!/bin/python
# python v3.5
i=0;
while i<=1000000000:
i=i+1;
쿵 코드:
$ cat bash2.sh
#!/bin/bash
# bash v4.3
i=0
while [[ $i -le 1000000000 ]]
do
let i++
done
이 time
명령을 사용하면 Python 코드는 완료하는 데 48초밖에 걸리지 않는 반면 Bash 코드는 스크립트를 종료하는 데 1시간 이상이 걸린다는 사실을 발견했습니다.
왜 그럴까요? Bash가 더 빨라질 것으로 기대합니다. 내 스크립트에 문제가 있나요? 아니면 이 스크립트를 사용하면 Bash가 정말 훨씬 느려지나요?
답변1
쉘 루프는 느리고 bash의 루프는 가장 느립니다. Shell은 루프에서 무거운 작업을 수행하는 데 적합하지 않습니다. 셸은 대량 데이터에 대해 일부 외부 최적화 프로세스를 시작하도록 설계되었습니다.
어쨌든 쉘 루프가 어떻게 비교되는지 궁금해서 몇 가지 벤치마크를 수행했습니다.
#!/bin/bash
export IT=$((10**6))
echo POSIX:
for sh in dash bash ksh zsh; do
TIMEFORMAT="%RR %UU %SS $sh"
time $sh -c 'i=0; while [ "$IT" -gt "$i" ]; do i=$((i+1)); done'
done
echo C-LIKE:
for sh in bash ksh zsh; do
TIMEFORMAT="%RR %UU %SS $sh"
time $sh -c 'for ((i=0;i<IT;i++)); do :; done'
done
G=$((10**9))
TIMEFORMAT="%RR %UU %SS 1000*C"
echo 'int main(){ int i,sum; for(i=0;i<IT;i++) sum+=i; printf("%d\n", sum); return 0; }' |
gcc -include stdio.h -O3 -x c -DIT=$G -
time ./a.out
( 세부 사항:
- CPU: Intel(R) Core(TM) i5 CPU M 430 @ 2.27GHz
- ksh: sh 버전(AT&T Research) 93u+ 2012-08-01
- bash: GNU bash, 버전 4.3.11(1)-릴리스(x86_64-pc-linux-gnu)
- zsh: zsh 5.2 (x86_64-unknown-linux-gnu)
- 대시:0.5.7-4ubuntu1
)
(축약된) 결과(반복당 시간)는 다음과 같습니다.
POSIX:
5.8 µs dash
8.5 µs ksh
14.6 µs zsh
22.6 µs bash
C-LIKE:
2.7 µs ksh
5.8 µs zsh
11.7 µs bash
C:
0.4 ns C
결과에서:
약간 더 빠른 쉘 루프를 원하는 경우 [[
구문이 있고 빠른 쉘 루프를 원한다면 고급 쉘에 있는 것이며 C와 유사한 for 루프도 있습니다. 그런 다음 C 언어 for 루프를 사용하십시오. while [
동일한 쉘의 -loop보다 약 2배 빠릅니다.
- 케시가장 빠른
for (
루프는 대략2.7마이크로초각 반복 - 스프린트가장 빠른
while [
루프는 대략5.8마이크로초각 반복
C for 루프는 십진수 3~4배 더 빠를 수 있습니다. (Torvalds가 C를 좋아한다고 들었습니다.)
최적화된 C for 루프 while [
는 bash 루프(가장 느린 쉘 루프)보다 56500배 빠르고 for (
ksh 루프(가장 빠른 쉘 루프)보다 6750배 빠릅니다.
다시 말하지만, 셸의 일반적인 모드는 외부 최적화 프로그램의 몇 가지 프로세스로 로드를 오프로드하는 것이므로 셸이 느린지는 중요하지 않습니다.
이 패턴을 사용하면 셸에서 Python 스크립트보다 성능이 더 좋은 스크립트를 더 쉽게 작성할 수 있는 경우가 많습니다(마지막으로 확인했을 때 Python에서 프로세스 파이프라인을 만드는 것은 꽤 서투른 일이었습니다).
고려해야 할 또 다른 사항은 시작 시간입니다.
time python3 -c ' '
내 PC에서는 30~40ms가 걸리고, 쉘은 약 3ms가 걸립니다. 많은 스크립트를 시작하면 이 시간이 빠르게 합산되며, Python을 시작하는 데 걸리는 추가 27-37밀리초 안에 많은 작업을 수행할 수 있습니다. 이 시간 동안 작은 스크립트를 여러 번 완료할 수 있습니다.
(NodeJ는 시작하는 데 약 100밀리초밖에 걸리지 않기 때문에 아마도 이 부서에서 최악의 스크립팅 런타임일 것입니다. (그리고 일단 시작하더라도 스크립팅 언어에서 더 나은 성능을 찾기가 어려울 것입니다.)
답변2
이것은 bash의 알려진 버그입니다. 매뉴얼 페이지를 참조하여 "BUGS"를 검색하십시오.
BUGS It's too big and too slow.
;)
쉘 스크립팅과 다른 프로그래밍 언어 간의 개념적 차이점에 대한 훌륭한 입문서를 보려면 다음을 읽어볼 것을 적극 권장합니다.
가장 관련성이 높은 발췌:
쉘은 고급 언어입니다. 심지어 언어가 아니라고 말할 수도 있습니다. 모든 명령줄 해석기보다 우선합니다. 작업은 사용자가 실행하는 명령에 의해 수행되며 셸은 명령을 조정합니다.
...
IOW, 쉘에서는 특히 텍스트를 처리할 때 수천 개의 도구를 순서대로 실행하고 각 도구가 시작되고 실행되고 정리될 때까지 기다리는 대신 가능한 한 적은 수의 유틸리티를 호출하여 작업에 적합하도록 합니다. 다음 도구를 다시 실행하세요.
...
앞서 언급했듯이 명령을 실행하는 데는 비용이 듭니다. 명령어가 내장되어 있지 않으면 비용이 엄청나지만, 내장되어 있어도 비용이 엄청납니다.
쉘은 이러한 방식으로 작동하도록 설계되지 않았으며 고성능 프로그래밍 언어라고 주장하지도 않습니다. 그들은 아닙니다. 그들은 단지 명령줄 해석기일 뿐입니다. 따라서 이와 관련하여 최적화가 거의 이루어지지 않았습니다.
쉘 스크립트에서는 큰 루프를 사용하지 마십시오.
답변3
몇 가지 테스트를 수행하고 내 시스템에서 다음 코드를 실행했습니다. 그 중 어느 것도 경쟁력을 갖추는 데 필요한 정도의 속도 향상을 달성하지 못했지만 더 빠르게 만들 수 있습니다.
테스트 1: 18.233초
#!/bin/bash
i=0
while [[ $i -le 4000000 ]]
do
let i++
done
테스트 2: 20.45초
#!/bin/bash
i=0
while [[ $i -le 4000000 ]]
do
i=$(($i+1))
done
테스트 3: 17.64초
#!/bin/bash
i=0
while [[ $i -le 4000000 ]]; do let i++; done
테스트 4: 26.69초
#!/bin/bash
i=0
while [ $i -le 4000000 ]; do let i++; done
테스트 5: 12.79초
#!/bin/bash
export LC_ALL=C
for ((i=0; i != 4000000; i++)) {
:
}
마지막 항목에서 중요한 부분은 LC_ALL=C 내보내기입니다. 나는 이것을 사용하면 많은 bash 작업, 특히 정규 표현식 기능이 훨씬 더 빨라진다는 것을 발견했습니다. 또한 {} 및 :를 무작동으로 사용하는 문서화되지 않은 구문도 보여줍니다.
답변4
답변: Bash는 Python보다 훨씬 느립니다.
블로그 게시물에 작은 예가 있습니다.다국어 성능.