将文件长时间保存在内存中

时间:2015-10-30 12:49:35

标签: python memory-management large-files

我正在处理一个相对较大的文件(大约2GB)。它的内容在一段时间内不断需要,运行至少1-2天。

有足够的RAM,我在循环之前将整个文件加载到内存中,使用:

f = open(filename)
lines = f.readlines()

while ...
    #using different portions of the file (randomly picked)
  • 我想知道如果程序要长时间运行,我是否会面临内存管理问题。具有完整内容的文件是否会在内存中保持不变,但可能需要多长时间?如果没有,我有哪些替代方案?

  • 当然我最初尝试做的事情是正确的,只需要读取循环的每次迭代所需的部分,使用itertools中的islice,并使用seek(0)将迭代器设置回0用于随后的循环运行。但它运行得非常慢,因为文件很大而while循环很长。

评论后更多澄清:

当我没有将它加载到内存中时,我基本上是这样做的:

from itertools import islice 
f = open(filename) 
while ...:
    for line in islice(f, start_line, end_line): 
        text += line 
    f.seek(0) 

与我在内存中加载的内容相比,它真的很慢,如下所示:

lines = f.readlines() 
while...: 
    for i in range(start_line, end_line): text += lines[i]

2 个答案:

答案 0 :(得分:1)

您在内存中保留的数据类型是一个列表,而不是文件对象,因此Python将特别小心,不要在以后使用该列表时对其进行垃圾收集。

如果你没有按顺序使用它并不重要。 Python在编译之前会对代码进行分析,并且他知道稍后您将使用此列表。

无论如何,如果你在文件对象上使用seek()和tell(),我就不明白它为什么会变慢。

除非你的线条像大象一样大。

Seek将读/写指针移动到您希望的内存块(文件内)。当你以后执行f.readline()时,它会直接跳转到那里。

不应该慢。如果你使用它,你将避免一些其他程序崩溃的可能性,因为Python保留了大量的内存。

此外,Python列表并非完全不确定。我认为它可以在32位PC上容纳更多10 ** 7项。

所以你有多少行也很重要。

直接从HD / SSD / Flash快速随机读取的示例:

from random import randint
from time import sleep

f = open("2GB.file", "rb")
linemap = [] # Keeps the start and end position of each line
for x in f:
    linemap.append((f.tell(), len(x)))
    # It is slightly faster to have start and length than only start and then f.readline()
    # But either way will work OK for you

def getline (index):
    line = linemap[index]
    f.seek(line[0])
    return f.read(line[1])

def getslice (start=0, stop=None):
    if stop==None: stop = len(linemap)
    howmany = 0
    for x in xrange(start, stop): howmany += linemap[x][1]
    f.seek(linemap[start][0])
    return f.read(howmany).splitlines(1)

while True:
    print getline(randint(0, len(linemap)-1))
    sleep(2)

当然,速度永远不能与RAM的直接访问相匹配。只是为了清楚。但是,与使用islice()的解决方案相比,这很快。虽然你实际上可以使用islice()以相同的速度做同样的事情,但你必须寻找甚至那时代码会变得有点混乱。

答案 1 :(得分:1)

根据我的评论解释,您可以创建一个函数来返回内存中的字节缓冲区,并缓存该函数以对仅仅变量进行更多控制。

例如(如果您使用的是python3.2 +,3.3 +使用"键入"选项):

from functools import lru_cache
import io

@lru_cache(maxsize=None, typed=True)  # typed will cache as per different arg.
def get_cached_file(filename):
    m = io.BytesIO()
    with open(filename, 'rb') as f:
        m.write(f.read())
    return m

用法:

a = get_cached_file('a.file')
b = get_cached_file('b.file')

# since the files are new to cache, they belong "misses"
get_cached_file.cache_info()
CacheInfo(hits=0, misses=2, maxsize=None, currsize=2)

a1 = get_cached_file('a.file')
b2 = get_cached_file('b.file')

# simply return the result from cache, ie. "hits"
get_cached_file.cache_info()
CacheInfo(hits=2, misses=2, maxsize=None, currsize=2)

要阅读缓冲区,您只需要seek(0)或任何你想要的东西。

您也可以清除缓存:

get_cached_file.cache_clear()

# now its counter reset to "0"
get_cached_file.cache_info()
CacheInfo(hits=0, misses=0, maxsize=None, currsize=0)

您可以阅读更多here

如果您使用的是python2.x,请注意一些现有的库,以便在内存中进行缓存,例如memcached或redis。您当然也可以实现自己的缓存。

希望这有帮助。