【问题标题】:C fread() and buffer managementC fread() 和缓冲区管理
【发布时间】:2018-05-01 15:03:17
【问题描述】:

假设我正在解析一个二进制记录文件。该文件可以包含任意数量的记录。格式为:

  • 12 字节 C 字符串
  • 4 字节长度
  • 4 字节偏移

我打算使用fread() 从文件中获取记录,并将它们转换为内部数据结构。据我了解,fread() 在数据文件上拥有自己的内部缓冲区,以减少小读取的硬盘访问。

所以有两种选择:

  • 循环:使用fread()一次读取20个字节并解析它。这很简单,但缺点是我在每个循环中都对fread() 进行函数调用。它不像系统调用那么糟糕,但它仍然每 20 个字节产生开销。在另一种极端情况下(例如,一次一个字节的文件解压缩)我可能会执行一个函数调用每个字节
  • 使用fread()一次读取一个大块,然后循环:一次遍历我的副本20个字节,并解析它。解析本地内存很快,但这有一个缺点,即我正在“缓冲一个缓冲区”——即填充内部的fread() 缓冲区,这样我就可以转身并将memcpy() 它转到另一个结构。此外,这有点乏味 - 要么我必须自己将整个文件放入 RAM,要么我必须管理自己的缓冲区填充和刷新例程来分块读取文件。

所以我在这里有点纠结,因为理论上这两种解决方案似乎都不是最优的!

我的问题是:是否有任何使用顺序文件 IO 的模式可以避免这两个这些缺点?(请不要“仅分析它”cmets -问题不是“哪个更好”而是“是否有第三种选择”)

【问题讨论】:

  • “第三种选择”取决于操作系统,例如mmap() 在 POSIX 系统上。如果你想坚持使用 C stdio,只需使用简单的方法,让 stdio 处理缓冲。
  • 引用:no "just profile it" comments 为什么不呢?您可能正在“发明”一个不真实的问题。您可能会发现,与磁盘访问时间相比,您在 SW 中所做的任何事情都无关紧要。在现代系统中几乎不可能猜测性能。
  • 第四个选择是使用read并管理你自己的缓冲区。
  • @melpomene 也依赖于操作系统(我猜你的意思是 POSIX 函数 read()
  • 了解 setvbuf 和 _IONBF,然后使用 fread。

标签: c optimization io fread stdio


【解决方案1】:

所以我在这里有点纠结,因为理论上这两种解决方案似乎都不是最优的!

请不要“仅描述它”cmets - 问题不是“其中哪个更好”而是“是否有第三种选择”)

嗯,大多数系统都有fread 的替代品。例如内存映射文件、异步 IO、read()。此外,通常有许多方法可以“配置”不同类型的文件访问,例如对于内存映射文件,您可以选择填充整个文件文件。

但是,没有一种方法可以被认为是更好的解决方案。无论你采用哪种方法,你都会得到:

简单介绍一下

对于你写的方法1:

这很简单,但缺点是我在每个循环中都对 fread() 进行函数调用。

这与“理论上似乎是最优的”一起是这里的关键。你在没有证据的情况下做出假设。你不能只用“看起来”来判断表现。

在现代计算机系统上几乎不可能仅通过查看 C 代码来猜测性能,因为在编译和执行期间都会发生极端优化。

你怎么知道每 20 个字节调用一次fread 与非常慢的磁盘访问相比是一个问题?

再次,它带您回到:

简单介绍一下

只是为了好玩,我使用fread 和不同数量的 nmemb 在我的 Linux 机器上做了一些简单的基准测试。该程序读取一个 1G 的文件,并在每个块读取中使用 1 个字节计算一个简单的运行总和。

文件冷(即未缓存)时的结果

nmemb        time (sec)
   1             15.68
   2             15.76
   4             15.91
   8             15.93
  20             15.60
1000             15.60

文件很热(即缓存)时的结果

nmemb        time (sec)
   1            13.90
   2             7.04
   4             3.79
   8             2.07
  20             1.09
1000             0.42

所以在我的系统上,当文件冷时,块大小 (nmemb) 并不重要。对于大多数应用程序,我会认为冷文件是“正常情况”,但可以肯定的是,有些应用程序可能会多次读取同一个文件。

如果文件很热(即由于先前的读取而被缓存),则块大小很重要。但是随着块大小的增加,差异会迅速减小。已经在 20 对 1000 的差异非常小。

从上面我得出的结论是:每次只需读取 20 个字节以保持代码简单。

以上所有测量/结论对我的特定系统有效。另一个系统可能会产生完全不同的结果,具体取决于 CPU 和磁盘系统和操作系统的类型/速度。

所以再一次,它让我们回到:

简单介绍一下

【讨论】:

    猜你喜欢
    • 2013-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-09
    • 2013-01-16
    • 2022-01-03
    • 2017-07-12
    • 1970-01-01
    相关资源
    最近更新 更多