mmap()vs read()

我正在用C编写批量ID3标签编辑器.ID3标签通常位于mp3编码文件的开头,尽管旧的(版本1)标签在最后。 该应用程序旨在接受命令行中的目录和帧ID列表,然后递归目录结构,更新它找到的所有ID3标记。 用户可另外选择删除所有旧(版本1)标签。 另一种选择是简单地显示当前标签,而不执行更新。 该目录可能包含2个文件或200万个文件。 如果用户意味着更新文件,我打算将整个文件加载到内存中,执行更新,然后保存(文件也可以重命名)。 但是,如果用户仅表示打印当前的ID3标签,则加载整个文件似乎过多。 毕竟文件可能是200mb。

我已经阅读了这个线程,这是有见地的 – mmap()与阅读块

所以我的问题是,最有效的方法是什么 – read(),mmap()或某种组合? 欢迎设计理念。

TIA,

安德鲁

编辑:我的理解是mmap基本上委托将文件加载到内存中,并加载到虚拟内存子系统。 在我看来,VMM将在大多数系统上进行高度优化,因为它对系统性能至关重要。

这取决于你想要做什么。 如果你需要做的只是跳到一个已知的偏移并读出一个小标签, read()可能会更快( mmap()必须做一些相当复杂的内部记帐)。 但是,如果您计划复制所有200mb的MP3,或者扫描一些可能出现在未知偏移处的标签,那么mmap()可能是一种更快的方法。

例如,如果您需要将整个文件向下移动几百个字节以插入ID3标记,一种简单的方法是使用ftruncate()扩展文件,mmap文件,然后memmove()内容下来位。 但是,如果程序在运行时崩溃,则会破坏该文件。 您还可以将文件的内容复制到一个新文件中 – 这是mmap()真正闪耀的另一个地方; 你可以简单地mmap()旧文件,然后用一个write()将其所有数据复制到新文件中。

简而言之,如果你在传输的总字节数方面做了大量的IO, mmap()就很棒了。 这是因为它减少了所需的副本数量,并且可以显着减少读取缓存数据所需的内核条目数。 但是mmap()需要至少两次进入内核(如果你在完成后清理映射,则需要三次!)并执行一些复杂的内部内核计算,因此固定开销可能很高。

另一方面, read()涉及额外的内存到内存副本,因此对于大型I / O操作来说效率低,但是很简单,因此固定开销相对较低。 简而言之,对于大容量I / O使用mmap()对于一次性小I / O使用read()pread()

除非你的代码是CPU绑定的,否则不要打扰mmap ,特别是由于大量的读写操作。 mmap可能听起来不错,但它不是很棒的为什么不是每个人都使用这个替代品看起来像。

鉴于您正在通过潜在的大型目录结构进行递归,您的瓶颈将是目录IO和并发性。 mmap无济于事。

Update0

阅读链接到问题找到支持我的经验的答案:

  • mmap()与阅读块

如果您通常不会将文件流式传输然后进行处理,而是跳来跳去(比如读取前面的标签,然后跳到最后等),那么我会使用mmap只是因为你的代码会更清洁,更容易维护将文件视为大缓冲区,而无需实际管理缓冲和分页。

如前所述,如果您正在处理大量数据磁盘,I / O可能会主导您的处理。 mmap可能比读取更快,但是对于合理的实现,它可能不会快得多,特别是在今天的硬件上,这种硬件不断变得越来越快,而磁盘驱动器已经被锁定在7200和10,000 RPM多年。

因此,请使用mmap并使代码简单明了。

我不知道标准POSIX函数是否存在于您允许的范围内,或者您将用于开发但是请考虑这两个函数:

 int ftruncate(int fildes, off_t length); int truncate(const char *path, off_t length); 

unistd.h定义,可用于截断指定长度的文件。 这样你很容易

  • 找到ID3标签框架开始的地方(不知道你是否可以通过阅读MP3文件的标题轻松计算它,但我想是的)
  • 保存偏移量
  • 关闭文件
  • 使用提供的函数截断文件
  • 以追加二进制模式打开文件并编写新标签

我不确定性能,你应该测试这个方法,但它应该在ram中加载更少的东西,同时提供一种有意义的方法。