我写了关于 fwrite 与 mmap 比较的学士论文(“测量传统 I/O 和内存映射文件之间的性能权衡的实验”)。首先,对于写作,你不必去寻找内存映射文件,尤其是大文件。 fwrite 完全没问题,并且几乎总是优于使用 mmap 的方法。 mmap 将为您提供最大的并行数据读取性能提升;对于顺序数据,使用fwrite 写入您的真正限制是您的硬件。
在我的示例中,remapSize 是文件的初始大小以及文件在每次重新映射时增加的大小。
fileSize 跟踪文件的大小,mappedSpace 表示当前 mmap 的大小(它的长度),alreadyWrittenBytes 是已经写入文件的字节。
这里是初始化示例:
void init() {
fileDescriptor = open(outputPath, O_RDWR | O_CREAT | O_TRUNC, (mode_t) 0600); // Open file
result = ftruncate(fileDescriptor, remapSize); // Init size
fsync(fileDescriptor); // Flush
memoryMappedFile = (char*) mmap64(0, remapSize, PROT_WRITE, MAP_SHARED, fileDescriptor, 0); // Create mmap
fileSize = remapSize; // Store mapped size
mappedSpace = remapSize; // Store mapped size
}
广告第一季度:
我使用了“Unmap-Remap”机制。
取消映射
- 第一次刷新 (
msync)
- 然后取消映射内存映射文件。
这可能如下所示:
void unmap() {
msync(memoryMappedFile, mappedSpace, MS_SYNC); // Flush
munmap(memoryMappedFile, mappedSpace)
}
对于重新映射,您可以选择重新映射整个文件或仅重新映射新附加的部分。
基本上重新映射
完整重映射的示例实现:
void fullRemap() {
ftruncate(fileDescriptor, mappedSpace + remapSize); // Make file bigger
fsync(fileDescriptor); // Flush file
memoryMappedFile = (char*) mmap64(0, mappedSpace + remapSize, PROT_WRITE, MAP_SHARED, fileDescriptor, 0); // Create new mapping on the bigger file
fileSize += reampSize;
mappedSpace += remapSize; // Set mappedSpace to new size
}
小型重映射的示例实现:
void smallRemap() {
ftruncate(fileDescriptor, fileSize + remapSize); // Make file bigger
fsync(fileDescriptor); // Flush file
remapAt = alreadyWrittenBytes % pageSize == 0
? alreadyWrittenBytes
: alreadyWrittenBytes - (alreadyWrittenBytes % pageSize); // Adjust remap location to pagesize
memoryMappedFile = (char*) mmap64(0, fileSize + remapSize - remapAt, PROT_WRITE, MAP_SHARED, fileDescriptor, remapAt); // Create memory-map
fileSize += remapSize;
mappedSpace = fileSize - remapAt;
}
那里有一个mremap function,但它声明
此调用是特定于 Linux 的,不应在程序中使用
旨在便携。
广告第二季度:
我不确定我是否正确理解了这一点。如果您想告诉内核“现在加载下一页”,那么不,这是不可能的(至少据我所知)。但请参阅Ad Q3,了解如何为内核提供建议。
广告第三季度:
您可以将madvise 与标志MADV_SEQUENTIAL 一起使用,但请记住,这不会强制内核提前读取,而只是建议它。
摘自man:
这可能导致内核主动预读
个人结论:
不要使用mmap 进行顺序数据写入。与使用 fwrite 的简单编写算法相比,它只会导致更多的开销,并且会导致更多“不自然”的代码。
使用mmap 对大文件进行随机访问读取。
这也是我论文期间得到的结果。我无法通过使用mmap 进行顺序写入来实现任何加速,事实上,为此目的它总是较慢。