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

提供相同的程序后,读取一个随机生成的input文件,并回显相同的string读取到输出。 唯一的区别是,一方面我提供从Linux系统调用读取和写入方法,另一方面我使用fread / fwrite。

定时我的应用程序的大小为10Mb的input,并将其回显到/ dev / null,并确保文件没有被caching,我发现libc的fwrite在使用非常小的缓冲区时速度更快(1字节案件)。

这是我的输出时间,使用fwrite:

real 0m0.948s user 0m0.780s sys 0m0.012s 

并使用系统调用写入:

 real 0m8.607s user 0m0.972s sys 0m7.624s 

我能想到的唯一可能是内部libc已经在缓冲我的input了……不幸的是,我无法在networking上find那么多的信息,所以也许这里的专家们可以帮助我。

使用10Mb大小的输入来定时我的应用程序并将其回显到/ dev / null,并确保文件没有被高速缓存,我发现libc的frwite在使用非常小的缓冲区时速度更快(1字节案件)。

fwrite在缓冲的流上工作。 因此,许多小缓冲区会更快,因为在缓冲区填满之前(或者清空它或关闭流),它不会运行昂贵的系统调用。 另一方面,发送到缓冲区的小缓冲区会为每个缓冲区运行一个昂贵的系统调用 – 这就是你正在失去速度的地方。 使用1024字节的流缓冲区,并写入1个字节的缓冲区,你看每个千字节的 1024个write调用,而不是1024个write调用变成一个write – 看到不同?

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

换句话说, fwrite(3)只是一个库函数,将输出汇总成块,然后调用write(2) 。 现在write(2) ,是一个陷入内核系统调用 。 这就是I / O实际发生的地方。 简单地调用内核有一些开销,然后有实际写入内容的时间。 如果使用大缓冲区,会发现write(2)更快,因为它最终必须被调用,而且如果每个fwrite写入一次或多次,那么fwrite缓冲开销就是:更多的开销。

如果你想了解更多的信息,你可以看一下这个文档 ,它解释了标准的I / O流。

write (2)是内核的基本操作。

fwrite (3)是一个库函数,在write (2)之上添加缓冲。

对于较小的(例如,一次一行)字节计数, fwrite (3)更快,因为只是执行内核调用的开销。

对于大块(块I / O)字节计数, write (2)更快,因为它不会缓冲,因此在这两种情况下都必须调用内核。

如果你看看cp (1)的源代码,你将看不到任何缓冲。

最后还有最后一个考虑:ISO C和Posix。 像fwrite这样的缓冲库函数是在ISO C中指定的,而像write这样的内核调用是Posix。 虽然许多系统声称Posix兼容性,特别是当试图符合政府合同时,实际上它是特定于类Unix系统。 所以,缓冲的操作更便携。 因此,一个Linux cp肯定会使用write但是一个需要跨平台工作的C程序可能不得不使用fwrite。

您也可以使用setbuf()函数禁用缓冲。 当缓冲被禁用时,如果不是较慢,则fwrite()将比write()慢。

有关此主题的更多信息,请参阅: http : //www.gnu.org/s/libc/manual/html_node/Controlling-Buffering.html