将文件长期保存在内存中

Keeping files in memory for long times

我正在处理一个相对较大的文件(大约 2GB)。在至少 1-2 天 运行s 的 while 循环中持续需要其内容。

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

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

while ...
    #using different portions of the file (randomly picked)

更多说明,评论后:

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

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]

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

不按顺序使用也没关系。 Python编译前分析代码,他知道你后面会用到这个列表

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

除非你的台词像大象一样大。

Seek 将 read/write 指针移动到您想要的内存块(文件内)。当你之后执行 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() 以相同的速度做同样的事情,但即使那样你也必须寻找并且代码会变得有点混乱。

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

例如(如果您使用 python3.2+、3.3+ 并带有 "typed" 选项):

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。您当然也可以实现自己的缓存。

希望对您有所帮助。