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

22 c linux

提供相同的程序,该程序读取随机生成的输入文件并回显相同的字符串,然后将其读取到输出.唯一的区别是,在一边,我提供的读取和写入从Linux系统调用的方法,并在另一边我用FREAD/FWRITE.

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

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

real    0m0.948s
user    0m0.780s
sys     0m0.012s
Run Code Online (Sandbox Code Playgroud)

并使用syscall写:

real    0m8.607s
user    0m0.972s
sys     0m7.624s
Run Code Online (Sandbox Code Playgroud)

我能想到的唯一可能性是内部libc已经缓冲了我的输入......不幸的是我无法在网络上找到那么多信息,所以也许这里的大师可以帮助我.

Mat*_*lin 35

使用大小为10Mb的输入对应用程序进行定时并将其回显到/ dev/null,并确保文件未缓存,我发现当使用非常小的缓冲区时,libc的frwite速度更快(1个字节)案件).

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

对于大缓冲区,差异会很小,因为缓冲会更少,因此在fwrite和之间的系统调用数量会更加一致write.

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

如果您想了解更多相关信息,可以查看本文档,该文档解释了标准I/O流.


Dig*_*oss 15

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

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

对于小的(例如,一次一行)字节计数,fwrite(3)更快,因为仅仅进行内核调用的开销.

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

如果你看源cp(1),你将看不到任何缓冲.

最后,最后一个考虑因素是:ISO C vs Posix.缓冲的库函数fwrite在ISO C中指定,而内核调用如writePosix.虽然许多系统声称Posix兼容性,特别是在试图获得政府合同的资格时,实际上它特定于类Unix系统.因此,缓冲的操作更便携.因此,Linux cp肯定会使用,write但必须跨平台工作的C程序可能必须使用fwrite.


dmi*_*gov 11

您还可以使用setbuf()函数禁用缓冲.当禁用缓冲时,如果不慢,fwrite()将与write()一样慢.

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