c0r*_*0rp 8 performance grep bash efficiency
这个问题的动机是“反向grepping ”,关于从下往上grepping一个巨大的文件。
Run Code Online (Sandbox Code Playgroud)tac file | grep whatever
或者更有效一点:
Run Code Online (Sandbox Code Playgroud)grep whatever < <(tac file)
本
< <(tac filename)
应该是一样快的管道
还有许多来自其他用户的有趣评论。
我的问题:
|
和 和有< <()
什么区别?xargs
?Cel*_*ada 11
构造<(tac file)
导致外壳:
/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
,但它是一种优化:在不支持此功能的平台上,如上所述使用真实文件系统中的常规命名管道。
另请注意:将语法描述为<<( ... )
具有误导性。事实上<( ... )
,它被替换为管道的名称,然后作为<
整个事物前缀的另一个字符与此语法分开,它是用于从文件重定向输入的常规众所周知的语法。
| 和有什么区别 和<<()?
它们之间有一个区别:
|
导致每个命令在单独的子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 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
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)