배쉬 스크립트; 처리 속도 최적화


10

Bash 스크립트를 최적화하기위한 일반적인 지침이 있는지 궁금합니다.

  • 예를 들어, 명령 행보다 루프를 작성 하는 것이 더 편리 하지만 시스템 을 처리 하는 것이 더 빠릅 니까? 예:

    for i in a b c; do echo $i; done
    
    echo a
    echo b
    echo c
  • 때때로 사람들은 같은 문제에 대해 다른 해결책을 제시합니다. 예를 들어, sed, cut, awk, 및 echo모든 문자열에서 숫자를 제거 할 수 있습니다. 숫자 코드가 적을수록 더 빨리 사용할 수 있는지 궁금합니다.

    1. 같은 명령, 예.

      STRING=abc.def
      echo ${STRING} | sed 's/.def//g'
      echo ${STRING} | sed '$s/....$//'
    2. 다른 명령, 예 :

      STRING=abc.def
      echo ${STRING} | cut -d . -f 1
      echo ${STRING} | sed 's/.def//g'

2
대부분의 경우 속도보다 가독성을 선호합니다.
Bernhard

1
a) 예, for 루프를 사용하는 데 오버 헤드가 있지만 중요하지 않습니다. b) 외부 명령을 최소화하고 bash 내에서 항목을 바꾸십시오. 그리고 쉘 스크립트를 최적화해야한다면 무언가 잘못하고있는 것이므로 프로파일 러를 더 잘 지원하는 언어를 사용하는 것을 고려해야합니다.
Ulrich Dangel

정규식은 일반적으로 다른 어떤 선택보다 성능면에서 효율적이지 않으므로 한 가지 (예 : 잘라 내기)없이 무언가를 수행 할 수있는 방법을 사용하는 경우이를 사용하십시오. 물론, 읽기 능력이 정규식으로 제한되는 경우 가독성에 영향을 줄 수 있습니다 .)
goldilocks

답변:


8

쉘은 코드를 재구성하지 않으며, 한 줄씩 하나씩 해석됩니다 (명령 해석기에는 다른 의미가 없습니다). 쉘이 소비 한 많은 시간은 호출 된 프로그램을 어휘 분석 / 파싱 / 시작하는 데 사용됩니다.

간단한 작업 (질문 끝의 예제에서 문자열을 자르는 것과 같은)을 위해 프로그램을로드하는 시간이 약간의 속도 차이를 휩쓸 지 않으면 놀랄 것입니다.

이야기의 교훈은 실제로 더 많은 속도가 필요한 경우 Perl 또는 Python과 같은 (반) 컴파일 된 언어를 사용하는 것이 좋습니다.이 언어는 시작하기가 더 빨라서 직접 언급 된 많은 작업을 작성할 수 있습니다 외부 프로그램을 호출 할 필요가 없으며 외부 프로그램을 호출하거나 많은 작업을 수행하기 위해 최적화 된 C (또는 기타) 모듈을 호출 할 수 있습니다. 이것이 Fedora에서 "시스템 관리 설탕"(GUI, 본질적으로)이 파이썬으로 작성된 이유입니다. 너무 많은 노력없이 멋진 GUI를 추가 할 수 있습니다. 속도가 충분하지 않으면 C ++ 또는 C를 사용하십시오.

그러나 성능 향상이 유연성과 개발 시간을 잃을 가치가 있다는 것을 입증 할 수 없다면, 그곳에 가지 마십시오 . 쉘 스크립트는 읽기에 나쁘지 않지만 Ultrix를 설치하는 데 사용 된 일부 스크립트를 기억할 때 나는 한 번 해독하려고했습니다. 나는 너무 많은 "쉘 스크립트 최적화"가 적용되었다.


1
+1 하지만 많은 사람들이 될 가능성이 주장 이득 파이썬이나 펄 대 쉘이 아닌 손실 같은 것을 사용하여 유연성과 개발 시간이다. 필자는 쉘 스크립트가 필요하거나 많은 양의 쉘 특정 명령이 필요한 경우에만 쉘 스크립트를 사용한다고 말합니다.
goldilocks

22

최적화의 첫 번째 규칙은 최적화 하지 않는 것 입니다. 먼저 테스트하십시오. 테스트 결과 프로그램 속도가 너무 느리면 가능한 최적화를 찾으십시오.

확실한 유일한 방법은 사용 사례를 벤치마킹하는 것입니다. 일반적인 규칙이 있지만 일반적인 응용 프로그램의 일반적인 데이터 양에만 적용됩니다.

특정 상황에서 사실 일 수도 있고 아닐 수도있는 몇 가지 일반적인 규칙 :

  • 쉘 내부 처리의 경우 ATT ksh가 가장 빠릅니다. 많은 문자열 조작을 수행하는 경우 ATT ksh를 사용하십시오. 대시가 두 번째입니다. bash, pdksh 및 zsh가 뒤쳐집니다.
  • 매번 매우 짧은 작업을 수행하기 위해 셸을 자주 호출해야하는 경우 시작 시간이 짧기 때문에 대시가 이깁니다.
  • 외부 프로세스를 시작하는 데 시간이 걸리므로 루프의 파이프 라인보다 복잡한 부분이있는 파이프 라인을 하나만 갖는 것이 더 빠릅니다.
  • echo $fooecho "$foo"큰 따옴표가 없으면 $foo단어로 분할 되어 각 단어를 파일 이름 와일드 카드 패턴으로 해석 하기 때문에 보다 느립니다 . 더 중요한 것은, 분할 및 globbing 동작이 거의 필요하지 않다는 것입니다. 따라서 변수 대체 및 명령 대체는 항상 큰 따옴표로 묶어야합니다. "$foo", "$(foo)".
  • 전용 도구는 범용 도구보다 승리하는 경향이 있습니다. 예를 들어와 같은 도구를 사용 cut하거나 head에뮬레이션 할 수 sed있지만 sed속도가 느리고 속도 awk가 느려집니다. 셸 문자열 처리 속도는 느리지 만 짧은 문자열의 경우 외부 프로그램 호출보다 크게 뛰어납니다.
  • Perl, Python 및 Ruby와 같은 고급 언어를 사용하면 더 빠른 알고리즘을 작성할 수 있지만 시작 시간이 상당히 높아 대량의 데이터 성능에 대해서만 가치가 있습니다.
  • Linux에서는 최소한 파이프가 임시 파일보다 속도가 빠릅니다.
  • 대부분의 셸 스크립팅은 I / O 바운드 프로세스와 관련이 있으므로 CPU 소비는 중요하지 않습니다.

쉘 스크립트에서 성능이 문제가되는 경우는 드 rare니다. 위의 목록은 순전히 표시입니다. 차이는 종종 퍼센트의 일부이므로 대부분의 경우 "느린"방법을 사용하는 것이 좋습니다.

일반적으로 쉘 스크립트의 요점은 빠른 작업을 수행하는 것입니다. 스크립트를 작성하는 데 추가 시간을 소비하는 것을 정당화하려면 최적화를 통해 많은 것을 얻어야합니다.


2
동안 python과이 ruby시작 확실히 느리다, 내 시스템에 최소한 perl으로 시작 빠른 같다 bashksh. GNU awk는 특히 utf-8 로켈에서 GNU sed보다 상당히 느리지 만 모든 awk와 모든 sed에 해당되는 것은 아닙니다. ksh93> dash> pdksh> zsh> bash가 항상 그렇게 명확한 것은 아닙니다. 어떤 껍질은 다른 것보다 낫고 승자는 항상 같은 것은 아닙니다.
Stéphane Chazelas

2
다시 "당신은에서 ... 많이 얻을 수 있습니다" 다음과 같은 경우에 "당신이" 참 userbase가 포함되어 있습니다. 널리 사용되는 Linux 패키지의 셸 스크립트를 사용하면 사용자는 종종 성급한 프로그래머가 저장하는 것보다 몇 배 더 많은 시간을 낭비하게됩니다.
agc

2

쉘 스크립트 인터프리터의 일부 성능 특성을 설명하기 위해 위의 글 로빙 예제를 살펴 보겠습니다. 프로세스가 30,000 개 파일마다 생성되는이 예제의 해석기 bashdash인터프리터를 비교하면 대시가 wc프로세스를 거의 두 배 빠르게 포크 할 수 있음을 보여줍니다.bash

bash-4.2$ time dash -c 'for i in *; do wc -l "$i"; done>/dev/null'
real    0m1.238s
user    0m0.309s
sys     0m0.815s


bash-4.2$ time bash -c 'for i in *; do wc -l "$i"; done>/dev/null'
real    0m1.422s
user    0m0.349s
sys     0m0.940s

wc프로세스 를 호출하지 않고 기본 루핑 속도를 비교하면 대시의 루핑이 거의 6 배 빠릅니다.

$ time bash -c 'for i in *; do echo "$i">/dev/null; done'
real    0m1.715s
user    0m1.459s
sys     0m0.252s



$ time dash -c 'for i in *; do echo "$i">/dev/null; done'
real    0m0.375s
user    0m0.169s
sys     0m0.203s

앞에서 설명한 것처럼 어느 쪽의 쉘에서도 루핑이 상대적으로 느리므로 확장 성을 위해 더 많은 기능적 기술을 사용하여 컴파일 된 프로세스에서 반복이 수행되도록해야합니다.

$ time find -type f -print0 | wc -l --files0-from=- | tail -n1
    30000 total
real    0m0.299s
user    0m0.072s
sys     0m0.221s

위의 방법은 지금까지 가장 효율적인 솔루션이며 쉘 스크립트에서 가능한 한 적은 작업을 수행해야하며 UNIX 시스템에서 사용할 수있는 다양한 유틸리티 세트에서 사용 가능한 기존 논리를 연결하는 데 목표를 두어야한다는 점을 잘 보여줍니다.

Pádraig Brady의 Stolen From Common shell script errors.


1
일반적인 규칙 : 파일 디스크립터 처리에도 비용이 들기 때문에 개수를 줄입니다. for i in *; do wc -l "$i">/dev/null; done더 나은 대신에 for i in *; do wc -l "$i"; done>/dev/null.
manatwork

@manatwork 그것은 또한 timecmd의 출력을 무효로합니다
Rahul Patil

@manatwork Good ... now 호출하지 않고 출력 해주세요. 출력 결과를 wc -l업데이트했습니다
Rahul Patil

이전 측정은 더 작은 디렉토리에서 이루어졌습니다. 이제 30000 개의 파일이있는 파일을 만들고 테스트를 반복했습니다. pastebin.com/pCV6QKp2
manatwork

이러한 벤치 마크에서는 각 쉘의 시작 시간이 다를 수 없습니다. 각 셸 에서 수행 된 벤치 마크 가 더 좋습니다.
agc
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.