One*_*ero 5 linux posix mmap memory-mapped-files
假设地址空间可以覆盖文件,在我看来mmap只是分配一块与要读取的文件一样大的内存块,并在它们相应的块之间创建一对一的关系.但是,为什么这样做会加快文件读取速度?似乎为了实际获取文件的内容,您仍然必须转到磁盘,并读取它上面的所有字节.
与malloc相同大小的内存并手动将整个文件读入malloc区域相比,它有何不同?
mmap工作不同.这是预期的,并适应该计划的访问模式.此外,可以通过madvise进一步微调使用来设置特定策略.
有关如何mmap在请求分页环境中工作的更全面的讨论,请参阅我的答案:哪些段受写入时复制影响?因为它也谈到了使用mmap
mmap是execveet等程序执行的命脉.人.所以,你可以打赌它很快.作为旁注,具有讽刺意味的是,malloc实际上也使用匿名mmap.
但是,为了讨论这里,特别要注意文件的"后备存储"(即分页磁盘)mmap与执行malloc和read(2)
使用mmap,内存区域的后备存储是文件本身.该区域将页面直接映射到内核的文件系统缓冲区页面[它们已经统一了很长时间].因此,不需要像执行那样从内核文件系统缓冲区页面到应用程序页面的[浪费]副本read(2).
当你这样做时malloc/read,你仍然有上面的页面,但是,现在malloc的区域在分页/交换磁盘上有一个后备存储.因此,页面缓冲区的数量是其两倍mmap.正如我所提到的,读取完成后必须将数据复制到该区域.
此外,在性能方面,进行大量读取是次优的.块[文件系统相关]中的建议大小约为64 KB.
执行大型读取时,程序在完成之前无法启动.如果文件的大小大于物理内存,系统将读入您的malloc区域,并浪费地将早期页面刷新到分页磁盘,为文件末尾附近的页面腾出空间,直到整个文件为止.读入.
换句话说,当这个大的预读发生时,应用程序正在等待[并且什么都不做].对于[比较] 60 GB的文件,启动时间会很明显.
如果你的文件是真正足够大,你甚至会用完的分页磁盘上的空间(即malloc返回NULL).
因为mmap,没有这样的问题.映射文件时,可以立即开始使用它.它将根据需要直接从该区域的后备存储中"故障"[后者再次是文件系统中的文件].并且,如果你有[说] 1 TB文件,mmap处理就好了.
此外,您还可以通过控制映射策略madvise(2),并posix_madvise(2)在页面逐页或包括整个文件中的任何页面范围.该madvise系统调用是比较轻巧所以它的罚款使用它很多.这是一个提示,但不会延迟应用程序的I/O. 如果I/O开始为提示进行预读,那么内核将其作为后台活动完成.
你甚至可以告诉系统很快就需要一个给定的页面[并且系统将其作为预取它的提示]或者你可以告诉系统不再需要该页面[并且系统将释放页面缓冲存储器].
你可以对整个文件说"顺序访问",这意味着系统会知道自动执行预读,以及不再需要的页面的释放(即如果你当前正在访问页面N,然后系统在Nk之前释放任何页面)
当你这样做时read(2),没有办法告诉系统不再需要给定的内核FS页面缓冲区.它们会一直徘徊,直到物理RAM填满[或超过给定的限制],这给整个内存系统增加了压力.
在实践中,使用read,我已经看到,在应用程序移动到文件的不同部分或完全不同的文件之后,用于FS缓冲区的内存量仍然很长.事实上,我已经看到一个I/O密集型应用程序使用如此多的缓冲区,导致无关的[idle]进程将其页面被盗并刷新到分页磁盘.当我停止I/O应用程序时,firefox需要花费几分钟来重新登录并再次响应.
我为常规阅读vs mmap做了一些广泛的基准测试.通过它们,mmap可以提高某些应用程序的速度.
请在此处查看我的答案:以最有效的方式逐行阅读*平台特定*
在我这样做之前,我对mmap的好处持怀疑态度,但基准测试表明mmap是一个胜利者.
此外,如果你正在做read(2)(速度)对比fgets,如果给定的行跨越读缓冲区边界(即缓冲区的最后50个字符具有前50个字节),则可能会因缓冲区移位而陷入困境80 char线).
请注意,在此链接页面的评论中,还有另一个指向pastebin的链接到我的基准程序的更高版本,结果太大,无法在上述SO答案中发布基准并比较各种madvise选项
我对此很好奇,所以我尝试对 \nsize 1、2、4、8 等文件的整个文件读取进行基准测试,一次使用mmap(M),一次使用read(R)(理论上,使用 fstat-ed 大小进行一次调用,但如果该调用返回部分结果,它将重试)。在读取/映射之后,以不可优化的方式访问每个映射/读取页面的字节。
这是我的结果:
\n\nSize M(\xc2\xb5s) R(\xc2\xb5s)\n1 9.5 4.2\n2 10.8 4.5\n4 8.4 3.8\n8 8.6 3.8\n16 7.3 4\n32 7.8 3.5\n64 8.3 3.9\n128 9.2 4.6\n256 8.6 4.7\n512 10.6 5.1\n1.0Ki 9.8 4.7\n2.0Ki 10.1 5.4\n4.0Ki 10.5 5.6\n8.0Ki 10.4 6.9\n16Ki 9.9 10\n32Ki 14.4 12.8\n64Ki 16.1 23.7\n128Ki 28.1 41.1\n256Ki 34.5 82.4\n512Ki 57.9 154.6\n1.0Mi 103.5 325.8\n2.0Mi 188.5 919.8\n4.0Mi 396.3 1963.2\n8.0Mi 798.8 3885\n16Mi 1611.4 7660.2\n32Mi 3207.4 23040.2\n64Mi 6712.1 84491.9\nRun Code Online (Sandbox Code Playgroud)\n\n看起来read速度大约是原来的两倍16Ki。从那时起,mmap开始赢得大胜利(对于文件来说是1264MiB倍)。
(在我的笔记本电脑上使用 3.19 的 Linux 进行测试,对同一文件重复读取 10^4 次。)
\n