提供相同的程序,该程序读取随机生成的输入文件并回显它读取到输出的相同字符串。唯一的区别是,一方面我提供了linux系统调用的读写方法,另一方面我使用的是fread / fwrite。
使用大小为10Mb的输入来定时我的应用程序并将其回显到/ dev / null,并确保文件没有被缓存,我发现当使用非常小的缓冲区时,libc的fwrite速度更快(如果是1个字节)。
这是我的时间输出,使用fwrite:
real 0m0.948s
user 0m0.780s
sys 0m0.012s
使用系统调用写:
real 0m8.607s
user 0m0.972s
sys 0m7.624s
我能想到的唯一可能性是内部libc已经在缓冲我的输入......不幸的是我在网上找不到那么多信息,所以也许这里的大师可以帮助我。
答案 0 :(得分:33)
使用输入来定时我的应用程序 大小为10Mb并与之呼应 / dev / null,并确保文件在 没有缓存,我发现了libc的 当大规模时,frwite的速度更快 使用非常小的缓冲区(1个字节) 情况)。
fwrite
适用于缓冲的流。因此,许多小缓冲区将更快,因为它不会运行昂贵的系统调用,直到缓冲区填满(或您冲洗它或关闭流)。另一方面,发送到write
的小缓冲区将对每个缓冲区进行代价高昂的系统调用 - 这就是你失去速度的地方。使用1024字节流缓冲区并写入1字节缓冲区,您将看到每个千字节的1024 write
次调用,而不是1024 fwrite
次调用变成一个write
调用1}} - 看到差异?
对于大缓冲区,差异会很小,因为缓冲会更少,因此fwrite
和write
之间的系统调用次数会更加一致。
换句话说,fwrite(3)
只是一个库例程,它将输出收集到块中,然后调用write(2)
。现在,write(2)
是系统调用,陷入内核。这就是I / O实际发生的地方。简单地调用内核有一些开销,然后有时间实际编写内容。如果你使用大缓冲区,你会发现write(2)
更快,因为它最终必须被调用,如果你每次写入一次或多次,那么fwrite缓冲开销就是:更多的开销。 / p>
如果您想了解更多相关信息,可以查看标准I / O流的this document。
答案 1 :(得分:15)
write
(2)是基本的内核操作。
fwrite
(3)是一个库函数,它在write
(2)之上添加缓冲。
对于小的(例如,一次一行)字节计数,fwrite
(3)更快,因为只是进行内核调用的开销。
对于大(块I / O)字节计数,write
(2)更快,因为它不需要缓冲,你必须在两种情况下调用内核。
如果您查看cp
(1)的来源,您将看不到任何缓冲。
最后,最后一个考虑因素是:ISO C vs Posix。像fwrite
这样的缓冲库函数在ISO C中指定,而像write
这样的内核调用是Posix。虽然许多系统声称Posix兼容性,特别是在试图获得政府合同的资格时,实际上它特定于类Unix系统。因此,缓冲的操作更便携。因此,Linux cp
肯定会使用write
,但必须跨平台工作的C程序可能必须使用fwrite。
答案 2 :(得分:11)
您还可以使用setbuf()函数禁用缓冲。当禁用缓冲时,如果不慢,fwrite()将与write()一样慢。
有关此主题的更多信息,请访问:http://www.gnu.org/s/libc/manual/html_node/Controlling-Buffering.html