将文件长期保存在内存中
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)
我想知道这样做是否会遇到内存管理问题,如果程序要长时间 运行。无论需要多长时间,具有完整内容的文件是否会在内存中保持完整?如果没有,我有什么选择?
当然,最初我确实尝试过正确地做事,通过只读取循环的每次迭代我需要的部分,使用 itertools 中的 islice,并使用 seek( 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]
您保存在内存中的数据类型是一个列表,而不是一个文件对象,因此 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。您当然也可以实现自己的缓存。
希望对您有所帮助。
我正在处理一个相对较大的文件(大约 2GB)。在至少 1-2 天 运行s 的 while 循环中持续需要其内容。
有足够的 RAM,我在循环之前将整个文件加载到内存中,使用:
f = open(filename)
lines = f.readlines()
while ...
#using different portions of the file (randomly picked)
我想知道这样做是否会遇到内存管理问题,如果程序要长时间 运行。无论需要多长时间,具有完整内容的文件是否会在内存中保持完整?如果没有,我有什么选择?
当然,最初我确实尝试过正确地做事,通过只读取循环的每次迭代我需要的部分,使用 itertools 中的 islice,并使用 seek( 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]
您保存在内存中的数据类型是一个列表,而不是一个文件对象,因此 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。您当然也可以实现自己的缓存。
希望对您有所帮助。