加速文件I / O:mmap()与read()

时间:2011-11-08 20:57:14

标签: c++ c file-io mmap

我有一个Linux应用程序并行读取150-200个文件(4-10GB)。每个文件依次以小的,可变大小的块读取,每个块通常少于2K。

我目前需要从文件集中保持超过200 MB / s的读取速率。磁盘处理这个很好。预计需要超过1 GB / s(目前不在磁盘范围内)。

我们已经实现了两个不同的读取系统,它们都大量使用posix_advise:首先是mmap读取,我们在其中映射整个数据集并按需读取。 第二个是基于read() / seek()的系统。

两者都运行良好,但仅适用于中等情况,read()方法可以更好地管理我们的整体文件缓存,并且可以很好地处理100 GB的文件,但速度受限,mmap是能够预缓存数据使得持续数据速率超过200MB / s易于维护,但无法处理大的总数据集大小。

所以我的问题是:

答:read()类型文件i / o可以进一步优化,超出Linux上的posix_advise调用,或调整磁盘调度程序,VMM和posix_advise调用是否符合我们的预期?

B:mmap是否有系统的方法来更好地处理非常大的映射数据?

Mmap-vs-reading-blocks 与我正在工作的问题类似,并提供了一个关于这个问题的良好起点,以及mmap-vs-read中的讨论。

3 个答案:

答案 0 :(得分:14)

回读什么?这个数据的最终目的地是什么?

由于听起来你完全是IO界限,mmapread应该没有区别。有趣的部分在于如何将数据传输到接收器。

假设您将此数据放入管道,我建议您将每个文件的内容全部转储到管道中。要使用零拷贝执行此操作,请尝试splice系统调用。您也可以尝试手动复制文件,或者分支cat的实例或其他一些工具,这些工具可以使用当前文件作为stdin进行大量缓冲,并将管道作为标准输出。

if (pid = fork()) {
    waitpid(pid, ...);
} else {
    dup2(dest, 1);
    dup2(source, 0);
    execlp("cat", "cat");
}

Update0

如果您的处理与文件无关,并且不需要随机访问,则需要使用上面列出的选项创建管道。您的处理步骤应该接受来自stdin或管道的数据。

回答更具体的问题:

  

答:read()类型文件i / o可以在Linux上的posix_advise调用之外进一步优化,或者调整了磁盘调度程序,VMM和posix_advise调用是否达到了我们的预期效果?

关于告诉内核如何从用户空间做什么,这就好了。剩下的由您自己决定:缓冲,线程等等,但它是危险的,可能是非生产性的猜测工作。我只是将文件拼接成一个管道。

  

B:有没有系统的方法让mmap更好地处理非常大的映射数据?

是。 following options可能会给您带来很棒的性能优势(并且可能会使mmap值得使用而不是通过测试):

  • MAP_HUGETLB 使用“巨大页面”分配映射。

    这将减少内核中的分页开销,如果您要映射千兆字节大小的文件,这将是很好的。

  • MAP_NORESERVE 不要为此映射保留交换空间。保留交换空间时,可以保证可以修改映射。如果没有保留交换空间,如果没有可用的物理内存,则可能在写入时获得SIGSEGV。

    如果您实际上没有足够的物理内存+交换来完成整个映射,这可以防止内存不足,同时保持实现简单。**

  • MAP_POPULATE 填充(prefault)页表以进行映射。对于文件映射,这会导致对文件进行预读。以后访问映射不会被页面错误阻止。

    这可能会为您提供足够的硬件资源,并且如果预订是有序的,则是懒惰的。我怀疑这个标志是多余的,默认情况下VFS可能做得更好。

答案 1 :(得分:4)

如果您的程序可以预先预测它想要读取的文件片段,那么使用readahead系统调用可能会有所帮助(但这只是猜测,我可能是错的)。

我认为你应该调整你的应用程序,甚至是你的算法,来读取大于几千字节的数据块。而不是半兆字节?

答案 2 :(得分:1)

这里的问题似乎不是使用哪个api。如果你使用mmap()或read()并不重要,光盘仍然必须寻找指定点并读取数据(尽管os确实有助于优化访问)。

如果你读取非常小的块(几个字节),那么

mmap()优于read(),因为你没有为每个块调用os,这变得非常慢。

我也会建议像Basile那样连续读取超过2kb的光盘,这样光盘就不必经常寻找。