假设地址空间可以覆盖文件,在我看来,mmap只是分配一块与要读取的文件一样大的内存块,并在它们相应的块之间创建一对一的关系。但是,为什么这样做会加快文件读取速度?似乎为了实际获取文件的内容,您仍然必须转到磁盘,并读取它上面的所有字节。
与malloc相同大小的内存并手动将整个文件读入malloc区域相比,它有何不同?
答案 0 :(得分:6)
mmap
的工作方式不同。这是预期的,并适应该计划的访问模式。此外,可以通过madvise
设置特定政策,以进一步微调使用情况。
有关mmap
在请求分页环境中如何运作的更全面的讨论,请参阅我的答案:Which segments are affected by a copy-on-write?因为它还讨论了mmap
的使用
mmap
是通过execve
等执行程序的生命线。人。所以,你可以打赌它很快。作为旁注,具有讽刺意味的是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可以提高某些应用程序的速度。
请在此处查看我的回答:read line by line in the most efficient way *platform specific*
在我这样做之前,我对mmap的好处持怀疑态度,但基准测试显示mmap是胜利者。
另外,如果你正在read(2)
(对于速度)与fgets
进行比较,如果给定的行跨越读缓冲区边界,则可能会因缓冲区移位而陷入困境(即缓冲区的最后50个字符具有80字符行的前50个字节。)
请注意,在此链接页面的评论中,还有另一个指向pastebin的链接到我的基准测试程序的更高版本,结果太大,无法在上述SO答案中发布基准并比较各种madvise
选项
答案 1 :(得分:1)
我对此感到好奇所以我尝试对整个文件进行基准测试
大小为1,2,4,8等,一次为Monad
(M),一次为bind_sm
(R)(理论上一次调用fstat-ed大小,但如果有调用返回了部分结果)。在读取/ mmaping之后,以不可优化的方式访问每个mmaped / read页面的一个字节。
这是我的结果:
instance Monad m => Functor (StateM m s) where ..
instance Monad m => Applicative (StateM m s) where ..
instance Monad m => Monad (StateM m s) where ..
看起来mmap
的速度大约是read
的两倍。从那时起,Size M(µs) R(µs)
1 9.5 4.2
2 10.8 4.5
4 8.4 3.8
8 8.6 3.8
16 7.3 4
32 7.8 3.5
64 8.3 3.9
128 9.2 4.6
256 8.6 4.7
512 10.6 5.1
1.0Ki 9.8 4.7
2.0Ki 10.1 5.4
4.0Ki 10.5 5.6
8.0Ki 10.4 6.9
16Ki 9.9 10
32Ki 14.4 12.8
64Ki 16.1 23.7
128Ki 28.1 41.1
256Ki 34.5 82.4
512Ki 57.9 154.6
1.0Mi 103.5 325.8
2.0Mi 188.5 919.8
4.0Mi 396.3 1963.2
8.0Mi 798.8 3885
16Mi 1611.4 7660.2
32Mi 3207.4 23040.2
64Mi 6712.1 84491.9
开始赢得大量时间(read
个文件的因素 12 。
(在我的笔记本电脑上使用3.19在Linux上测试,10 ^ 4重复读取到同一文件。)
答案 2 :(得分:0)
它没有。具体来说,当你调用它时,mmap()不会将整个文件加载到内存中,以某种方式加速访问。相反,它映射文件,也就是说,在内存中创建文件的索引(我使用松散的术语,承担我),以便在您尝试读取/写入时触发页面错误&# 34;键&#34 ;.所以净效应是你有一个简单的文件接口和一些延迟加载文件的内容。
我可以继续,但其他人做得更好。例如,请参阅here。