在大约300,000个文件上,以下
echo
命令需要10秒钟:for file in `find . -name "*.xml"`; do echo >> $file; done
但是以下
touch
命令需要10分钟! :for file in `find . -name "*.xml"`; do touch $file; done
为什么回声比触摸此处要快得多?
#1 楼
在bash中,touch
是一个外部二进制文件,而echo
是一个内置的shell:$ type echo
echo is a shell builtin
$ type touch
touch is /usr/bin/touch
由于
touch
是一个外部二进制文件,您对每个文件调用一次touch
,因此该shell必须创建300,000个touch
实例,这需要很长的时间。echo
是Shell内置的,执行shell内置不需要分叉。相反,当前的shell会执行所有操作,并且不会创建任何外部进程。这就是为什么它要快得多的原因。这是Shell操作的两个配置文件。您会看到使用
touch
克隆新进程时花费了大量时间。使用/bin/echo
代替内置的shell应该显示出更多可比的结果。使用touch
$ strace -c -- bash -c 'for file in a{1..10000}; do touch "$file"; done'
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
56.20 0.030925 2 20000 10000 wait4
38.12 0.020972 2 10000 clone
4.67 0.002569 0 80006 rt_sigprocmask
0.71 0.000388 0 20008 rt_sigaction
0.27 0.000150 0 10000 rt_sigreturn
[...]
使用echo
$ strace -c -- bash -c 'for file in b{1..10000}; do echo >> "$file"; done'
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
34.32 0.000685 0 50000 fcntl
22.14 0.000442 0 10000 write
19.59 0.000391 0 10011 open
14.58 0.000291 0 20000 dup2
8.37 0.000167 0 20013 close
[...]
评论
您是在OS X上编译strace还是在另一个OS上运行测试?
–bmike
2014年4月9日19:49
@bmike我的测试是在Linux上进行的,但是原理是相同的。
–克里斯唐(Chris Down)
2014年4月10日在2:24
我完全同意-看到我对主要问题的评论,即/ bin / echo与/ bin / touch一样慢,因此推理是合理的。我只是想重现strace的时序,但使用dtruss / dtrace失败了,bash -c语法在OS X上也无法正常工作。
–bmike
2014年4月10日在2:56
#2 楼
正如其他人回答的那样,使用echo
将比touch
更快,因为echo
是通常内置于外壳中的命令(尽管不是必需的)。使用它消除了与为touch
获得的每个文件运行一个新进程有关的内核开销。但是,请注意,达到此效果的最快方法仍然是使用
touch
,但是与为每个文件运行一次程序相比,可以将-exec
选项与find
一起使用,以确保仅运行几次。此方法通常会更快,因为它避免了与Shell循环相关的开销:find . -name "*.xml" -exec touch {} +
将
+
(与\;
相对)与find ... -exec
一起使用仅运行一次命令如果可能,将每个文件作为参数。如果参数列表很长(例如300,000个文件),那么将使用长度接近限制的参数列表进行多次运行(在大多数系统上为ARG_MAX
)。另一个优点这种方法的优点是,它可以对包含所有空格字符的文件名表现出强大的性能,而原始循环则不是如此。
评论
+1指出find +参数。我认为很多人都没有意识到这一点(我不是)。
– Gerrit
2014年4月9日12:50
并非所有版本的find都带有+参数。通过管道传递给xargs,可以获得类似的效果。
– Barmar
2014年4月9日在18:08
@ Barmar,POSIX要求+部分,因此应该是可移植的。 -print0不是。
– Graeme
2014年4月9日在21:01
我仍然偶尔会遇到没有它的实现。 YMMV。
– Barmar
2014年4月9日在21:03
@ChrisDown,我发现的发现是Busybox查找具有可用选项,但只将其视为;即可。在表面之下。
– Graeme
2014年4月10日在9:25
#3 楼
echo
是内置的shell。另一方面,touch
是一个外部二进制文件。$ type echo
echo is a shell builtin
$ type touch
touch is hashed (/usr/bin/touch)
Shell内置程序要快得多,因为在加载程序时不会涉及开销,即没有
fork
/ exec
参与其中。这样,您会在执行内置命令与外部命令时观察到明显的时间差异。这就是为什么像
time
这样的实用程序可用作shell内置程序的原因。 > 您可以通过说出以下内容来获取完整的shell内置列表:
enable -p
如上所述,使用实用程序而非内置函数导致性能显着下降。以下是使用内置
echo
和实用程序echo
创建〜9000个文件所花费时间的统计信息:# Using builtin
$ time bash -c 'for i in {1000..9999}; do echo > $i; done'
real 0m0.283s
user 0m0.100s
sys 0m0.184s
# Using utility /bin/echo
$ time bash -c 'for i in {1000..9999}; do /bin/echo > $i; done'
real 0m8.683s
user 0m0.360s
sys 0m1.428s
评论
而且我认为大多数系统上都有一个回显二进制文件(对我来说是/ bin / echo),因此您可以使用它而不是内置的方法重试时序测试。
– Michael Mrozek
2014年4月9日下午5:31
@MichaelMrozek添加了针对内置和二进制文件的计时测试。
– devnull
2014年4月9日下午13:06
评论
附带一提:您知道这两个命令并不等效,不是吗?至少对于Unix / Linux,echo >> $ file将在$ file后面添加换行符,然后对其进行修改。我认为对于OS / X它将是相同的。如果您不想这样做,请使用echo -n >> $ file。也不会碰`find。名称“ * .xml”`甚至比上述两者都快?
或者只考虑>> $ file
这不是对明确问题的答案,但是为什么要调用触摸那么多次呢?找 。名称'* .xml'-print0 | xargs -0 touch调用触摸的次数要少得多(可能只有一次)。在Linux上可以使用,在OS X上也可以使用。
@elmo参数列表太长(很容易包含300.000个文件...)