mmap()vs read()

J. *_*lin 12 c mmap

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

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

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

TIA,

安德鲁

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

bdo*_*lan 16

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

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

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

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


Mat*_*ner 6

mmap除非您的代码受CPU限制,否则请不要理会,特别是由于大量的读写操作.mmap可能听起来不错,但它并不是很棒的为什么不是每个人都使用这种替代品看起来像.

鉴于您正在通过潜在的大型目录结构进行递归,您的瓶颈将是目录IO和并发.mmap没有帮助.

Update0

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


Jac*_*ack 0

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

int ftruncate(int fildes, off_t length);
int truncate(const char *path, off_t length);
Run Code Online (Sandbox Code Playgroud)

中定义unistd.h,可用于将文件截断至指定长度。这样你就可以轻松地

  • 找到 ID3 标签帧开始的位置(不知道是否可以通过读取 MP3 文件的标头轻松计算它,但我想是的)
  • 保存偏移量
  • 关闭文件
  • 使用提供的函数截断文件
  • 以附加二进制模式打开文件并写入新标签

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