每次执行fread/fwrite会有多少随机/顺序访问?

3

我对C文件I/O有以下问题。

从物理层面(硬盘)来看,可以假设每次fread(n_blocks, size, length,FILE fp)操作的成本应该是第一页(块)的一个随机访问和下一个相同缓冲区的n-1个顺序访问吗?

我做出这个假设是因为操作系统有那么多进程,很可能其中一个进程也在每个本地程序的fread之间写入或读取文件,并且根据这个假设,硬盘会定位到另一个扇区/柱面。

这个假设正确吗?

5个回答

5
无论您认为与否,这是对实际情况的极度简化。
首先,您似乎认为第三个参数(length)对应于某些离散的“访问操作”的数量。事实并非如此。 fread 所做的只是读取 size*length 个字节;因此,只要乘法不溢出,以下三个调用执行的是完全相同的操作:
fread(n_blocks, size, length, fp);
fread(n_blocks, size*length, 1, fp);
fread(n_blocks, 1, size*length, fp);

实际上,fread/fwrite 会在您的进程的内存中读取和写入内部缓冲区。该缓冲区可以通过 setbuf/setvbuf 函数进行控制。当缓冲区已满/已空时,它们将把读/写操作转发到操作系统。操作系统有自己的文件缓存。如果您正在读取,而操作系统在缓存中找不到文件的某个部分,则程序将等待数据实际从驱动器获取。在写入时,数据将被复制到操作系统缓存中,并在操作系统决定将其写入驱动器之前一直驻留在那里,这可能会在您的程序关闭文件并退出很久以后才会发生。反过来,今天的硬盘具有自己的内部缓存,操作系统甚至可能意识不到。
对于所有实际目的,您不必关心每次调用fread/fwrite 将执行多少次驱动器访问。只需知道 C 语言、操作系统和底层硬件将尽最大努力尽快提供所请求的数据即可。但是,请记住,整个堆栈都针对顺序访问进行了优化。因此,避免没有充分理由就跳转到文件中的其他位置。

我开始理解I/O中隐藏的复杂性。谢谢。 - labotsirc

3
不,它并不是。如果文件系统碎片化,单个文件的块可能会分散在硬盘的各个位置。

你是对的。还有一件事让我困惑的是,一些网站声称连续读取可以提高顺序访问速度,这也应该是错误的假设,除非他们假设没有其他I/O进程的理想环境? - labotsirc
@Cristobal:他们可能认为所涉及的文件是连续存储的。现代文件系统确实尝试保持这个承诺,但在这方面并不完美。 - Fred Foo

1

不是这样的。你甚至不能假设 fread 将触发物理 I/O。你的操作系统可以对 I/O 请求进行大量操作,包括缓存结果、重新排序和合并(或分割)读取(甚至有时写入)。

如果有大量的 I/O 操作正在进行,根据你(和可能的 I/O 流库)使用的缓冲区大小,你也不能指望获得顺序读取。一些操作系统提供了在文件描述符上“提示”你将按顺序读取(或 mmap 区域),这可能会有所帮助。


感谢您的快速回复。我想知道如何获取我所拥有的C程序中进行了多少顺序和随机访问的信息。 - labotsirc
C语言本身并没有提供这方面的功能。您需要查看系统相关的I/O跟踪工具或API。 - Mat

1
从应用程序员的角度来看,读取块的确切过程是不确定的。这完全取决于磁盘调度程序,它组织来自多个进程的多个请求的访问操作。有多种算法可以解决这个问题, 但过于简单化(1个随机搜索,n个顺序搜索)根本不现实。最终,无论是C标准还是C++标准都没有为明显的原因定义这样的事情。

1

正如许多人所解释的那样,必须考虑缓存(可能在几个级别上)。

也许您想知道如何从C代码中加速或调整它。这是高度特定于操作系统的。

在最近的Linux系统上,您可以使用readaheadmadvise(带有mmap)和其他系统调用。

通常,您可以提前读取文件(可能只需使用cat yourfile > /dev/null),然后您的程序在Linux上运行速度会更快。

例如,尝试在某个大文件上两次运行wc单词计数实用程序。第二次运行通常比第一次快得多。


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