【发布时间】:2012-03-28 13:52:55
【问题描述】:
我正在使用低级 I/O 函数“写入”在我的代码(Linux 上的 C 语言)中将一些数据写入磁盘。首先,我将数据累积在内存缓冲区中,然后在缓冲区已满时使用“写入”将数据写入磁盘。那么“写入”的最佳缓冲区大小是多少?根据我的测试,并不是越大越快,所以我在这里寻找答案。
【问题讨论】:
-
+1 提出了一个好问题......我一直想知道写入功能的正确大小......
我正在使用低级 I/O 函数“写入”在我的代码(Linux 上的 C 语言)中将一些数据写入磁盘。首先,我将数据累积在内存缓冲区中,然后在缓冲区已满时使用“写入”将数据写入磁盘。那么“写入”的最佳缓冲区大小是多少?根据我的测试,并不是越大越快,所以我在这里寻找答案。
【问题讨论】:
你可以使用<stdio.h>中定义的BUFSIZ
否则,请使用页面大小 sysconf(_SC_PAGESIZE) 的小倍数(例如,该值的两倍)。大多数 Linux 系统都有 4Kbytes 的页面(通常与文件系统块大小相同或小倍数)。
正如其他人回答的那样,使用mmap(2) 系统调用可能会有所帮助。 GNU 系统(例如 Linux)有一个扩展名:fopen 的第二个模式字符串可能包含后者 m,当这种情况发生时,GNU libc 会尝试使用 mmap。
如果您处理的数据几乎与您的 RAM(或一半)一样大,您可能还想使用 madvise(2) 来微调 mmap 的性能。
另请参阅this answer 以了解与您的问题非常相似的问题。 (您可以使用 64Kbytes 作为合理的缓冲区大小)。
【讨论】:
进行文件系统块大小的倍数的写入可能有一些优势,尤其是当您正在就地更新文件时。如果您向文件写入的块少于部分块,则操作系统必须读取旧块,合并新内容,然后将其写出。如果您按顺序快速写入小片段,则不一定会发生这种情况,因为更新将在稍后刷新的内存缓冲区上完成。尽管如此,如果每次写入操作都没有填充块(以及正确对齐的块:偏移量的块大小的倍数,偏移量是块大小的倍数),有时您可能会触发一些低效率。
mmap 不一定会解决传输大小的问题。如果您映射一个文件,然后将memcpy 一些数据放入映射中,则您正在使页面变脏。该页面必须在稍后的某个时间刷新:不确定何时。如果您创建另一个触及同一页面的memcpy,则该页面现在可能是干净的,而您又将其弄脏了。所以它被写了两次。页面大小的倍数的页面对齐副本将是可行的方法。
【讨论】:
“最佳”大小在很大程度上取决于底层文件系统。
stat 和 fstat 调用填充了一个数据结构 struct stat,其中包括以下字段:
blksize_t st_blksize; /* blocksize for file system I/O */
操作系统负责为 write() 块填充“合适的大小”。但是,使用“良好对齐”的内存调用 write() 也很重要(例如,malloc 调用的结果)。实现这一点的最简单方法是使用提供的<stdio.h> 流接口(带有FILE * 对象)。
使用mmap,就像这里的其他答案一样,在许多情况下也可以非常快。请注意,它不太适合某些类型的流(例如,套接字和管道)。
【讨论】:
writev 实现类似的效果,但它通常比它的价值更多——即使这样内核也往往必须执行相同的内存复制。)
st_blksize 中返回的值实际上不是最优的,那么内核就无法坚持到底。 :-)
这取决于 RAM、VM 等的数量以及正在写入的数据量。更一般的答案是对最适合您正在处理的负载的缓冲区进行基准测试,并使用最有效的缓冲区。
【讨论】:
您会希望它是 CPU 页面大小的倍数,以便尽可能高效地使用内存。
但理想情况下,您希望改用 mmap,这样您就不必自己处理缓冲区。
【讨论】:
mmap 全部都可以。您可以使用madvise 进行微调。
malloc 设置了一些东西而无法扩展到 300 megs。如果 mmap 是一个代替缓冲流 I/O 的好主意,那么 glibc 可能会以这种方式实现 FILE * 流。