为什么fwrite libc函数比syscall写函数更快?

时间:2009-09-01 00:21:01

标签: c linux

提供相同的程序,该程序读取随机生成的输入文件并回显它读取到输出的相同字符串。唯一的区别是,一方面我提供了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已经在缓冲我的输入......不幸的是我在网上找不到那么多信息,所以也许这里的大师可以帮助我。

3 个答案:

答案 0 :(得分:33)

  

使用输入来定时我的应用程序   大小为10Mb并与之呼应   / dev / null,并确保文件在   没有缓存,我发现了libc的   当大规模时,frwite的速度更快   使用非常小的缓冲区(1个字节)   情况)。

fwrite适用于缓冲的流。因此,许多小缓冲区将更快,因为它不会运行昂贵的系统调用,直到缓冲区填满(或您冲洗它或关闭流)。另一方面,发送到write的小缓冲区将对每个缓冲区进行代价高昂的系统调用 - 这就是你失去速度的地方。使用1024字节流缓冲区并写入1字节缓冲区,您将看到每个千字节的1024 write次调用,而不是1024 fwrite次调用变成一个write调用1}} - 看到差异?

对于大缓冲区,差异会很小,因为缓冲会更少,因此fwritewrite之间的系统调用次数会更加一致。

换句话说,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