2020年10月5日 / 1,902次阅读 / Last Modified 2020年11月6日
用mmap来读取超大文件,不是mmap的主要应用场景,python官方文件也没有提到这一点。如果仅仅是读取超大文件,使用文件对象的read(N),来得更快更好更简单。这是我个人的测试结果。
---------------分割线-------------------
记录一点自己的学习和思考,关于标准库中的mmap模块。项目上遇到一个需求,要对超大文件进行读写,这个文件大到什么程度呢?有接近40G,notepad++等工具直接拒绝打开此文件。
用 r+ 模式打开文件,可以随意读写,但是要特别小心。readline是否能够使用,要看这个文件每行都多长,如果没有换行,就不能用,就算知道每行的大小,也要带个参数N来控制最大读取数量。readlines是肯定不能用的,就算带参数,也可能直接卡死!read(N)没问题,主要控制是N的大小。(关于read,readline和readlines函数)
总之,传统读写文件的方式可以用,但是不够方便。速度也是个问题,传统的缓存IO方式,涉及到OS内核态的内存和进程虚拟空间内存的内容交换,对于超大文件而言,这种交换会浪费大量的CPU时间和内存。
mmap是另一个方式!它省掉了内核态和用户态页copy这个动作(两态间copy),直接将用户态的虚拟地址与内核态空间进行mapping,进程直接读取内核空间,速度提高了,内存占用也少了。
总结来说,常规文件操作为了提高读写效率和保护磁盘,使用了页缓存机制。这样造成读文件时需要先将文件页从磁盘拷贝到页缓存中,由于页缓存处在内核空间,不能被用户进程直接寻址,所以还需要将页缓存中数据页再次拷贝到内存对应的用户空间中。这样,通过了两次数据拷贝过程,才能完成进程对文件内容的获取任务。写操作也是一样,待写入的buffer在内核空间不能直接访问,必须要先拷贝至内核空间对应的主存,再写回磁盘中(延迟写回),也是需要两次数据拷贝。
而使用mmap操作文件中,创建新的虚拟内存区域和建立文件磁盘地址和虚拟内存区域映射这两步,没有任何文件拷贝操作。而之后访问数据时发现内存中并无数据而发起的缺页异常过程,可以通过已经建立好的映射关系,只使用一次数据拷贝,就从磁盘中将数据传入内存的用户空间中,供进程使用。
总而言之,常规文件操作需要从磁盘到页缓存再到用户主存的两次数据拷贝。而mmap操控文件,只需要从磁盘到用户主存的一次数据拷贝过程。说白了,mmap的关键点是实现了用户空间和内核空间的数据直接交互而省去了空间不同数据不通的繁琐过程。因此在某些场景下,mmap效率更高。
从python官网上看mmap的介绍,生成的mmap对象,就像一个bytearray对象,可以直接用index的方式读写,可以slicing。同时,mmap对象还有一组类似文件操作的接口,read,readline,flush等等。即mmap对象兼具bytearray和file对象的功能。
不过还是要注意,对于超大文件的读(先不考虑写的问题吧),从磁盘带内核,依然会占用内存,因此绝对不能一口气全部读出来。read(N)是肯定的,mmap的使用只是可能会提高效率。(如果频繁的创建和关闭mmap映射,这种创建是为了指向超大文件的不同位置,反而效率更低。一般情况下的read(N)实现,不需要使用mmap。)
mmap的另一个应用场景,是进程间的内存共享。多个进程将同一个文件map到同一段内核地址上,即实现了相互之间的共同访问。
现在回答标题的问题:什么时候用mmap?
1、将一个普通文件映射到内存中,通常在需要对文件进行频繁读写时使用,这样用内存映射读写取代I/O缓存读写,以获得较高的性能;
2、将特殊文件进行匿名内存映射,可以为关联进程提供共享内存空间;
3、为无关联的进程提供共享内存空间,一般也是将一个普通文件映射到内存中。
上面这3点是从网上摘录的,几个属于我现在也不懂,总结起立就是读写文件更快,进程间的内存共享。
-- EOF --
本文链接:https://www.pynote.net/archives/2532
《什么时候用mmap?》有2条留言
前一篇:进度条模块tqdm介绍
后一篇:用inspect.getsource查看源码
©Copyright 麦新杰 Since 2019 Python笔记
flags specifies the nature of the mapping. MAP_PRIVATE creates a private copy-on-write mapping, so changes to the contents of the mmap object will be private to this process, and MAP_SHARED creates a mapping that’s shared with all other processes mapping the same areas of the file. The default value is MAP_SHARED. mmap模块的一个应用场景,process间的内存共享! [ ]
mmap兼具file对象的接口,就可以指定read(N),每次读取的长度,在内存受限的情况下。 [ ]