배쉬 5.0 해결됨

배쉬 5.0 해결됨

배쉬 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#매우 간단하며 여기에만 표시됩니다.
엄청난 지연의 원인을 찾기 위해 원래 기능이 점차 제거되었습니다.

위의 스크립트는 작동하므로 실행하고 아주 작은 작업을 수행하는 데 시간을 낭비할 수 있습니다.

대기 시간이 정확히 어디인지 찾기 위해 단순화하는 과정에서(많은 시도를 거친 후 각 테스트 기능을 거의 아무것도 없는 상태로 줄이는 것은 극단적임) 시간이 얼마나 개선되었는지 확인하기 위해 각 테스트 기능에 전달 매개변수를 제거하기로 결정했습니다. 별로.

직접 시도해 보려면 "$@"함수의 모든 항목을 제거 main1하거나 복사본을 만들고 다시 테스트(또는 둘 다 main1및 복사본 main2( 사용 main2 "$@"))하여 비교하세요. 이는 원본 게시물(OP)의 기본 구조입니다.

하지만 저는 알고 싶습니다. 쉘이 "아무 것도 하지 않는" 데 왜 그렇게 오랜 시간이 걸리는 걸까요? 예, 단지 "몇 초"만 소요됩니다. 그런데 왜 그렇습니까?

이로 인해 다른 쉘에서 테스트하게 되었고 bash에만 이 문제가 있음을 발견했습니다.
시도해 보세요 ksh ./script(위와 동일한 스크립트).

test#이는 매개변수 없이 함수를 호출하는 경우( ) 상위 함수( )의 매개변수에 의해 지연된다는 설명으로 이어집니다 . main#아래 설명은 다음과 같습니다. 아래 원본 게시물(OP)도 마찬가지입니다.

원본 게시물.

아무것도 하지 않는 함수(Bash 4.4.12(1)-릴리스에서)를 호출하는 것은 호출하는 것 보다 f1(){ :; }1000배 느립니다.:오직정의된 매개변수가 있는 경우부모함수를 호출하는 이유는 무엇입니까?

#!/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

함수에는 매개변수가 사용되지 않고, 입력이나 출력도 사용되지 않으며 f1, 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

답변1

복사한 곳:루프에 지연이 발생하는 이유는 무엇입니까?귀하의 요청에 따르면:

테스트 사례를 다음과 같이 단축할 수 있습니다.

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)

관련 정보