C 磁盘 I/O - 在文件的相同偏移量处读取后写入将使读取吞吐量非常低

Posted

技术标签:

【中文标题】C 磁盘 I/O - 在文件的相同偏移量处读取后写入将使读取吞吐量非常低【英文标题】:C Disk I/O - write after read at the same offset of a file will make read throughput very low 【发布时间】:2015-12-20 13:14:03 【问题描述】:

背景:

我正在开发一个与数据库相关的程序,我需要按顺序将脏元数据从内存刷新到磁盘。 /dev/sda1 是卷格式,因此 /dev/sda1 上的数据将被逐块访问,如果顺序访问,这些块在物理上是相邻的。 而且我使用的是直接I/O,所以I/O会绕过文件系统的缓存机制,直接访问磁盘上的块。

问题:

打开/dev/sda1后,我会读取一个块,更新块,然后将块写回与/dev/sda1开头相同的偏移量,迭代。

代码如下 -

//block_size = 256KB
int file = open("/dev/sda1", O_RDWR|O_LARGEFILE|O_DIRECT);
for(int i=0; i<N; i++) 
    pread(file, buffer, block_size, i*block_size);
    // Update the buffer
    pwrite(file, buffer, block_size, i*block_size);

我发现如果我不做 pwrite,读取吞吐量是 125 MB/s

如果我执行 pwrite,读取吞吐量将为 21 MB/s,写入吞吐量为 169 MB/s

如果我在 pwrite 之后进行预读,写入吞吐量为 115 MB/s,读取吞吐量为 208 MB/s

我也试过 read()/write() 和 aio_read()/aio_write(),但问题依旧。我不知道为什么在文件的同一位置读取后写入会导致读取吞吐量如此之低。

如果一次访问多个块,像这样

pread(file, buffer, num_blocks * block_size, i*block_size);

问题会得到缓解,请参阅chart。

【问题讨论】:

你的块大小是多少?您很有可能会看到硬件缓存和预读对您正在访问的磁盘的影响。 pwrite() 填充缓存,如果下一个pread() 用于不同的数据,则不会缓存。在pwrite() 之后执行pread() 可以直接从磁盘的硬件缓存中读取数据。 不知道物理块大小,我在程序中设置为256KB。感谢您的评论,现在我认为这很可能是由磁盘缓冲区引起的。 【参考方案1】:

而且我使用的是直接I/O,所以I/O会绕过文件系统的缓存机制,直接访问磁盘上的块。

如果设备上没有文件系统,直接使用设备读写,则没有文件系统缓存进入画面。

您观察到的行为是典型的磁盘访问和 IO 行为。

我发现如果不做pwrite,读吞吐量是125 MB/s

原因:磁盘只是读数据,不用回offset再写数据,少1次操作。

如果我执行 pwrite,读取吞吐量将为 21 MB/s,写入吞吐量为 169 MB/s。

原因:您的磁盘可能有更好的写入速度,可能磁盘缓冲区正在缓存写入而不是直接命中媒体。

如果我在 pwrite 之后进行 pread,写入吞吐量为 115 MB/s,读取吞吐量为 208 MB/s。

原因:写入的数据很可能在磁盘级别缓存,因此读取从缓存而不是媒体获取数据。

要获得最佳性能,您应该一次使用异步 IO 和块数。但是,您必须使用合理数量的块,并且不能使用非常大的数量。应该通过反复试验找出最佳方案。

【讨论】:

感谢您的回答,现在我认为这很可能是由磁盘缓冲区引起的。但我仍然无法想象仅仅寻求上一个位置会让读取吞吐量从 125 MB/s 下降到 21 MB/s... @leo,是的,寻找是昂贵的。查看吞吐量下降时会增加的 IO 等待时间。

以上是关于C 磁盘 I/O - 在文件的相同偏移量处读取后写入将使读取吞吐量非常低的主要内容,如果未能解决你的问题,请参考以下文章

Linux中文件I/O函数

C++ 宏在自定义偏移量处设置类字段

pread中偏移的时间复杂度?

在给定偏移量处将音频剪辑插入原始音频文件

在磁盘上读取/写入文件时如何限制硬盘 I/O?

以编程方式确定在一天/时间的偏移量处使用 UTC 时间