当前位置: 首页 > 面试题库 >

mmap用于写入顺序日志文件以提高速度?

侯池暝
2023-03-14
问题内容

我想使用mmap(为了提高速度)编写非结构化格式的日志文件(一次一行)。最好的程序是什么?我是否要打开空白文件truncate至1页大小(写空字符串以调整文件大小?),然后mmap-在mmaped区域已满时重复?

我通常使用mmap在一个时间写固定大小的结构,通常只有一个页面,但是这是(从0.5的任何地方- 10
Gb)的写入日志文件使用mmap,但不知道什么是最好的做法,一旦第一mmaped区域充满-
munmap,调整文件大小truncatemmap下一页?

在将日志写入内存区域时,我将跟踪大小,并且msync,一旦到达映射内存区域的末尾,正确的处理是什么?

假设我永远不需要返回或覆盖现有数据,因此我只将新数据写入文件。

Q1:当我到达映射区域的末尾时munmapftruncate是否要按其他页面大小和mmap下一页调整大小的文件?

Q2:是否有一种标准的方式可以进行抢占并在内存中准备好下一页以进行下一次写入?当我们接近映射区域的末端时,是否在另一个线程上执行此操作?

Q3:我madvise是否可以顺序访问?

这是用于实时数据处理,需要保留日志文件-当前我只是写入文件。日志文件是非结构化,文本格式,基于行的。

这是针对Linux / c ++ / c(可选)在Mac上进行的测试(因此没有重新映射[?])。

任何有关最佳做法的链接/指标都值得赞赏。


问题答案:

我写了关于fwrite VS mmap( “衡量传统I / O与内存映射文件之间的性能折衷的实验”)
的比较学士学位论文。首先,对于写入,您不必去寻找内存映射文件,尤其是大文件。fwrite是完全可以的,并且几乎总是会优于使用mmapmmap将为您提供最大的并行数据读取性能提升;用于连续数据写入的真正限制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
}

广告Q1:

我使用了“取消映射-重新映射”机制

取消映射

  • 第一次冲洗(msync
  • 然后取消映射内存映射的文件。

这看起来可能如下:

void unmap() {
  msync(memoryMappedFile, mappedSpace, MS_SYNC); // Flush
  munmap(memoryMappedFile, mappedSpace)
}

对于 Remap ,您可以选择重新映射整个文件或仅重新映射新添加的部分。

基本上重新映射

  • 增加文件大小
  • 创建新的内存映射

完整重映射的示例实现:

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的,不应在旨在可移植的程序中使用。

广告Q2:

我不确定我是否明白这一点。如果您想告诉内核“现在加载下一页”,则不行,这是不可能的(至少据我所知)。但是请参阅 广告第3步 ,了解如何建议内核。

广告Q3:

您可以madvise与flag一起使用MADV_SEQUENTIAL,但请记住,这不会强制内核提前读取,而只是建议您这样做。

男子摘录:

可能会 导致内核主动预读

个人结论:

请勿mmap用于顺序数据写入。与使用编写简单的算法相比,它只会导致更多的开销,并且会导致更多的“非自然”代码fwrite

使用mmap随机访问读取大型文件。

这也是我论文期间获得的结果。通过使用mmap顺序写入,我无法实现任何提速,实际上,这样做总是较慢。



 类似资料:
  • 与malloc'相同大小的内存和手动读取整个文件到malloc'区域相比,这有什么不同?

  • 我的grails配置log4j部分中有以下内容: 文件“onetract3.log”已成功创建,但未向该文件写入任何内容。 我可以在控制台中看到信息处理正确。 Grails版本是2.3.5 知道为什么这没有写入日志文件吗? 编辑:10.03.1014,设置相加为false。

  • 问题内容: 我正在尝试配置log4j以将消息记录到文件中。现在,确实使用我提供的名称创建了该文件,但是日志未写入该文件。我的代码: 我文件的内容: 运行此命令时,将在控制台中获得以下输出: 该文件确实在我的主目录中创建。但是它是空的。 有什么想法我做错了吗?我正在使用log4j 1.2.17版。 问题答案: 输出似乎是Java标准日志记录框架(JUL)发出的默认格式。 因此,有两种可能性(想到):

  • 我正在使用带有log4j2的Spring Boot1.5.9,当我测试日志记录功能时,创建了日志文件,但没有将日志写入其中,而控制台日志记录程序运行良好。请在pom.xml&log4j2.properties配置下面找到。 log4j2.properties文件:

  • 是否有写入此事件日志的方法: 或者至少是其他一些Windows默认日志,在那里我不必注册事件源?

  • 问题内容: 我正在尝试使用Go写入日志文件。 我尝试了几种方法,但都失败了。这是我尝试过的: 日志文件被创建,但是没有任何打印或附加到该文件。为什么? 问题答案: 过去的工作方式一定不同,但这对我有用: 基于Go文档,不能用于,因为它会打开文件“供阅读:” 打开命名文件以供读取。如果成功,则可以使用返回文件上的方法进行读取;关联的文件描述符具有mode 。如果有错误,它将是类型。 编辑 检查后移至