【问题标题】:what's the proper buffer size for 'write' function?“写入”功能的正确缓冲区大小是多少?
【发布时间】:2012-03-28 13:52:55
【问题描述】:

我正在使用低级 I/O 函数“写入”在我的代码(Linux 上的 C 语言)中将一些数据写入磁盘。首先,我将数据累积在内存缓冲区中,然后在缓冲区已满时使用“写入”将数据写入磁盘。那么“写入”的最佳缓冲区大小是多少?根据我的测试,并不是越大越快,所以我在这里寻找答案。

【问题讨论】:

标签: c linux file io


【解决方案1】:

你可以使用<stdio.h>中定义的BUFSIZ

否则,请使用页面大小 sysconf(_SC_PAGESIZE) 的小倍数(例如,该值的两倍)。大多数 Linux 系统都有 4Kbytes 的页面(通常与文件系统块大小相同或小倍数)。

正如其他人回答的那样,使用mmap(2) 系统调用可能会有所帮助。 GNU 系统(例如 Linux)有一个扩展名:fopen 的第二个模式字符串可能包含后者 m,当这种情况发生时,GNU libc 会尝试使用 mmap

如果您处理的数据几乎与您的 RAM(或一半)一样大,您可能还想使用 madvise(2) 来微调 mmap 的性能。

另请参阅this answer 以了解与您的问题非常相似的问题。 (您可以使用 64Kbytes 作为合理的缓冲区大小)。

【讨论】:

    【解决方案2】:

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

    mmap 不一定会解决传输大小的问题。如果您映射一个文件,然后将memcpy 一些数据放入映射中,则您正在使页面变脏。该页面必须在稍后的某个时间刷新:不确定何时。如果您创建另一个触及同一页面的memcpy,则该页面现在可能是干净的,而您又将其弄脏了。所以它被写了两次。页面大小的倍数的页面对齐副本将是可行的方法。

    【讨论】:

      【解决方案3】:

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

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

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

      操作系统负责为 write() 块填充“合适的大小”。但是,使用“良好对齐”的内存调用 write() 也很重要(例如,malloc 调用的结果)。实现这一点的最简单方法是使用提供的<stdio.h> 流接口(带有FILE * 对象)。

      使用mmap,就像这里的其他答案一样,在许多情况下也可以非常快。请注意,它不太适合某些类型的流(例如,套接字和管道)。

      【讨论】:

      • 你的意思是 fwrite 通常比使用适当缓冲区大小的 write 快?
      • 不一定“快于”,但它会根据需要在用户空间中进行快速块复制。此外,如果您需要从不同的位置编写一堆不同的短字符串,它会将它们聚集在一起,并在一次系统调用中将一个适当大小的块传递给内核。 (在某些情况下,您可以使用writev 实现类似的效果,但它通常比它的价值更多——即使这样内核也往往必须执行相同的内存复制。)
      • 这个值本来就是这个值,但事实证明,如果我使用这个值,它仍然会更慢,例如将数据从 A 复制到 B,因为我必须以这种方式进行更多的系统调用。
      • 如果在st_blksize 中返回的值实际上不是最优的,那么内核就无法坚持到底。 :-)
      【解决方案4】:

      这取决于 RAM、VM 等的数量以及正在写入的数据量。更一般的答案是对最适合您正在处理的负载的缓冲区进行基准测试,并使用最有效的缓冲区。

      【讨论】:

      • 考虑到所有这些情况,有什么好方法可以计算出适当的数量
      • 迭代缓冲区大小的不同设置并对其进行基准测试?
      • 那行得通,我想我想知道是否有某种基于 ram、vm exc 的公式
      【解决方案5】:

      您会希望它是 CPU 页面大小的倍数,以便尽可能高效地使用内存。

      但理想情况下,您希望改用 mmap,这样您就不必自己处理缓冲区。

      【讨论】:

      • 那么如果要写出3GB的数据,做个3GB的mmap就可以了?哈哈。您可以制作一个较小的 mmap,然后在您浏览文件时重新映射它,这更复杂。至于没有缓冲区:嗯,mmap 是什么?它是一个带有基指针的内存区域,以及一些当前指针,它告诉您在哪里存储下一块。这些 memcpy 操作的理想大小是多少?如果在这里复制 300 个字节,那里复制 300 个字节,您可能会触发次优刷新。 IE。 CPU 可以捕捉到你两次弄脏同一个页面并刷新它两次。
      • 如果要处理的数据远大于RAM怎么办?
      • 如果你有超过 3Gb 的 RAM 并且想要写入 3Gb 的数据,那么mmap 全部都可以。您可以使用madvise 进行微调。
      • 另外,如果你事先不知道文件有多大怎么办?你是做一个最坏情况的映射并让它扩展文件,还是做额外的 mmap 调用来扩展它?如果您通过多个步骤获取地图,如果有什么东西挡住了怎么办?现在在某个地址 200 megs 是可以的,但后来因为malloc 设置了一些东西而无法扩展到 300 megs。如果 mmap 是一个代替缓冲流 I/O 的好主意,那么 glibc 可能会以这种方式实现 FILE * 流。
      • mmap 可能不关心 RAM,但它关心虚拟空间。顺便说一句,严格的过量使用会计关心与 RAM 相关的虚拟空间!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-09-05
      • 2011-06-06
      • 2015-06-07
      • 1970-01-01
      • 2018-12-12
      • 1970-01-01
      • 2012-05-28
      相关资源
      最近更新 更多