C `mmap()`手动并发预故障/分页

C `mmap()`手动并发预故障/分页,c,linux,concurrency,mmap,page-fault,C,Linux,Concurrency,Mmap,Page Fault,我正在尝试微调mmap(),以对可能非常大的文件执行快速写入或读取(通常不是同时执行)。写入和读取在一个过程中基本上是连续的,在以后的过程中可能非常稀疏。无需多次访问内存区域 换言之,可以将其视为具有某种损失的文件传输,并以异步方式进行修复 正如预期的那样,mmap()性能的主要限制似乎是它在大文件上生成的小页面错误的数量。此外,我怀疑Linux内核的页到磁盘的惰性导致了一些性能问题。也就是说,任何最终对mmaped内存执行大量写入的测试程序似乎在执行所有写入操作后需要很长时间才能终止/code

我正在尝试微调
mmap()
,以对可能非常大的文件执行快速写入或读取(通常不是同时执行)。写入和读取在一个过程中基本上是连续的,在以后的过程中可能非常稀疏。无需多次访问内存区域

换言之,可以将其视为具有某种损失的文件传输,并以异步方式进行修复

正如预期的那样,
mmap()
性能的主要限制似乎是它在大文件上生成的小页面错误的数量。此外,我怀疑Linux内核的页到磁盘的惰性导致了一些性能问题。也就是说,任何最终对
mmap
ed内存执行大量写入的测试程序似乎在执行所有写入操作后需要很长时间才能终止/code>munmap内存

我希望通过在执行几乎连续的访问和分页的同时并发地预故障页面来抵消这些错误的代价,而我不再需要这些页面了。但关于这种方法和我对问题的理解,我有三个主要问题:

  • 是否有一种直接(最好是POSIX[或至少OSX]兼容)的方式来执行部分预故障?我知道
    MAP\u POPULATE
    标志,但这似乎试图将整个文件加载到内存中,这在许多情况下是不可容忍的。此外,这似乎会导致
    mmap()
    调用阻塞,直到预故障完成,这也是不可容忍的。我对手动替代方案的想法是生成一个线程,简单地尝试读取内存中的下一个
    N
    页面以强制预取。但实际上,
    madvise
    MADV_SEQUENTIAL
    可能已经做到了这一点
  • msync()
    可用于刷新对磁盘的更改。然而,定期这样做真的有用吗?我的想法是,如果程序经常处于磁盘IO的“空闲”状态,并且能够压缩一些磁盘写回,那么它可能会很有用。而且,内核本身可能比应用程序更好地处理这个问题
  • 我对磁盘IO的理解是否准确?我的假设是,预故障和读/写页面可以由不同的线程或进程同时完成;如果我错了,那么手动预故障将毫无用处。类似地,如果一个
    msync()
    调用阻塞了所有磁盘IO,包括到文件系统缓存和原始文件系统的IO,那么在程序终止时使用它来过度刷新整个磁盘缓存也没有多大的动机
  • 正如预期的那样,mmap()性能的主要限制似乎是它在大文件上生成的小页面错误的数量

    我同意,这并不特别令人惊讶。但这是无法避免的成本,至少对于与您实际访问的映射文件区域相对应的页面而言是如此

    此外,我怀疑Linux内核的页到磁盘的惰性导致了一些性能问题。也就是说,任何最终对mmaped内存执行大量写操作的测试程序在执行所有对terminate/munmap内存的写操作之后似乎需要很长时间

    这是有道理的。同样,这是一个不可避免的成本,至少对于脏页来说是如此,但您可以对这些成本的产生施加一定的影响

    我希望通过以下方式抵消这些故障的成本: 在执行几乎连续的访问和 调出我不再需要的页面。但我有三个主要问题 有关此方法的问题以及我对该问题的理解:

  • 是否有一种直接(最好是POSIX[或至少OSX]兼容)的方式来执行部分预故障?我知道
    MAP\u POPULATE
    标志,但这似乎试图加载整个文件 进入记忆
  • 是的,这与它的文档是一致的

    这在许多情况下是无法容忍的。而且,这似乎 导致
    mmap()
    调用阻塞,直到预故障完成

    这也是有记录的

    哪个 这也是不能容忍的。我对手动替代方案的想法是产生一个 线程只是尝试读取内存中接下来的N页,以强制 预取

    除非您最初
    mmap()
    文件与开始访问映射之间存在延迟,否则我不清楚您为什么希望这会带来任何改进

    但它可能已经是带有
    MADV_SEQUENTIAL
    的madvise了 事实上,这是真的

    如果您想要POSIX兼容性,那么您需要寻找
    POSIX\u madvise()
    。我确实建议您使用这个函数,而不是尝试使用您自己的用户空间替代方案。特别是,如果您使用
    posix_madvise()
    在部分或全部映射区域上断言
    posix_madvise\u SEQUENTIAL
    ,则有理由希望内核在需要页面之前先读取以加载页面。此外,如果您建议使用
    POSIX_MADV_DONTNEED
    ,那么您可能会根据内核的判断提前同步到磁盘,并减少总体内存使用。如果这个机制有用的话,您还可以通过它传递其他建议

  • msync()
    可用于刷新对磁盘的更改。然而,定期这样做真的有用吗?我的想法是它可能 如果程序经常处于磁盘IO的“空闲”状态,则此选项非常有用 并且可以压缩一些磁盘写回。再说一次 内核可能会比以前更好地处理这个问题 申请可以
  • 这是需要检验的。请注意,
    msync()
    支持异步同步,因此不需要I/O空闲。因此,当你确信你完成了一个给定的页面,你可以考虑< COD