【问题标题】:Shell redirection and file I/O durationsShell 重定向和文件 I/O 持续时间
【发布时间】:2012-11-14 06:45:03
【问题描述】:

我以三种不同的方式将一些输出重定向到一个文件,每种方式都需要明显不同的时间。

$ >/tmp/file ; time for i in {1..1000}; do for j in {1..1000}; do echo $i $j >> /tmp/file; done; done

real    0m33.467s
user    0m21.170s
sys     0m11.919s

$ >/tmp/file ; exec 3>/tmp/file; time for i in {1..1000}; do for j in {1..1000}; do echo $i $j >&3; done; done; exec 3>&-

real    0m24.211s
user    0m17.181s
sys     0m7.002s

$ >/tmp/file ; time for i in {1..1000}; do for j in {1..1000}; do echo $i $j; done; done >> /tmp/file 

real    0m17.038s
user    0m13.072s
sys     0m3.945s

谁能解释一下这里的区别。我目前的理解/怀疑是:

  1. 1st 是最慢的,因为它会多次打开/关闭文件,而其他人只执行一次。那正确吗?缓冲呢。通常,我希望所有输出都得到缓冲,在这种情况下,我们不应该有这么大的时间差异。
  2. 第三,如果所有输出都只写在外循环的末尾,那么当循环仍在执行时,所有输出都存储在哪里。也许在记忆中。这是否意味着如果我回显很多内容并且只在最后写入,我可能会耗尽内存。
  3. 第二名更像第一名或第三名。为什么两者都如此不同。

PS:我已经运行了几次上述命令,发现时间是一致的。所以,我看到的差异一定是由于一些真正的原因。

【问题讨论】:

  • 是的,每次追加都会打开和关闭文件。您对缓冲的评论不适用,因为文件每次都关闭,并且会刷新缓冲区。
  • 您的第二条评论错过了重要的一点,从外部循环重定向与时间无关,这意味着它内部的所有内容都被重定向。它不会将数据存储在内存中(正常缓冲除外),然后只完成一次写入,每次缓冲区变满时都会写入。缓冲区大小各不相同,通常可能在 2k 到 4k 之间。
  • 正如时序所示,#2 介于 #1 和 #3 之间。没有打开和关闭输出文件,但在内循环中有文件描述符 3 的 dup(),而替代 #3 没有这样的开销。
  • @cdarke - 感谢您的回复。你说的很有道理。
  • @tripleee - 抱歉,我不明白。在#2 中,我们在开头打开文件并在结尾关闭。所以,就像#3。写fd是什么意思。我会假设它与写入文件相同。因此,如上所述,这些写入将被缓冲,直到缓冲区已满,然后才被写入。这使它与#3 更加相似。 dup() 的开销是否足以解释我看到的差异,即 17 秒到 24 秒。这里发生了什么额外的工作?

标签: linux bash io


【解决方案1】:
  1. 第一个版本对echo $i $j >> /tmp/file 做了一百万次,它打开文件进行追加,写入并关闭它。

  2. 执行一百万次echo $i $j >&3 与一次的不同之处在于它不会每次都打开/关闭文件,而是写入文件描述符#3。 exec 3>/tmp/file 打开文件进行写入并将文件描述符保存为#3。现在,当命令将其标准输出重定向到文件描述符 #3(回显后 >&3 的效果)时,shell 需要在执行命令之前设置此重定向,然后恢复之前对标准输出的分配。

  3. >> /tmp/file 这样重定向整个循环的输出对于 shell 来说要容易得多:它可以简单地执行 echo 命令,而无需设置额外的文件描述符。它只更改一次标准输出的分配。

关于缓冲:在所有三种情况下,底层文件系统都会缓冲对物理文件的访问,因此在该级别上没有区别。此外,大多数 linux 都在 /tmp 上安装了一个 tmpfs,它使您所做的一切都成为纯内存操作。所以你在这里测量的不是 IO 性能,而是 shell 命令执行性能。您可以通过增加写入的字节数来证明这一点(在行回显打印中添加一个常量值):

>/tmp/file ; time for i in {1..1000}; do for j in {1..1000}; do echo "1000000 $i $j" >> /tmp/file; done; done

>/tmp/file ; exec 3>/tmp/file; time for i in {1..1000}; do for j in {1..1000}; do echo "1000000 $i $j" >&3; done; done; exec 3>&-

>/tmp/file ; time for i in {1..1000}; do for j in {1..1000}; do echo "1000000 $i $j"; done; done >> /tmp/file

在我的 PC 上,这与没有常量“1000000”的时间相同,但写入文件的字节数是原来的两倍。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-11-09
    • 2013-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-20
    相关资源
    最近更新 更多