“write”函数的适当缓冲区大小是多少?

18

我在我的代码中使用底层I/O函数'write'将一些数据写入磁盘(Linux系统的C语言)。首先,我将数据累积在内存缓冲区中,然后当缓冲区满时使用'write'将数据写入磁盘。那么什么是最好的'write'缓冲区大小呢?根据我的测试,不是越大越快,所以我来寻找答案。


+1 鼓励你提出这样好的问题... 我一直想知道 write 函数的正确大小... - Amit
1
可能是write(2)的最佳缓冲区大小的重复问题。 - Raedwald
5个回答

3

为了尽可能高效地使用内存,您需要将其设置为CPU页面大小的倍数。

但最好使用mmap,这样您就永远不必自己处理缓冲区。


如果您拥有超过3GB的RAM并且想要写入3GB的数据,使用mmap映射所有数据肯定没问题。您可以使用madvise进行微调。 - Basile Starynkevitch
另外,如果您事先不知道文件的大小怎么办?您会进行最坏情况的映射并让它扩展文件,还是进行额外的mmap调用来扩展它?如果您分多个步骤获取地图,如果有什么阻碍怎么办?200兆字节在某个地址上很好,但稍后无法扩展到300兆字节,因为malloc放置了一些东西。如果mmap代替缓冲流I/O是一个好主意,也许glibc会以这种方式实现FILE *流。 - Kaz
3
mmap 可能不关心内存,但它关心虚拟空间。严格的过度承诺计算与 RAM 相关时,也关心虚拟空间! - Kaz
Glibc 可能会使用 mmap,例如在包含 m 的模式下使用 fopen - Basile Starynkevitch
你必须向glibc请求它;这是什么原因? - Kaz
显示剩余4条评论

3
您可以使用在<stdio.h>中定义的BUFSIZ
否则,请使用页面大小sysconf(_SC_PAGESIZE)的小倍数(例如,该值的两倍)。大多数Linux系统都有4K字节的页面(通常与文件系统块大小相同或是其小倍数)。
正如其他回答所述,使用mmap(2)系统调用可能会有帮助。GNU系统(例如Linux)具有扩展功能: fopen的第二种模式字符串可能包含后者的m,当发生这种情况时,GNU libc尝试进行mmap
如果您处理的数据几乎与RAM一样大(或其一半),您可能还想使用madvise(2)来微调mmap的性能。
另请参见此答案中与您的问题非常相似的问题。 (您可以使用64K字节作为合理的缓冲区大小)。

3

在进行写操作时,尤其是就地更新文件时,使用文件系统块大小的倍数可能会有一些优势。如果你向文件中写入少于一个块部分的内容,则操作系统必须读取旧块,将新内容合并到其中,然后写出。如果你按顺序快速写入小块,则不一定会发生这种情况,因为更新将在稍后刷新的内存缓冲区上完成。但是,如果你没有每次写操作都填满(并且填满一个正确对齐的块:以块大小的倍数偏移),偶尔会触发一些效率低下的情况。

传输大小的这个问题并不一定会通过映射消失。如果你映射一个文件,然后memcpy一些数据到映射中,你就使一个页面变脏了。该页必须在某个时间点之后刷新:但是具体什么时间点是不确定的。如果你再进行另一个memcpy操作,并且触及同一页,则该页面现在可能已经是干净状态,而你又使它变脏了。因此,它将被写入两次。对齐页面大小并且是页面大小倍数的复制将是最好的方式。


2

"最佳"大小很大程度上取决于底层文件系统。

statfstat 调用填充一个数据结构 struct stat,其中包括以下字段:

blksize_t st_blksize; /* blocksize for file system I/O */

操作系统负责填充此字段以获取适当的"写入块"大小。然而,调用write()时使用"对齐良好"的内存也很重要(例如,malloc()调用的结果)。最简单的方法是使用提供的<stdio.h>流接口(使用FILE *对象)来实现这一点。
使用mmap(如其他答案中所述)在许多情况下也可以非常快速。但请注意,它不适用于某些类型的流(例如套接字和管道)。

你的意思是说,使用适当的缓冲区大小,fwrite通常比write更快吗? - Mickey Shine
不一定比其他方式更快,但它可以在需要时在用户空间进行快速块复制。此外,如果您需要从各个位置写入许多不同的短字符串,则它将把它们全部收集在一起,并通过一个适当大小的块传递给内核,在一个系统调用中完成。(在某些情况下,您可以使用writev来实现类似的效果,但通常这样做的工作量更大,甚至内核也必须执行相同的内存复制操作。) - torek
这个值本来应该是精确的,但事实证明,如果我使用这个值,例如从A复制数据到B,仍然会更慢,因为我必须以这种方式进行更多的系统调用。 - glglgl
如果st_blksize返回的值实际上不是最优的,那么内核就没有履行它的承诺。 :-) - torek

1

这取决于RAM,VM等的数量以及要写入的数据量。更一般的答案是测试何种缓冲区对你所处理的负载效果最佳,然后使用最佳的方案。


1
怎样计算出适当的数量是一个好方法,考虑到所有这些因素? - pyCthon
遍历不同的缓冲区大小设置并进行基准测试? - Waynn Lue
我猜那个可行,不过我在想是否有一种基于内存、虚拟机等的公式。 - pyCthon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接