为什么是`tac 文件| grep foo'(管道)比'grep foo < <(tac 文件)'(进程替换)快?

c0r*_*0rp 8 performance grep bash efficiency

这个问题的动机是“反向grepping ”,关于从下往上grepping一个巨大的文件。

@chaos 说

tac file | grep whatever
Run Code Online (Sandbox Code Playgroud)

或者更有效一点:

grep whatever < <(tac file)
Run Code Online (Sandbox Code Playgroud)

@vinc17 说

< <(tac filename)应该是一样快的管道

还有许多来自其他用户的有趣评论。

我的问题:

  • |和 和有< <()什么区别?
  • 为什么一个比另一个快?
  • 哪个真的更快?
  • 为什么没人建议xargs

Cel*_*ada 11

构造<(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
Run Code Online (Sandbox Code Playgroud)

然后grep被执行,它读取它的标准输入(即管道),读取它,并在其中搜索它的第一个参数。

所以最终的结果是一样的......

tac file | grep whatever
Run Code Online (Sandbox Code Playgroud)

...因为启动了相同的两个程序,并且仍然使用管道连接它们。但是<( ... )构造更加复杂,因为它涉及更多步骤并且可能涉及一个临时文件(命名管道)。

<( ... )构造是一个扩展,在标准 POSIX bourne shell 或不支持/dev/fd或命名管道的平台上均不可用。仅出于这个原因,因为所考虑的两种替代方案在功能上完全相同,更便携的command | other-command形式是更好的选择。

<( ... )建筑应该是比较慢,因为额外的卷积的,但它只是在启动阶段,我不期望的差异很容易衡量。

注意:在 Linux SysV 平台上,< ( ... )不使用命名管道而是使用常规管道。常规管道(实际上是所有文件描述符)可以通过特殊命名来引用,/dev/fd/<file-descriptor-number因此 shell 将其用作管道的名称。通过这种方式,它避免了在真实文件系统中创建一个具有真正临时文件名的真实命名管道。尽管/dev/fd技巧是最初出现在 中时用来实现此功能的方法ksh,但它是一种优化:在不支持此功能的平台上,如上所述使用真实文件系统中的常规命名管道。

另请注意:将语法描述为<<( ... )具有误导性。事实上<( ... ),它被替换为管道的名称,然后作为<整个事物前缀的另一个字符与此语法分开,它是用于从文件重定向输入的常规众所周知的语法。


cuo*_*glm 7

| 和有什么区别 和<<()?

它们之间有一个区别:

  • | 导致每个命令在单独的子shell中运行。

  • <() 运行命令,该命令在后台被替换。

对于接下来的两个问题,我们将做一些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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

为什么有些东西比其他东西快?

什么是真正更快?

可以看到,process substitutionpipe这种情况要慢,因为它使用了更多的系统调用。两者都花费大量时间等待子进程,但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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)