mmap()vs read()

时间:2011-04-07 22:59:06

标签: c mmap

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

我已经阅读了这个非常有见地的帖子 - mmap() vs. reading blocks

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

TIA,

安德鲁

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

4 个答案:

答案 0 :(得分:16)

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

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

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

另一方面,

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

答案 1 :(得分:6)

除非您的代码受CPU限制,否则不要打扰mmap,特别是由于大量的读写操作。 mmap可能听起来不错,但不是真棒,为什么不是每个人都使用这个替代品。

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

Update0

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

答案 2 :(得分:3)

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

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

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

答案 3 :(得分:1)

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

int ftruncate(int fildes, off_t length);
int truncate(const char *path, off_t length);
unistd.h中定义的

,可用于截断指定长度的文件。通过这种方式,您可以轻松地

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

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