Win32内存映射文件的性能与CRT fopen/fread相比如何?

14

我需要按顺序读取(扫描)文件并处理其内容。文件的大小可以是从几KB到几GB的任何值。

我尝试了两种在Windows 7 64位上使用VC10/VS2010的技术:

  1. Win32内存映射文件(例如CreateFile、CreateFileMapping、MapViewOfFile等)
  2. CRT中的fopen和fread。

我认为内存映射文件技术可能比CRT函数更快,但是一些测试表明速度在两种情况下几乎相同。

以下C++语句用于MMF:

HANDLE hFile = CreateFile(
    filename,
    GENERIC_READ,
    FILE_SHARE_READ,
    NULL,
    OPEN_EXISTING,
    FILE_FLAG_SEQUENTIAL_SCAN,
    NULL
    );

HANDLE hFileMapping = CreateFileMapping(
    hFile,
    NULL,
    PAGE_READONLY,
    0,
    0,
    NULL
    );
文件是按顺序逐块读取的;每个块的大小为SYSTEM_INFO.dwAllocationGranularity
考虑到使用MMF和CRT函数速度几乎相同,我会使用CRT函数,因为它们更简单、跨平台。但我很好奇:我是否正确地使用了MMF技术?在这种情况下,MMF在连续扫描文件时的性能与CRT相同,这正常吗?
谢谢。
5个回答

15

我相信如果你按顺序访问文件,你不会看到太多的差异。因为文件I/O非常重度缓存,并且可能也使用了预读取。

如果在文件数据处理过程中有很多“跳跃”,情况就会有所不同。然后,每次设置新的文件指针并读取新的文件部分可能会杀死CRT,而MMF将为您提供最大可能的性能。


4

由于您是按顺序扫描文件,我不希望两种方法的磁盘使用模式有太大的区别。

对于大文件,MMF可能会降低数据局部性,甚至导致整个或部分文件被放置在页面文件中,而使用小缓冲区通过CRT处理将全部在RAM中进行。在这种情况下,MMF可能会更慢。您可以通过每次只映射底层文件的一部分来减轻这种情况,但这样会变得更加复杂,而没有任何可能比直接顺序I/O更快的优势。

MMF实际上是Windows实现进程间共享内存的方式,而不是加速通用文件I/O的方法。您真正需要利用的是内核中的文件管理器缓存。


4
为什么MMF会把东西放到页面文件中?MMF页面是由打开的数据文件支持的,而不是由页面文件支持的。 - Ben Voigt
我同意Ben的观点。将内容复制到页面文件中似乎不太可能。 - Joe
是的,通过使用MMF,页面文件被明确地避免了。但请注意问题:“当将大文件映射到虚拟内存以对其执行I/O操作时,请注意每个在虚拟内存中烧录的地址都是另一个无法由您的应用程序使用的地址。通常更有效的方法是使用常规文件I/O例程来执行大文件的读/写操作。”。 - Malcolm McCaffery

3
我认为内存映射文件技术可能比CRT函数更快,但一些测试显示两种情况下的速度几乎相同。
你可能正在进行文件系统缓存测试。除非您明确创建文件句柄以绕过文件系统缓存(在调用CreateFile时使用FILE_FLAG_NO_BUFFERING),否则文件系统缓存将启动并将最近访问的文件保存在内存中。
对于已经在文件系统缓存中开启了缓冲的文件进行读取会有一点速度差异,因为操作系统需要执行额外的复制以及系统调用开销。但是对于您的目的,您应该坚持使用CRT文件函数。
Gustavo Duarte在他的文章中(从通用OS角度)介绍了内存映射文件的相关知识。点击此处阅读

1
使用ReadFile
  • 进入内核模式
  • 从磁盘缓存中执行memcpy
  • 如果数据不在磁盘缓存中,则触发页面故障,使缓存管理器从磁盘读取数据。
  • 退出内核模式
  • 我测量时进入和退出内核模式的成本约为1600个CPU周期。
  • 避免小读操作,因为每次调用ReadFile都需要进入和退出内核模式的开销。

内存映射文件:

  • 基本上将磁盘缓存直接放在应用程序的地址空间中。
  • 如果数据在缓存中,则直接读取它。
  • 如果数据不在缓存中,则触发页面故障,使缓存管理器从磁盘读取数据。(需要进行用户/内核模式转换以处理此异常)
  • 磁盘读取并非总是成功的。您需要能够处理来自系统的内存异常,否则磁盘读取失败将导致应用程序崩溃。

因此,两种方法都将使用相同的磁盘缓存,并使用相同的获取数据到缓存的机制(页面故障异常->缓存管理器从磁盘读取数据)。缓存管理器还负责执行数据预取等操作,因此它可以一次读取多个页面。您不会在每个内存页面上都收到页面故障。

因此,内存映射文件的主要优点是:

  • 可能可以直接使用数据而无需先将其复制出来
  • 较少的用户<->内核模式转换(取决于访问模式)

缺点包括:

  • 需要处理访问违规异常以处理失败的磁盘读取
  • 在程序中占用地址空间以映射整个文件

1

这两种方法最终都会涉及到磁盘 I/O,这将成为您的瓶颈。我会选择一种更符合我的高级功能的方法-如果我需要流式传输,我会选择文件,如果我需要顺序访问和固定大小的文件,我会考虑内存映射文件。

或者,在您有一个仅在内存上运行的算法时,mem-mapped 文件可能是更简单的解决方案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接