我正在开发一个程序,可以处理100GB或更大的文件。文件包含可变长度的记录集。我已经有了第一个实现并运行,现在正在寻求提高性能,特别是在更有效地做I/O,因为输入文件被扫描了很多次。
是否有使用mmap()与通过c++的fstream库读取块的经验法则?我想做的是将大块从磁盘读入缓冲区,处理缓冲区中的完整记录,然后读取更多数据。
mmap()代码可能会变得非常混乱,因为mmap的块需要位于页面大小的边界上(我的理解),而记录可能位于页面边界上。使用fstreams,我可以只寻找记录的开始并重新开始读取,因为我们不局限于读取位于页面大小边界上的块。
如果不首先编写完整的实现,我如何在这两个选项之间做出决定呢?有什么经验法则(例如,mmap()快2倍)或简单的测试吗?
我同意mmap文件I/O将会更快,但是当您对代码进行基准测试时,不应该对反例进行一些优化吗?
本·柯林斯写道:
char data[0x1000];
std::ifstream in("file.bin");
while (in)
{
in.read(data, 0x1000);
// do something with data
}
我建议你也试试:
char data[0x1000];
std::ifstream iifle( "file.bin");
std::istream in( ifile.rdbuf() );
while( in )
{
in.read( data, 0x1000);
// do something with data
}
除此之外,您还可以尝试使缓冲区大小与一页虚拟内存大小相同,以防0x1000不是您机器上一页虚拟内存的大小……IMHO mmap文件I/O仍然是赢家,但这应该使事情更接近。
我认为mmap最大的优点是可以实现异步读取:
addr1 = NULL;
while( size_left > 0 ) {
r = min(MMAP_SIZE, size_left);
addr2 = mmap(NULL, r,
PROT_READ, MAP_FLAGS,
0, pos);
if (addr1 != NULL)
{
/* process mmap from prev cycle */
feed_data(ctx, addr1, MMAP_SIZE);
munmap(addr1, MMAP_SIZE);
}
addr1 = addr2;
size_left -= r;
pos += r;
}
feed_data(ctx, addr1, r);
munmap(addr1, r);
问题是我找不到正确的MAP_FLAGS来提示这个内存应该尽快从文件同步。
我希望MAP_POPULATE为mmap提供了正确的提示(即它不会尝试在调用返回之前加载所有内容,但会在异步中这样做。feed_data)。至少使用这个标志可以得到更好的结果,即使手册上说自2.6.23以来没有MAP_PRIVATE它什么都不做。
主要的性能成本是磁盘i/o。"mmap()"当然比istream快,但这种差异可能不明显,因为磁盘I / O将主导您的运行时。
我尝试了Ben Collins的代码片段(见上面/下面)来测试他的断言“mmap()快得多”,并没有发现可测量的差异。请看我对他的回答的评论。
我当然不建议逐个逐个地mmap每条记录,除非你的“记录”非常大——那样会非常慢,每条记录需要2个系统调用,而且可能会从磁盘内存缓存.....中丢失页面
在你的情况下,我认为mmap(), istream和低级的open()/read()调用都是相同的。在这些情况下,我建议使用mmap():
文件中有随机访问(而不是顺序访问)和
或者在文件中存在引用位置,以便某些页面可以映射进来,其他页面可以映射出去。这样操作系统就能最大限度地利用可用RAM。
或者,如果多个进程正在读取/处理同一个文件,那么mmap()非常有用,因为所有进程都共享相同的物理页面。
(顺便说一下-我喜欢mmap()/MapViewOfFile())。