为什么echo> file比echo使用更多实时时间| sed>文件?


28

下面的例子令我惊讶。这似乎是反直观的……除了事实之外,该组合还有更多的用户时间echo | sed

为什么单独运行时会花费echo那么多系统时间,或者问题应该是,如何sed改变游戏状态?似乎在两种情况下echo都需要执行相同的回显 ...

time echo -n a\ {1..1000000}\ c$'\n' >file

# real    0m9.481s
# user    0m5.304s
# sys     0m4.172s

time echo -n a\ {1..1000000}\ c$'\n' |sed s/^\ // >file

# real    0m5.955s
# user    0m5.488s
# sys     0m1.580s

1
我的直觉反应是它与缓冲有关。
bahamat 2012年

1
@bahamat我认为你是对的。echo对每个参数执行单独的write()。sed缓冲它们。因此,第一个版本通过文件系统驱动程序进入块设备层时,对常规文件的写入量为100万次,而第二个版本中,通过管道的写入量为100万次,而通过较昂贵的内核代码层的写入量则较少。
艾伦·库里

@bahamat绝对缓冲。time echo ... |cat >file甚至time echo ... |perl -ne 'print'sed版本相似。
StarNamer 2012年

4
感谢大家的良好解释...因此,对于大型多行书写(以bash表示),cat获得了Cat点的有用用法:)
Peter.O 2012年

Answers:


29

bahamat和Alan Curry正确地说:这是由于您的shell缓冲的输出的方式echo。具体来说,您的shell是bash,write每行发出一个系统调用。因此,第一个代码段对磁盘文件进行1000000次写入,而第二个代码段对管道进行1000000次写入并sed(如果有多个CPU,则并行执行)由于其输出而导致对磁盘文件的写入次数要少得多缓冲。

您可以通过运行strace观察发生了什么。

$ strace -f -e write bash -c 'echo -n a\ {1..2}\ c$'\'\\n\'' >file'
write(1, "a 1 c\n", 6)                  = 6
write(1, " a 2 c\n", 7)                 = 7
$ strace -f -e write bash -c 'echo -n a\ {1..2}\ c$'\'\\n\'' | sed "s/^ //" >file'
Process 28052 attached
Process 28053 attached
Process 28051 suspended
[pid 28052] write(1, "a 1 c\n", 6)      = 6
[pid 28052] write(1, " a 2 c\n", 7)     = 7
Process 28051 resumed
Process 28052 detached
Process 28051 suspended
[pid 28053] write(1, "a 1 c\na 2 c\n", 12) = 12
Process 28051 resumed
Process 28053 detached
--- SIGCHLD (Child exited) @ 0 (0) ---

其他外壳程序(例如ksh)echo即使在多行的情况下也缓冲了输出,因此不会有太大的区别。

$ strace -f -e write ksh -c 'echo -n a\ {1..2}\ c$'\'\\n\'' >file'
write(1, "a 1 c\n a 2 c\n", 13)         = 13
$ strace -f -e write ksh -c 'echo -n a\ {1..2}\ c$'\'\\n\'' | sed "s/^ //" >file'
Process 28058 attached
[pid 28058] write(1, "a 1 c\n a 2 c\n", 13) = 13
Process 28058 detached
--- SIGCHLD (Child exited) @ 0 (0) ---
write(1, "a 1 c\na 2 c\n", 12)          = 12

使用bash可以获得类似的计时比率。使用ksh时,我看到第二个代码段运行速度变慢。

ksh$ time echo -n a\ {1..1000000}\ c$'\n' >file

real    0m1.44s
user    0m1.28s
sys     0m0.06s
ksh$ time echo -n a\ {1..1000000}\ c$'\n' | sed "s/^ //" >file

real    0m2.38s
user    0m1.52s
sys     0m0.14s

谢谢...最后一个ksh例子更符合我的预期……
Peter.O 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.