Shell重定向和文件I/O持续时间

Viv*_*vek 6 linux io bash

我正在以三种不同的方式将一些输出重定向到文件,并且每个输出都占用明显不同的时间.

$ >/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
Run Code Online (Sandbox Code Playgroud)

有人可以解释这里的差异.我目前的理解/怀疑是:

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

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

hol*_*ero 1

  1. 第一个版本执行 100 万次echo $i $j >> /tmp/file,打开文件进行追加、写入并关闭它。

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

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

关于缓冲:在所有三种情况下,底层文件系统都会缓冲对物理文件的访问,因此在该级别上没有区别。此外,大多数 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
Run Code Online (Sandbox Code Playgroud)

在我的电脑上,这与没有常量“1000000”时花费的时间相同,但向文件写入的字节数是其两倍。