为什么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.