파이프와 here 문자열을 사용한 리소스 사용량


16

에서 다음 두 가지를 사용하여 동일한 결과를 얻을 수 있습니다 bash.

echo 'foo' | cat

cat <<< 'foo'

내 질문은 사용 된 자원에 관한 한이 두 가지의 차이점은 무엇이며 어느 것이 더 낫습니까?

내 생각은 파이프를 사용하는 동안 우리는 여분의 프로세스 echo와 파이프를 사용하고 여기 문자열에서는 파일 설명자 만 사용한다고 생각 cat합니다.

답변:


17

파이프는 커널 내부 파일 시스템에서 열린 파일이며 디스크의 일반 파일로 액세스 할 수 없습니다. 특정 크기로만 자동 버퍼링되며 가득 차면 결국 차단됩니다. 블록 장치에서 제공되는 파일과 달리 파이프는 문자 장치와 매우 유사하게 작동하므로 일반적으로 지원하지 않으며 파일에서 lseek()읽은 데이터는 일반 파일에서와 같이 다시 읽을 수 없습니다.

here-string은 마운트 된 파일 시스템에서 생성 된 일반 파일입니다. 쉘은 파일을 생성하고 즉시 전용 파일 시스템 링크를 제거하는 동안 그 설명을 유지 (그래서 그것을 삭제) 전에 이제까지는 기록 / 파일에서 /에 바이트를 읽습니다. 커널은 모든 프로세스가 파일에 대한 모든 설명자를 해제 할 때까지 파일에 필요한 공간을 유지합니다. 그러한 디스크립터에서 읽는 자식이 그렇게 할 수있는 능력이 있다면, 다시 감고 lseek()다시 읽을 수 있습니다 .

두 경우 모두 토큰 <<<|파일 설명자를 나타내며 반드시 파일 자체는 아닙니다. 다음과 같은 작업을 수행하여 진행 상황을 더 잘 이해할 수 있습니다.

readlink /dev/fd/1 | cat

...또는...

ls -l <<<'' /dev/fd/*

두 파일의 가장 큰 차이점은 here-string / doc이 거의 한 번에 발생하는 일이라는 점입니다. 셸은 읽기 설명자를 자식에게 제공하기 전에 모든 데이터를 기록합니다. 반면에, 쉘은 적절한 디스크립터에서 파이프를 열고 파이프에 대한 디스크립터를 관리하기 위해 하위를 포크 오프하므로 양쪽 끝에서 동시에 쓰거나 읽습니다 .

그러나 이러한 차이점은 일반적으로 사실입니다. 내가 아는 한 (실제로는 그렇지는 않습니다) 이것은 단일 예외를 가진 here-document redirection에 <<<대한 here-string short-hand를 처리하는 거의 모든 셸에서 사실 <<입니다 yash. yash, busybox, dash, 기타 및 ash변형, 그래서 그 껍질에 정말 결국 둘 사이에 약간의 차이가 있지만, 파이프 여기-문서를 백업하는 경향이있다.

확인-두 가지 예외가 있습니다. 이제 그것에 대해 생각하고 ksh93실제로 파이프를 전혀 사용 |하지 않지만 소켓을 사용하여 전체 비즈니스를 처리합니다 <<<*. 대부분의 다른 사람들처럼 삭제 된 tmp 파일을 수행합니다. 또한 파이프 라인의 별도 섹션을 서브 쉘 환경에 배치합니다.이 은 적어도 서브 쉘과 같은 역할을 하기 때문에 일종의 POSIX 완곡 어이며 포크조차하지 않습니다.

사실 @PSkocik의 벤치 마크 (매우 유용한) 결과는 여러 가지 이유로 광범위하게 달라질 수 있으며, 대부분은 구현에 따라 다릅니다. 여기서 문서 설정의 가장 큰 요인은 대상 ${TMPDIR}파일 시스템 유형과 현재 캐시 구성 / 가용성 및 더 많은 데이터를 쓸 것입니다. 파이프의 경우 필요한 포크에 대해 사본이 작성되므로 쉘 프로세스 자체의 크기가됩니다. 이 방법에서 bash입니다 끔찍한 파이프 라인 설치에 (포함하는 $(명령 )대체를) - 그것은 크고 있기 때문에 매우 느린, 그러나와 ksh93는 전혀 거의 차이가 있습니다.

다음은 쉘이 파이프 라인의 서브 쉘을 분리하는 방법을 보여주는 또 다른 작은 쉘 스 니펫입니다.

pipe_who(){ echo "$$"; sh -c 'echo "$PPID"'; }
pipe_who
pipe_who | { pipe_who | cat /dev/fd/3 -; } 3<&0

32059  #bash's pid
32059  #sh's ppid
32059  #1st subshell's $$
32111  #1st subshell sh's ppid
32059  #2cd subshell's $$
32114  #2cd subshell sh's ppid

파이프 라인 된 pipe_who()호출이보고하는 것과 현재 셸에서 한 실행에 대한보고의 차이점 은 확장 될 때 부모 셸의 pid를 주장 하는 (서브 쉘의 )지정된 동작 $$때문입니다. 하지만 bash서브 쉘은 확실히 별도의 프로세스가의 $$특별한 쉘 매개 변수는이 정보의 신뢰할 수있는 원본이 아니다. 그럼에도 불구하고 서브 쉘의 자식 sh쉘은이를 정확하게보고하지 않습니다 $PPID.


매우 도움이됩니다. 커널 내부 파일 시스템에 이름이 있습니까? 커널 공간에 존재한다는 의미입니까?
utlamn

2
@utlamn-사실, 예-단순히 pipefs . 하지만 - 그것은 모두에서 커널의 (옆으로 물건에서 FUSE 등) 그렇습니다 모든 파일 I / O .
mikeserv

10

벤치마킹을 대체 할 수있는 것은 없습니다 :

pskocik@ProBook:~ 
$ time (for((i=0;i<1000;i++)); do cat<<< foo >/dev/null; done  )

real    0m2.080s
user    0m0.738s
sys 0m1.439s
pskocik@ProBook:~ 
$ time (for((i=0;i<1000;i++)); do echo foo |cat >/dev/null; done  )

real    0m4.432s
user    0m2.095s
sys 0m3.927s
$ time (for((i=0;i<1000;i++)); do cat <(echo foo) >/dev/null; done  )
real    0m3.380s
user    0m1.121s
sys 0m3.423s

그리고 더 많은 양의 데이터의 경우 :

TENMEG=$(ruby -e 'puts "A"*(10*1024*1024)')
pskocik@ProBook:~ 
$ time (for((i=0;i<100;i++)); do echo "$TENMEG" |cat >/dev/null; done  )

real    0m42.327s
user    0m38.591s
sys 0m4.226s
pskocik@ProBook:~ 
$ time (for((i=0;i<100;i++)); do cat<<< "$TENMEG" >/dev/null; done  )

real    1m26.946s
user    1m23.116s
sys 0m3.681s
pskocik@ProBook:~ 

$ time (for((i=0;i<100;i++)); do cat <(echo "$TENMEG") >/dev/null; done  )

real    0m43.910s
user    0m40.178s
sys 0m4.119s

파이프 버전은 설치 비용이 더 크지 만 결국 더 효율적입니다.


@mikeserv 맞습니다. 더 많은 양의 데이터로 벤치 마크를 추가했습니다.
PSkocik

2
echo foo >/dev/shm/1;cat /dev/shm/1 >/dev/null두 경우 모두에 빠른 것처럼 보였다 ...
user23013

@ user23013 이해가 되네요. 효율성과 짧은 줄로 효율성을 조정 echo "$longstring"하거나 <<<"$longstring"조정 해야하는 이유를 모르겠습니다 . 어쨌든 효율성은별로 중요하지 않습니다.
PSkocik

필자의 경우 (Ubuntu 14.04에서 Intel quad core i7)가보다 cat <(echo foo) >/dev/null빠릅니다 echo foo | cat >/dev/null.
pabouk

1
@Prem 그래, 더 나은 접근 방법이지만, 더 좋은 방법은 전혀 걱정하지 않고 작업에 적합한 도구를 사용하는 것입니다. heredocs가 성능 조정될 것이라고 생각할 이유가 없습니다.
PSkocik
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.