当整个文件太大时,如何在python中使用mmap

当整个文件太大时,如何在python中使用mmap,python,Python,我有一个python脚本,它逐行读取文件,并查看每一行是否匹配正则表达式 我想通过在搜索之前使用内存映射文件来提高该脚本的性能。我研究了mmap示例: 我的问题是,当一个文件对于我的机器内存(4GB)来说太大(15GB)时,我如何映射它 我是这样读文件的: fi = open(log_file, 'r', buffering=10*1024*1024) for line in fi: //do somemthong fi.close() 由于我将缓冲区设置为10MB,在性能方面,

我有一个python脚本,它逐行读取文件,并查看每一行是否匹配正则表达式

我想通过在搜索之前使用内存映射文件来提高该脚本的性能。我研究了mmap示例:

我的问题是,当一个文件对于我的机器内存(4GB)来说太大(15GB)时,我如何映射它

我是这样读文件的:

fi = open(log_file, 'r', buffering=10*1024*1024)

for line in fi: 
    //do somemthong

fi.close()
由于我将缓冲区设置为10MB,在性能方面,它是否与我mmap 10MB的文件相同


谢谢。

首先,您机器的内存与此无关。这与你的流程的大小有关。使用32位Python,这将在4GB以下。使用64位Python,就足够了

这样做的原因是,它不是要进入物理内存,而是要进入物理内存。
mmap
ped文件就像是程序的特殊交换文件。考虑这一点可能会有点复杂,但上面的维基百科链接应该会有所帮助

因此,第一个答案是“使用64位Python”。但很明显,这可能不适用于你的情况

明显的替代方法是在第一个1GB中映射、搜索、取消映射、在下一个1GB中映射,等等。执行此操作的方法是将
length
offset
参数指定给
mmap
方法。例如:

m = mmap.mmap(f.fileno(), length=1024*1024*1024, offset=1536*1024*1024)
然而,您正在搜索的正则表达式可以在前1GB中找到一半,在第二1GB中找到一半。因此,您需要在第一个1GB中使用窗口映射,搜索、取消映射,然后在部分重叠的1GB中映射,等等

问题是,您需要多少重叠?如果你知道一场比赛的最大可能大小,你不需要更多的东西。如果你不知道……那么,如果不打破你的正则表达式,就没有办法真正解决这个问题。如果这不明显,想象一下你怎么可能在一个1GB窗口中找到2GB匹配

回答您的后续问题:

由于我将缓冲区设置为10MB,在性能方面,它是否与我mmap 10MB的文件相同

与任何性能问题一样,如果它真的很重要,您需要测试它,如果它不重要,就不要担心它

如果你想让我猜一下:我认为
mmap
在这里可能更快,但这仅仅是因为(正如J.F.Sebastian所暗示的)循环和调用
re.match
128K次可能会导致你的代码受CPU约束而不是IO约束。但是您可以在不使用
mmap
的情况下对其进行优化,只需使用
read
。那么,
mmap
会比
read
快吗?考虑到涉及的规模,我希望
mmap
在旧Unix平台上的性能要快得多,在现代Unix平台上的性能大致相同,在Windows上的性能要慢一点。(如果您使用
madvise
,您仍然可以从
mmap
而不是
read
read
+
lseek
中获得巨大的性能优势,但这与此无关。)但实际上,这只是一个猜测


使用
mmap
最令人信服的原因通常是它比基于
读取的代码更简单,而不是更快。当您必须在
mmap
中使用窗口时,当您不需要在
read
中进行任何搜索时,这就不那么引人注目了,但如果您尝试用两种方式编写代码,我希望
mmap
代码最终会更具可读性。(特别是当您试图从明显的
读取
解决方案中优化缓冲区副本时。)

我之所以尝试使用
mmap
,是因为我在一个大小为几十GB的文件上使用了
fileh.readline()
,并希望加快速度。Unix
strace
实用程序似乎显示文件现在是以4kB的块读取的,至少在我看来,strace的输出打印速度很慢,我知道解析文件需要很多小时

$ strace -v -f -p 32495
Process 32495 attached
read(5, "blah blah blah foo bar xxxxxxxxx"..., 4096) = 4096
read(5, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"..., 4096) = 4096
read(5, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"..., 4096) = 4096
read(5, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"..., 4096) = 4096
^CProcess 32495 detached
$
到目前为止,这个帖子是唯一解释我不应该尝试
mmap
太大文件的帖子。我不明白为什么现在还没有像
mmap\u for_dummies(filename)
这样的助手函数,它可以在内部执行os.path.size(filename),然后执行普通
open(filename,'r',buffering=10*1024*1024)
或执行
mmap.mmap(open(filename.fileno())
。我当然想避免自己摆弄滑动窗口方法,但函数是否会做一个简单的决定,是否执行
mmap
对我来说就足够了

最后要提到的是,我仍然不清楚为什么互联网上的一些示例提到
open(filename,'rb')
而没有解释(例如)。如果你经常想在for循环中使用文件,调用
.readline()
,我不知道是应该在
'rb'
模式下打开,还是只在
'r'
模式下打开(我想有必要保留
'\n'


感谢您提到
buffering=10*1024*1024)
参数,它可能比更改代码以提高速度更有帮助。

确保IO是性能瓶颈,而不是正则表达式搜索或程序中的其他操作。@J.F.Sebastian:+1。使用
re.search
(和隐式
find('\n')
等效)每80个字符执行一次Python循环,而不是每10MB执行一次,这可能足以使CPU工作大大降低IO成本,并使问题的其余部分变得无关紧要。我更新了我的答案以考虑到这一点。答案很好,但是如果你解释了为什么重要的是地址空间而不是机器中的物理内存量,那么IMHO可能会更好。@martineau:好主意。我试过了,但是…解释这些东西很难,特别是当你不知道观众已经知道多少的时候。如果最后一段是一个无法阅读的混乱,我不会太惊讶。有什么改进的建议吗?你添加的内容似乎有点太详细了。我想这可以归结为matt