使用re和mmap

时间:2015-08-12 10:53:17

标签: python regex python-3.x mmap

我在python 3.4(64位Windows)中对一个大的(30-ish GB)mmap ped文件运行正则表达式搜索时遇到“问题”。

基本上,我所观察到的是之间的匹配命中,内存占用量大致上升到匹配之间的字节数。它实际上并没有崩溃,但足迹足以减缓其他进程的速度(因为mmapped文件的大小)。

我的正则表达式来自一个字节字符串,它非常具体,所有量词都有界限。在我的表达式中没有*+,因此不是一个可怕的超越正则表达式的情况(最坏的情况是匹配将是2200字节长,大多数命中较小)。我将匹配的字符串存储在一个列表中,但最多通常只有几千次点击,因此不会占用占用所有空间的点击数。

我现在假设的是正则表达式引擎(sre对吗?)保留内存中匹配项之间的所有字符串,对于小数据集来说很好,但对我来说它并不是真的。所以我的问题是:这个假设是否正确,如果是这样,我可以改变这种行为(最好不要重新编译库!)

代码基本上是这样的:

pattern = re.compile(b"PATTERN.{1,20}", re.DOTALL)
f = open("file.bin", "rb")
mem = mmap.map(f.fileno(), 0, access=mmap.ACCESS_READ)

results = []
for match in pattern.finditer(mem):
    results.append(match.group(0))

f.close()

1 个答案:

答案 0 :(得分:0)

我不确定是否有办法解决这个问题。您正在以磁盘可以提供的速度快速读取大量数据。除非你有大量的RAM。如果你不在某些时候,你将耗尽RAM必须释放一些。大多数操作系统将使用LRU(最近最少使用)算法来决定从RAM中踢出什么。由于您尽可能快地访问数据,因此内存映射文件使用的大部分内存将具有最近的访问时间。因此意味着他们是“差”候选人被踢出RAM(至少根据操作系统)。

基本上,操作系统在可用内存不足时会对RAM的内容做出糟糕的选择。

但是,您更了解可以释放的内存。因此,您可以以块的形式扫描文件。当您不再需要文件的早期部分时,这将明确地允许操作系统,并允许释放该内存。当然,这会在块的边界产生问题。

作为改善程序内存性能的示例:

import re
import mmap
import os

filename = "some_file.txt"
file_size = os.stat(filename).st_size
chunk_size = 2**32
# chunk_size = 50 # smaller chunk_size I used for testing
regex = re.compile(rb"PATTERN\d{1,20}\n")
max_length = len("PATTERN") + 20 + len("\n")

matches = []
f = open(filename, "rb")    
for i in range(0, file_size, chunk_size - max_length + 1):
    # compute length of data to search over
    length = chunk_size if i + chunk_size <= file_size else file_size - i 

    m = mmap.mmap(f.fileno(), length=length, offset=i, access=mmap.ACCESS_READ)
    # f.seek(i) # used for testing
    # m = f.read(length)

    for match in regex.finditer(m):
        if not (match.end() == len(m) and len(match.group()) < max_length and length == chunk_size):
            # if match ends at end of string
            # and not maximum length of regex
            # but not also at the end of the file
            # THEN there *may* be a cross chunk-boundary match
            # THUS, defer match to next loop iteration
            matches.append(match.group())
    m.close()
f.close()