我有一个奇怪的问题。 我正在加载一个巨大的文件(3.5G)并从中制作一个字典并进行一些处理。 一切都完成后,我的脚本不会立即终止,它会在一段时间后终止。 我想这可能是由于内存释放,还有什么可能是其他原因?我很感激任何意见。如何让我的脚本运行得更快?
以下是相应的代码:
class file_processor:
def __init__(self):
self.huge_file_dict = self.upload_huge_file()
def upload_huge_file(self):
d = defaultdict(list)
f= codecs.open('huge_file', 'r', encoding='utf-8').readlines()
for line in f:
l = line.strip()
x,y,z,rb,t = l.split()
d[rb].append((x,y,z,t))
return d
def do_some_processing(self, word):
if word in self.huge_file_dict:
do sth with self.huge_file_dict[word]
答案 0 :(得分:4)
我的猜测是,你的程序完成后才会恢复的可怕速度下降是由于使用的内存比你实际拥有的多,这会导致你的操作系统开始将VM页面交换进磁盘。一旦你进行了足够的交换,你就会陷入“交换地狱”,其中大部分内存访问涉及磁盘读取甚至磁盘写入,这需要花费数量级的时间,并且系统将无法恢复在你最终释放所有记忆后几秒钟。
显而易见的解决方案是不要使用这么多内存。
tzaman's answer,避免readlines()
,将消除部分内存。 3.5GB文件中所有行的巨大列表在Python 3.4或2.7上至少需要3.5GB(但实际上至少比这个多20%),在3.0-3.3上可能是2x或4x。
但是dict会比列表更大,你需要它,对吧?
嗯,不,你可能没有。将dict保存在磁盘上并根据需要获取值可能听起来很慢,但如果虚拟内存必须不断地交换到磁盘,它仍然比将其保存在虚拟内存中要快得多。
您可能需要考虑使用简单的dbm
或更强大的键值数据库(某些选项的google“NoSQL键值”),或sqlite3
数据库,甚至是服务器基于MySQL的SQL数据库。
或者,如果您可以将所有内容保存在内存中,但是以更紧凑的形式,这是两个世界中最好的。
我注意到在你的示例代码中,你对dict做的唯一事情就是检查word in self.huge_file_dict
。如果这是真的,那么您可以使用set
而不是dict
,而不是将所有这些值保留在内存中。这应该会减少大约80%的内存使用量。
如果您经常需要密钥,但偶尔需要这些值,您可能需要考虑一个dict,它只是将索引映射到您可以根据需要从磁盘读取的内容(例如,具有固定长度字符串的文件,然后你可以mmap
和切片)。
或者您可以将这些值粘贴在Pandas框架中,这将比原生Python存储更紧凑 - 可能足以产生差异 - 并使用dict映射键到索引。
最后,您可以在不实际减少内存量的情况下减少交换量。对一个巨大的排序列表进行二分,而不是访问一个巨大的词典,可能 - 取决于你的单词模式 - 给出更好的记忆位置。
答案 1 :(得分:3)
不要调用.readlines()
- 预先将整个文件加载到内存中。你可以直接迭代f
,它会正常工作。
with codecs.open('huge_file', 'r', encoding='utf-8') as f:
for line in f:
...