가변 컨텐츠를 읽는 것보다 파일을 여는 것이 왜 더 빠릅니까?


36

bash스크립트 에서는 /proc/파일의 다양한 값이 필요 합니다. 지금까지 수십 줄이 파일을 직접 그 리핑했습니다.

grep -oP '^MemFree: *\K[0-9]+' /proc/meminfo

보다 효율적으로 만들기 위해 파일 내용을 변수에 저장하고 다음과 같이 정리했습니다.

a=$(</proc/meminfo)
echo "$a" | grep -oP '^MemFree: *\K[0-9]+'

파일을 여러 번 여는 대신 파일을 한 번 열고 변수 내용을 grep해야합니다. 더 빠르다고 가정했지만 실제로는 느립니다.

bash 4.4.19 $ time for i in {1..1000};do grep ^MemFree /proc/meminfo;done >/dev/null
real    0m0.803s
user    0m0.619s
sys     0m0.232s
bash 4.4.19 $ a=$(</proc/meminfo)
bash 4.4.19 $ time for i in {1..1000};do echo "$a"|grep ^MemFree; done >/dev/null
real    0m1.182s
user    0m1.425s
sys     0m0.506s

dash및에 대해서도 마찬가지입니다 zsh. 나는 /proc/파일 의 특수 상태를 이유로 의심 했지만 내용을 /proc/meminfo일반 파일에 복사 하고 결과가 동일하다는 것을 사용할 때 :

bash 4.4.19 $ cat </proc/meminfo >meminfo
bash 4.4.19 $ time for i in $(seq 1 1000);do grep ^MemFree meminfo; done >/dev/null
real    0m0.790s
user    0m0.608s
sys     0m0.227s

here 문자열을 사용하여 파이프를 저장하면 파일보다 약간 빠르지 만 여전히 빠릅니다.

bash 4.4.19 $ time for i in $(seq 1 1000);do <<<"$a" grep ^MemFree; done >/dev/null
real    0m0.977s
user    0m0.758s
sys     0m0.268s

변수에서 같은 내용을 읽는 것보다 파일을 여는 것이 왜 더 빠릅니까?


@ l0b0이 가정은 잘못된 것이 아니며, 질문은 내가 어떻게 생각해 왔는지 보여주고 대답은 이것이 왜 그런지를 설명합니다. 편집하면 제목 질문에 대한 답변이 더 이상 표시되지 않습니다. 그렇지 않은 경우에는 답변하지 않습니다.
디저트

알았어 대부분의 경우 제목이 잘못 되었기 때문에 특정 메모리 매핑 특수 파일이 아닙니다.
l0b0

@ l0b0 아니, 내가 여기 부탁 해요 무엇을한다 : "나는의 특별한 상태를 의심 /proc/이유로 파일을,하지만 난의 내용을 복사 할 때 /proc/meminfo일반 파일 및 사용에 결과가 동일한 지는"그것은이다 없는 특별한 /proc/일반 파일을 읽는 것도 빠릅니다!
디저트

답변:


47

여기서, 대한 아니라 파일을 열변수의 내용을 읽어 하지만, 더 추가 과정을 분기 여부에 대해.

grep -oP '^MemFree: *\K[0-9]+' /proc/meminfo이 실행하는 과정을 만들어 낸다 grep엽니 다 /proc/meminfo(메모리, 가상 파일이 어떤 디스크 I / O 참여가)를 읽고하지 않으며 정규 표현식과 일치합니다.

그중 가장 비싼 부분은 프로세스를 포크하고 grep 유틸리티와 라이브러리 종속성을로드하고 동적 연결을 수행하고 로케일 데이터베이스를 열고 디스크에 있지만 수십 개의 파일을 메모리에 캐시하는 것입니다.

읽기에 관한 부분은 /proc/meminfo비교할 때 중요하지 않으며, 커널은 정보를 생성하는 데 grep시간이 거의 걸리지 않으며 그것을 읽을 시간이 거의 없습니다.

strace -c당신이 그것을 실행 하면, 당신은 읽고 시작 하는 데 사용되는 하나 open()와 하나의 read()시스템 호출 /proc/meminfo이 다른 모든 grep시작 과 비교할 때 땅콩입니다 ( strace -c포킹을 계산하지 않음).

에서:

a=$(</proc/meminfo)

해당 $(<...)ksh 연산자 를 지원하는 대부분의 셸 에서 셸은 파일을 열고 내용을 읽은 다음 마지막 줄 바꿈 문자를 제거합니다. bash그것은 읽기를 수행하는 프로세스를 포크하고 파이프를 통해 부모에게 데이터를 전달한다는 점에서 다르다. 그러나 여기서는 한 번 수행되므로 중요하지 않습니다.

에서:

printf '%s\n' "$a" | grep '^MemFree'

쉘 은 동시에 실행되지만 파이프를 통해 서로 상호 작용하는 두 개의 프로세스 를 생성해야합니다 . 파이프 생성, 분해 및 쓰기 및 읽기는 약간의 비용이 듭니다. 더 큰 비용은 추가 프로세스의 생성입니다. 프로세스 스케줄링도 약간의 영향을 미칩니다.

zsh <<<연산자를 사용하면 약간 더 빨라질 수 있습니다.

grep '^MemFree' <<< "$a"

zsh 및 bash에서는 $a임시 파일에 내용을 작성하여 추가 프로세스를 생성하는 것보다 비용이 적게 들지만 데이터를 바로 얻는 것과 비교하여 이득을 얻지 못할 것입니다 /proc/meminfo. /proc/meminfo임시 파일 작성이 각 반복마다 수행되므로 디스크에 복사 하는 방식보다 여전히 효율성이 떨어 집니다.

dashhere-string을 지원하지 않지만 heredocs는 추가 프로세스를 생성하지 않는 파이프로 구현됩니다. 에서:

 grep '^MemFree' << EOF
 $a
 EOF

쉘은 파이프를 생성하고 프로세스를 분기합니다. 자식은 grepstdin을 파이프의 읽기 끝으로 실행 하고 부모는 파이프의 다른 끝에 내용을 씁니다.

그러나 파이프 처리 및 프로세스 동기화는 데이터를 바로 처리하는 것보다 여전히 비용이 많이 듭니다 /proc/meminfo.

내용물 /proc/meminfo이 짧고 제작하는 데 시간이 많이 걸리지 않습니다. 일부 CPU주기를 저장하려면 프로세스를 분기하고 외부 명령을 실행하는 등 비싼 부품을 제거하려고합니다.

처럼:

IFS= read -rd '' meminfo < /proc/meminfo
memfree=${meminfo#*MemFree:}
memfree=${memfree%%$'\n'*}
memfree=${memfree#"${memfree%%[! ]*}"}

bash패턴 일치가 매우 비효율적이지만 피하십시오 . 을 사용하면 다음 zsh -o extendedglob과 같이 단축 할 수 있습니다.

memfree=${${"$(</proc/meminfo)"##*MemFree: #}%%$'\n'*}

참고 ^많은 쉘 (Bourne 씨, 물고기, RC, ES 및 적어도 extendedglob 옵션 zsh을)에서 특별하다, 나는 그것을 인용 권하고 싶습니다. 또한 echo임의의 데이터를 출력하는 데 사용할 수 없습니다 (따라서 printf위의 사용 ).


4
경우에 printf당신은 쉘이 두 프로세스를 생성해야하지만 아니라고 printf쉘 내장?
David Conrad

6
@DavidConrad 그러나 대부분의 쉘은 현재 프로세스에서 실행할 있는 파트의 파이프 라인을 분석하려고 시도하지 않습니다 . 그것은 단지 포크 자체이며 아이들이 알아낼 수있게합니다. 이 경우 상위 프로세스는 두 번 분기합니다. 그런 다음 왼쪽의 자식은 내장 기능을보고 실행합니다. 오른쪽의 자식은보고 실행 grep합니다.
chepner

1
@DavidConrad에서 파이프는 IPC 메커니즘이므로 두 경우 모두 다른 프로세스에서 실행해야합니다. 에있는 동안 A | BAT & T ksh 또는 zsh와 같은 B내장 쉘 또는 복합 명령 또는 함수 명령 인 경우 현재 쉘 프로세스에서 실행되는 일부 쉘이 있지만 현재 프로세스에서 실행 되는 쉘을 모릅니다 A. 무엇이든, 그렇게하려면 A자식 프로세스에서 실행되는 것처럼 쉘을 종료하지 않고 복잡한 방식으로 SIGPIPE를 처리해야합니다 B. B부모 프로세스에서 실행 하는 것이 훨씬 쉽습니다 .
Stéphane Chazelas

배쉬 지원<<<
D. Ben Knoble

1
@ D. BenKnoble, 나는 bash지원하지 않았다는 것을 의미하지는 않았다 <<<. 단지 운영자가 ksh에서 온 zsh것처럼 $(<...)온 것입니다.
Stéphane Chazelas

6

첫 번째 경우 grep 유틸리티를 사용하고 file /proc/meminfo에서 무언가를 찾고 /proc가상 파일 시스템이므로 /proc/meminfo파일이 메모리에 있으므로 내용을 가져 오는 데 시간이 거의 걸리지 않습니다.

그러나 두 번째 경우 파이프를 생성 한 다음이 파이프를 사용하여 첫 번째 명령의 출력을 두 번째 명령에 전달하면 비용이 많이 듭니다.

차이점은 /proc(메모리에 있기 때문에) 파이프 때문입니다 . 아래 예를 참조하십시오.

time for i in {1..1000};do grep ^MemFree /proc/meminfo;done >/dev/null

real    0m0.914s
user    0m0.032s
sys     0m0.148s


cat /proc/meminfo > file
time for i in {1..1000};do grep ^MemFree file;done >/dev/null

real    0m0.938s
user    0m0.032s
sys     0m0.152s


time for i in {1..1000};do echo "$a"|grep ^MemFree; done >/dev/null

real    0m1.016s
user    0m0.040s
sys     0m0.232s

1

두 경우 모두 외부 명령을 호출하고 있습니다 (grep). 외부 통화에는 서브 쉘이 필요합니다. 해당 쉘을 포크하는 것이 지연의 기본 원인입니다. 두 경우 모두 비슷하므로 지연이 비슷합니다.

외부 파일을 한 번만 읽고 변수에서 여러 번 사용하려면 쉘에서 나가지 마십시오.

meminfo=$(< /dev/meminfo)    
time for i in {1..1000};do 
    [[ $meminfo =~ MemFree:\ *([0-9]*)\ *.B ]] 
    printf '%s\n' "${BASH_REMATCH[1]}"
done

grep 호출의 전체 1 초 대신 약 0.1 초만 걸립니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.