我正在开发一个程序,可以处理100GB或更大的文件。文件包含可变长度的记录集。我已经有了第一个实现并运行,现在正在寻求提高性能,特别是在更有效地做I/O,因为输入文件被扫描了很多次。

是否有使用mmap()与通过c++的fstream库读取块的经验法则?我想做的是将大块从磁盘读入缓冲区,处理缓冲区中的完整记录,然后读取更多数据。

mmap()代码可能会变得非常混乱,因为mmap的块需要位于页面大小的边界上(我的理解),而记录可能位于页面边界上。使用fstreams,我可以只寻找记录的开始并重新开始读取,因为我们不局限于读取位于页面大小边界上的块。

如果不首先编写完整的实现,我如何在这两个选项之间做出决定呢?有什么经验法则(例如,mmap()快2倍)或简单的测试吗?


当前回答

I remember mapping a huge file containing a tree structure into memory years ago. I was amazed by the speed compared to normal de-serialization which involves lot of work in memory, like allocating tree nodes and setting pointers. So in fact I was comparing a single call to mmap (or its counterpart on Windows) against many (MANY) calls to operator new and constructor calls. For such kind of task, mmap is unbeatable compared to de-serialization. Of course one should look into boosts relocatable pointer for this.

其他回答

主要的性能成本是磁盘i/o。"mmap()"当然比istream快,但这种差异可能不明显,因为磁盘I / O将主导您的运行时。

我尝试了Ben Collins的代码片段(见上面/下面)来测试他的断言“mmap()快得多”,并没有发现可测量的差异。请看我对他的回答的评论。

我当然不建议逐个逐个地mmap每条记录,除非你的“记录”非常大——那样会非常慢,每条记录需要2个系统调用,而且可能会从磁盘内存缓存.....中丢失页面

在你的情况下,我认为mmap(), istream和低级的open()/read()调用都是相同的。在这些情况下,我建议使用mmap():

文件中有随机访问(而不是顺序访问)和 或者在文件中存在引用位置,以便某些页面可以映射进来,其他页面可以映射出去。这样操作系统就能最大限度地利用可用RAM。 或者,如果多个进程正在读取/处理同一个文件,那么mmap()非常有用,因为所有进程都共享相同的物理页面。

(顺便说一下-我喜欢mmap()/MapViewOfFile())。

这听起来像是多线程的一个很好的用例……我认为你可以很容易地设置一个线程读取数据,而其他(s)处理它。这可能是一种显著提高感知表现的方法。只是一个想法。

在我看来,使用mmap()“只是”使开发人员不必编写自己的缓存代码。在一个简单的“每读一次文件”的情况下,这并不难(尽管mlbrock指出,您仍然将内存副本保存到进程空间中),但如果您在文件中来回执行或跳过位等等,我相信内核开发人员在实现缓存方面可能比我做得更好……

也许您应该对文件进行预处理,这样每个记录都在一个单独的文件中(或者至少每个文件都是mmap可用的大小)。

另外,您能否在处理下一条记录之前完成每条记录的所有处理步骤?也许这样可以避免一些IO开销?

我很抱歉本·柯林斯丢失了他的滑动窗口mmap源代码。这在Boost中是很好的。

是的,映射文件要快得多。您实际上是在使用OS虚拟内存子系统来关联内存和磁盘,反之亦然。可以这样想:如果OS内核开发者可以让它更快,他们会的。因为这样做几乎使所有事情都更快:数据库、启动时间、程序加载时间等等。

滑动窗口方法实际上并不难,因为可以一次映射多个连续的页面。因此,记录的大小并不重要,只要最大的记录可以放入内存。重要的是做好簿记工作。

如果一个记录不是从getpagesize()边界开始,那么映射就必须从前一页开始。映射区域的长度从记录的第一个字节(如有必要向下舍入到getpagesize()的最近倍数)扩展到记录的最后一个字节(四舍五入到getpagesize()的最近倍数)。当您完成一条记录的处理后,您可以unmap()它,然后继续到下一条记录。

这在Windows下工作也很好,使用CreateFileMapping()和MapViewOfFile()(和GetSystemInfo()来获取SYSTEM_INFO。dwAllocationGranularity——不是SYSTEM_INFO.dwPageSize)。