我以三种不同的方式将一些输出重定向到文件,每种方式花费的时间明显不同。
$ >/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
有人可以解释这里的差异吗?我目前的理解/疑问是:
1st 最慢,因为它多次打开/关闭文件- ,而其他人只打开/关闭文件一次。是吗?缓冲呢。通常,我希望所有输出都得到缓冲,在这种情况下,我们不应该有这么大的时差。
- 在 3rd 中,如果所有输出只写在外循环的末尾,则在循环仍在执行时存储所有输出。也许在记忆中。这是否意味着如果我回显很多东西并且只在最后写,我可能会耗尽内存。
- 是第二个更像第一个或第三个。为什么它与两者都如此不同。
PS:我已经运行了几次上述命令,发现时间是一致的。所以,我看到的差异一定是由于一些真正的原因。
-
第一个版本执行一百万倍的
echo $i $j >> /tmp/file
,它打开文件进行追加,写入并关闭它。
执行一百万次 一百万次的不同之处在于,它不会每次都打开/关闭文件,而是写入文件描述符 #3。
exec 3>/tmp/file
打开文件进行写入,并将文件描述符另存为 #3。当现在命令将其 stdout 重定向到文件描述符 #3(回显后>&3
的效果)时,shell 需要在执行命令之前设置此重定向,然后将以前的赋值恢复到 stdout。像这样重定向完整循环的输出
>> /tmp/file
对于 shell 来说要容易得多:它可以简单地执行 echo 命令,而无需设置额外的文件描述符。它只更改一次标准输出的分配。
echo $i $j >&3
与执行关于缓冲:在所有三种情况下,底层文件系统都将缓冲对物理文件的访问,因此在该级别没有区别。此外,大多数 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 "所需的时间相同,但向文件写入的字节数是其两倍。