배쉬가 인수 목록을 사용하는 데 성능 문제가 있습니까?


11

bash 5.0에서 해결되었습니다.

배경

배경 (그리고 이해 (그리고이 질문에 대한 다운 보트를 피하려고 노력하는 것)을 피하려고 노력함) 나는이 문제에 도달 한 경로를 설명 할 것입니다 (두 달 후에 기억할 수있는 최선).

유니 코드 문자 목록에 대해 일부 쉘 테스트를 수행한다고 가정하십시오.

printf "$(printf '\\U%x ' {33..200})"

그리고 100 만 개가 넘는 유니 코드 문자가 있으며 그 중 20.000 개를 테스트하는 것은 그리 많지 않은 것 같습니다.
또한 문자를 위치 인수로 설정했다고 가정하십시오.

set -- $(printf "$(printf '\\U%x ' {33..20000})")

문자를 각 함수에 전달하여 다른 방식으로 처리 할 수 ​​있습니다. 따라서 함수의 형식은 test1 "$@"비슷 해야합니다 . 이제 나는 이것이 bash에 얼마나 나쁜 생각인지 알고 있습니다.

이제 각 솔루션에 시간이 필요하다고 가정하고 (n = 1000) 더 나은 것을 찾기 위해 이러한 조건에서 다음과 유사한 구조로 끝날 것입니다.

#!/bin/bash --
TIMEFORMAT='real: %R'  # '%R %U %S'

set -- $(printf "$(printf '\\U%x ' {33..20000})")
n=1000

test1(){ echo "$1"; } >/dev/null
test2(){ echo "$#"; } >/dev/null
test3(){ :; }

main1(){ time for i in $(seq $n); do test1 "$@"; done
         time for i in $(seq $n); do test2 "$@"; done
         time for i in $(seq $n); do test3 "$@"; done
       }

main1 "$@"

test#여기에 제시된 기능 은 매우 간단합니다.
원본은 점차 지연되어 어디에서 큰 지연이 발생했는지 찾아 냈습니다.

위의 스크립트가 작동하면 실행하고 약간의 시간을 낭비 할 수 있습니다.

지연이 발생한 위치를 정확하게 찾기 위해 단순화하는 과정에서 (그리고 많은 시험 후 각 테스트 기능을 거의 아무것도 줄이지 않는 것은 극단적입니다) 나는 시간이 얼마나 많이 개선되었는지 알아 내기 위해 각 테스트 기능에 인수를 전달하지 않기로 결정했습니다. 6 배는 많지 않습니다.

자신을 시도하려면 제거 할 모든 "$@"기능에 main1(또는 복사 할) 및 다시 테스트 (또는 둘 다 main1와 복사 main2(와는 main2 "$@")) 비교를. 아래는 원래 게시물 (OP)에서 기본 구조입니다.

그러나 나는 궁금해했다 : 왜 껍질이 "아무것도하지 않는"시간이 오래 걸립니까? 예, "몇 초"이지만 여전히 왜?

이것은 다른 쉘에서 테스트하여 배쉬에만이 문제가 있음을 발견했습니다. (위와 동일한 스크립트)를
사용해보십시오 ksh ./script.

이 설명으로 이어집니다. test#인수없이 함수 ( )를 호출 하면 부모 ( main#) 의 인수에 의해 지연됩니다 . 이것은 다음의 설명이며 아래의 원본 게시물 (OP)입니다.

원본 게시물.

아무것도하지 않는 (배쉬 4.4.12 (1) -release을에) 함수를 호출 f1(){ :; }천 시간이되어보다 느린 :하지만 단지 에 정의 인수가있는 경우 부모 함수를 호출은, 왜?

#!/bin/bash
TIMEFORMAT='real: %R'

f1   () { :; }

f2   () {
   echo "                     args = $#";
   printf '1 function no   args yes '; time for ((i=1;i<$n;i++)); do  :   ; done 
   printf '2 function yes  args yes '; time for ((i=1;i<$n;i++)); do  f1  ; done
   set --
   printf '3 function yes  args no  '; time for ((i=1;i<$n;i++)); do  f1  ; done
   echo
        }

main1() { set -- $(seq $m)
          f2  ""
          f2 "$@"
        }

n=1000; m=20000; main1

결과 test1:

                     args = 1
1 function no   args yes real:  0.013
2 function yes  args yes real:  0.024
3 function yes  args no  real:  0.020

                     args = 20000
1 function no   args yes real:  0.010
2 function yes  args yes real: 20.326
3 function yes  args no  real:  0.019

function에 사용 된 인수 나 입력 또는 출력이 없으며 f11,000 (1000) 인수의 지연이 예상치 못한 것입니다. 1


테스트를 여러 쉘로 확장하면 결과가 일관되며 대부분의 쉘에는 문제가 없으며 지연이 발생하지 않습니다 (동일한 n 및 m이 사용됨).

test2(){
          for sh in dash mksh ksh zsh bash b50sh
      do
          echo "$sh" >&2
#         \time -f '\t%E' seq "$m" >/dev/null
#         \time -f '\t%E' "$sh" -c 'set -- $(seq '"$m"'); for i do :; done'
          \time -f '\t%E' "$sh" -c 'f(){ :;}; while [ "$((i+=1))" -lt '"$n"' ]; do : ; done;' $(seq $m)
          \time -f '\t%E' "$sh" -c 'f(){ :;}; while [ "$((i+=1))" -lt '"$n"' ]; do f ; done;' $(seq $m)
      done
}

test2

결과 :

dash
        0:00.01
        0:00.01
mksh
        0:00.01
        0:00.02
ksh
        0:00.01
        0:00.02
zsh
        0:00.02
        0:00.04
bash
        0:10.71
        0:30.03
b55sh             # --without-bash-malloc
        0:00.04
        0:17.11
b56sh             # RELSTATUS=release
        0:00.03
        0:15.47
b50sh             # Debug enabled (RELSTATUS=alpha)
        0:04.62
        xxxxxxx    More than a day ......

seq인수 목록을 처리하거나 처리하는 것이 지연의 원인이 아닌지 확인하려면 다른 두 테스트의 주석 처리를 제거하십시오 .

1 인수로 결과를 전달하면 실행 시간이 증가하는 것으로 알려져있다. 감사합니다@ slm


3
메타 효과로 저장됩니다. unix.meta.stackexchange.com/q/5021/3562
Joshua

답변:


9

복사 : 왜 루프 지연이 발생합니까? 귀하의 요청에 따라 :

테스트 케이스를 단축하여 다음을 수행 할 수 있습니다.

time bash -c 'f(){ :;};for i do f; done' {0..10000}

$@트리거하는 것처럼 보이는 동안 큰 함수를 호출하고 있습니다 .

내 생각에 시간은 $@스택에 저장 하고 나중에 복원하는 데 소비 됩니다. 아마도 bash모든 값 또는 같은 것을 복제하여 매우 비효율적으로 그것을 않습니다. 시간은 o (n²) 인 것 같습니다.

다른 쉘에서 다음과 같은 시간을 얻습니다.

time zsh -c 'f(){ :;};for i do f "$@"; done' {0..10000}

당신이 함수 인수 목록을 통과 할 곳이며,이 시간은 쉘은 필요 (값을 복사 할 bash하나 느린로 5 회를 끝낸다을).

(처음에는 bash 5 (현재 알파)에서는 더 나쁘다고 생각했지만 @egmont가 지적한대로 개발 버전에서 malloc 디버깅을 사용하도록 설정했습니다. 또한 bash자신의 빌드를 예를 들어 Ubuntu는 --without-bash-malloc)를 사용 합니다.


디버깅은 어떻게 제거됩니까?
Isaac Isaac

@isaac, 스크립트 에서 로 변경 RELSTATUS=alpha하여 수행 RELSTATUS=release했습니다 configure.
Stéphane Chazelas

모두에 대한 테스트 결과를 추가 --without-bash-malloc하고 RELSTATUS=release질문 결과. 그래도 f 호출에 문제가 있습니다.
Isaac Isaac

@Isaac, 예, 방금 bash5에서 더 나쁘다는 말을 잘못했다고 말했습니다. 나쁘지 않습니다. 나쁘지 않습니다.
Stéphane Chazelas

아닙니다 . 나쁘지 않습니다 . Bash5는 전화 문제를 해결하고 전화 :를 약간 향상시킵니다 f. 질문에서 test2 타이밍을보십시오.
Isaac Isaac
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.