`tac file | grep foo'(파이프)가 'grep foo <<(tac 파일)'(프로세스 교체)보다 빠른 이유는 무엇입니까?

`tac file | grep foo'(파이프)가 'grep foo <<(tac 파일)'(프로세스 교체)보다 빠른 이유는 무엇입니까?

이 질문의 동기는 "역방향 가져오기", 거대한 파일을 아래에서 위로 greping하는 방법에 대해 설명합니다.

@카오스가 말했어요:

tac file | grep whatever

또는 더 효율적으로:

grep whatever < <(tac file)

@vinc17 님이 말씀하셨습니다.:

< <(tac filename)파이프만큼 빨라야 합니다 .

다른 사용자들의 흥미로운 의견도 많이 있습니다.

내 질문:

  • |이들그리고 그리고 의 차이점은 무엇인가요 < <()?
  • 하나가 다른 것보다 빠른 이유는 무엇입니까?
  • 어느 것이 실제로 더 빠른가요?
  • 왜 아무도 제안하지 않았나요 xargs?

답변1

이 구성 <(tac file)으로 인해 쉘이 생성됩니다.

  • 이름으로 파이프 만들기
    • Linux 및 SysV와 같은 시스템에서는 /dev/fd일반 파이프를 사용하고 /dev/fd/<the-file-descriptor-of-the-pipe>이를 이름으로 사용합니다.
    • 다른 시스템에서는 디스크에 실제 파일 항목을 생성해야 하는 명명된 파이프가 사용됩니다.
  • 명령을 시작 tac file하고 파이프의 한쪽 끝에 연결합니다.
  • 명령줄의 전체 구조를 파이프 이름으로 바꿉니다.

교체 후 명령줄은 다음과 같습니다.

grep whatever < /tmp/whatever-name-the-shell-used-for-the-named-pipe

그런 다음 grep실행하려면 표준 입력(예: 파이프)을 읽고, 읽은 후 첫 번째 인수를 검색합니다.

그래서 최종 결과는 동일합니다 ...

tac file | grep whatever

...동일한 두 프로그램이 실행되고 파이프를 사용하여 연결하기 때문입니다. 그러나 <( ... )빌드 프로세스는 더 많은 단계를 포함하고 임시 파일(이름이 지정된 파이프)을 포함할 수 있으므로 더 복잡합니다.

<( ... )구성은 확장이며 /dev/fd표준 POSIX bourne 쉘이나 파이프를 지원하지 않거나 명명된 플랫폼에서는 사용할 수 없습니다. 이러한 이유만으로도 고려 중인 두 가지 대안은 기능적으로 동일하므로 이식성이 뛰어난 command | other-command형태가 더 나은 선택입니다.

추가 컨볼루션으로 인해 빌드 <( ... )속도가 느려져야 하지만 이는 시작 단계에만 해당되며 차이를 쉽게 측정할 수 없다고 생각합니다.

노트: Linux SysV 플랫폼에서는 < ( ... )명명된 파이프가 사용되지 않고 대신 일반 파이프가 사용됩니다. 일반 파이프(실제로 모든 파일 설명자)는 특별한 이름으로 참조될 수 있으므로 /dev/fd/<file-descriptor-number쉘은 해당 이름을 파이프 이름으로 사용합니다. 이렇게 하면 실제 파일 시스템에서 실제 임시 파일 이름을 사용하여 실제 명명된 파이프가 생성되는 것을 방지할 수 있습니다. /dev/fd이 기능이 에 처음 등장했을 때 구현하는 데 사용된 트릭이지만 이는 ksh최적화입니다. 이 기능을 지원하지 않는 플랫폼에서는 위에서 설명한 대로 실제 파일 시스템의 일반 명명된 파이프가 사용됩니다.

또한 참고하시기 바랍니다: 문법이 <<( ... )오해의 소지가 있다고 설명합니다. 실제로는 <( ... )파이프 이름으로 대체되고 <이 구문과 별도로 모든 항목 앞에 붙는 또 다른 문자가 사용되며 파일에서 입력을 리디렉션하는 잘 알려진 일반적인 구문입니다.

답변2

|와 <<()의 차이점은 무엇입니까?

그들 사이에는 차이점이 있습니다.

  • |각 명령이 별도의 하위 쉘에서 실행되도록 합니다.

  • <()백그라운드에서 대체되는 명령을 실행합니다.

다음 두 가지 질문에 대해 우리는 몇 가지를 할 것입니다 strace:

pipe:

$ strace -fc bash -c 'tac /usr/share/dict/american-english | grep qwerty'
$ time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00    0.008120        2707         3         1 wait4
  0.00    0.000000           0       352           read
  0.00    0.000000           0       229           write
  0.00    0.000000           0        20         2 open
  0.00    0.000000           0        29         2 close
  0.00    0.000000           0        40        17 stat
  0.00    0.000000           0        19           fstat
  0.00    0.000000           0       117           lseek
  0.00    0.000000           0        38           mmap
  0.00    0.000000           0        18           mprotect
  0.00    0.000000           0         6           munmap
  0.00    0.000000           0        25           brk
  0.00    0.000000           0        22           rt_sigaction
  0.00    0.000000           0        18           rt_sigprocmask
  0.00    0.000000           0         1           rt_sigreturn
  0.00    0.000000           0         3         2 ioctl
  0.00    0.000000           0        24        12 access
  0.00    0.000000           0         1           pipe
  0.00    0.000000           0         2           dup2
  0.00    0.000000           0         1           getpid
  0.00    0.000000           0         1         1 getpeername
  0.00    0.000000           0         2           clone
  0.00    0.000000           0         3           execve
  0.00    0.000000           0         1           uname
  0.00    0.000000           0         1           getrlimit
  0.00    0.000000           0        13           getuid
  0.00    0.000000           0        13           getgid
  0.00    0.000000           0        13           geteuid
  0.00    0.000000           0        13           getegid
  0.00    0.000000           0         1           getppid
  0.00    0.000000           0         1           getpgrp
  0.00    0.000000           0         3           arch_prctl
  0.00    0.000000           0         1           time
------ ----------- ----------- --------- --------- ----------------
100.00    0.008120                  1034        37 total

Process Substitution:

$ strace -fc bash -c 'grep qwerty < <(tac /usr/share/dict/american-english)'
$ time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 99.14    0.016001        4000         4         2 wait4
  0.46    0.000075           0       229           write
  0.24    0.000038           0       341           read
  0.16    0.000026           1        24           brk
  0.00    0.000000           0        21         2 open
  0.00    0.000000           0        27           close
  0.00    0.000000           0        40        17 stat
  0.00    0.000000           0        19           fstat
  0.00    0.000000           0       117           lseek
  0.00    0.000000           0        38           mmap
  0.00    0.000000           0        18           mprotect
  0.00    0.000000           0         6           munmap
  0.00    0.000000           0        35           rt_sigaction
  0.00    0.000000           0        24           rt_sigprocmask
  0.00    0.000000           0         2           rt_sigreturn
  0.00    0.000000           0         3         2 ioctl
  0.00    0.000000           0        24        12 access
  0.00    0.000000           0         1           pipe
  0.00    0.000000           0         3           dup2
  0.00    0.000000           0         1           getpid
  0.00    0.000000           0         1         1 getpeername
  0.00    0.000000           0         3           clone
  0.00    0.000000           0         3           execve
  0.00    0.000000           0         1           uname
  0.00    0.000000           0         1         1 fcntl
  0.00    0.000000           0         2           getrlimit
  0.00    0.000000           0        13           getuid
  0.00    0.000000           0        13           getgid
  0.00    0.000000           0        13           geteuid
  0.00    0.000000           0        13           getegid
  0.00    0.000000           0         1           getppid
  0.00    0.000000           0         1           getpgrp
  0.00    0.000000           0         3           arch_prctl
  0.00    0.000000           0         1           time
------ ----------- ----------- --------- --------- ----------------
100.00    0.016140                  1046        37 total

왜 어떤 것들은 다른 것보다 더 빠른가요?

실제로 더 빠른 것은 무엇입니까?

process substitution더 많은 시스템 호출을 사용하기 때문에 이 예제보다 속도가 느린 것을 볼 수 있습니다 . pipe둘 다 하위 프로세스를 기다리는 데 많은 시간을 소비하지만 process substitution더 많은 시스템 호출을 사용 wait4()하고 각 호출은 pipe.

왜 아무도 xargs를 제안하지 않았나요?

나는 여기에 많은 도움이 있다고 생각하지 않습니다 xargs. 그것은 그 일이 아닙니다.

고쳐 쓰다

@Gilles가 제안한 대로 더 큰 파일을 사용했습니다( /dev/urandom. 에서 . 파일이 pipe실제로 process substitution.

pipe:

$ strace -fc bash -c 'tac sample.txt | grep qwerty'
$ time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 81.15    8.284959     2761653         3         1 wait4
 17.89    1.825959           2    780959           read
  0.91    0.092708           0    524286           write
  0.05    0.005364           0    262146           lseek
  0.00    0.000000           0        20         2 open
  0.00    0.000000           0        29         2 close
  0.00    0.000000           0        40        17 stat
  0.00    0.000000           0        19           fstat
  0.00    0.000000           0        38           mmap
  0.00    0.000000           0        18           mprotect
  0.00    0.000000           0         6           munmap
  0.00    0.000000           0        25           brk
  0.00    0.000000           0        22           rt_sigaction
  0.00    0.000000           0        18           rt_sigprocmask
  0.00    0.000000           0         1           rt_sigreturn
  0.00    0.000000           0         3         2 ioctl
  0.00    0.000000           0        24        12 access
  0.00    0.000000           0         1           pipe
  0.00    0.000000           0         2           dup2
  0.00    0.000000           0         1           getpid
  0.00    0.000000           0         1         1 getpeername
  0.00    0.000000           0         2           clone
  0.00    0.000000           0         3           execve
  0.00    0.000000           0         1           uname
  0.00    0.000000           0         1           getrlimit
  0.00    0.000000           0        13           getuid
  0.00    0.000000           0        13           getgid
  0.00    0.000000           0        13           geteuid
  0.00    0.000000           0        13           getegid
  0.00    0.000000           0         1           getppid
  0.00    0.000000           0         1           getpgrp
  0.00    0.000000           0         3           arch_prctl
  0.00    0.000000           0         1           time
------ ----------- ----------- --------- --------- ----------------
100.00   10.208990               1567727        37 total

process substitution:

$ strace -fc bash -c 'grep qwerty < <(tac sample.txt)'
$ time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 99.51   13.912869     3478217         4         2 wait4
  0.38    0.053373           0    655269           read
  0.09    0.013084           0    524286           write
  0.02    0.002454           0    262146           lseek
  0.00    0.000030           1        38           mmap
  0.00    0.000024           1        24        12 access
  0.00    0.000000           0        21         2 open
  0.00    0.000000           0        27           close
  0.00    0.000000           0        40        17 stat
  0.00    0.000000           0        19           fstat
  0.00    0.000000           0        18           mprotect
  0.00    0.000000           0         6           munmap
  0.00    0.000000           0        24           brk
  0.00    0.000000           0        35           rt_sigaction
  0.00    0.000000           0        24           rt_sigprocmask
  0.00    0.000000           0         2           rt_sigreturn
  0.00    0.000000           0         3         2 ioctl
  0.00    0.000000           0         1           pipe
  0.00    0.000000           0         3           dup2
  0.00    0.000000           0         1           getpid
  0.00    0.000000           0         1         1 getpeername
  0.00    0.000000           0         3           clone
  0.00    0.000000           0         3           execve
  0.00    0.000000           0         1           uname
  0.00    0.000000           0         1         1 fcntl
  0.00    0.000000           0         2           getrlimit
  0.00    0.000000           0        13           getuid
  0.00    0.000000           0        13           getgid
  0.00    0.000000           0        13           geteuid
  0.00    0.000000           0        13           getegid
  0.00    0.000000           0         1           getppid
  0.00    0.000000           0         1           getpgrp
  0.00    0.000000           0         3           arch_prctl
  0.00    0.000000           0         1           time
------ ----------- ----------- --------- --------- ----------------
100.00   13.981834               1442060        37 total

답변3

나는 ~였다아니요표시된 결과를 복사하는 기능쿠엔람. 2GB 파일의 경우에도 MacOS Mojave의 Bash 5에서는 프로세스 교체와 파이프 사이의 시간이 매우 유사합니다. 호출과 관련된 오버헤드가 다음과 같기 때문에 이는 나에게 의미가 있습니다.가장 작은2GB 파일에 대한 호출의 실제 처리와 비교할 때, 파이프의 한 번의 반복으로 프로세스 대체를 사용하는 것은 임의성/파일 내용을 캐시하기 위해 먼저 실행되는 명령에 따라 달라집니다.

예전에는연구 결과를 재현하는 능력이 질문에는이는 다음을 나타냅니다.파이프보다 공정 교체가 빠릅니다.이러한 전화는 수천 번 이루어졌습니다.

이것은 내가 실행한 명령과 결과입니다.

파이프.sh:

shopt -s lastpipe
for i in {1..5000}; do
    echo foo bar |
    while read; do
        echo $REPLY >/dev/null
    done
done

proc-sub.sh:

for i in {1..5000}; do
    while read; do
        echo $REPLY >/dev/null
    done < <(echo foo bar)
done

최종 파이프가 없는 파이프.sh:

for i in {1..5000}; do
    echo foo bar |
    while read; do
        echo $REPLY >/dev/null
    done
done

시험:

time ./proc-sub.sh

real    0m9.505s
user    0m1.875s
sys     0m10.705s

time ./pipe.sh

real    0m14.036s
user    0m4.583s
sys     0m14.193s

time ./pipe-no-lastpipe.sh

real    0m16.696s
user    0m3.055s
sys     0m18.057s

관련 정보