我正在获取一个包含制表符分隔值的大型文本文件,并将它们添加到一个数组中。
当我在一个 32 Mb 的文件上运行我的代码时,python 内存消耗达到顶峰;使用大约 500 Mb RAM。
我需要能够为 2 GB 的文件运行此代码,甚至可能更大的文件。
我当前的代码是:
markers = []
def parseZeroIndex():
with open('chromosomedata') as zeroIndexes:
for line in zeroIndexes:
markers.append(line.split('\t'))
parseZeroIndex()
无法按原样对我的 2 GB 文件运行此代码。文件如下所示:
per1 1029292 string1 euqye
per1 1029292 string2 euqys
我的问题是:
什么在使用所有这些内存?
什么是更有效的内存方式?
最佳答案
“什么在使用所有这些内存?”
Python 对象有开销。查看一些字符串实际占用了多少字节:
python 2:
>>> import sys
>>> map(sys.getsizeof, ('', 'a', u'ä'))
[21, 22, 28]
python 3:
>>> import sys
>>> list(map(sys.getsizeof, ('', 'a', 'ä')))
[25, 26, 38]
“什么是更有效的内存方式?”
在评论中你说有很多重复值,所以 string interning (只存储每个不同字符串值的一个副本)可能会有很大帮助。试试这个:
python 2:
markers.append(map(intern, line.rstrip().split('\t')))
python 3:
markers.append(list(map(sys.intern, line.rstrip().split('\t'))))
请注意,我还使用 line.rstrip()
从该行中删除尾随 \n
。
实验
我试过了
>>> x = [str(i % 1000) for i in range(10**7)]
和
>>> import sys
>>> x = [sys.intern(str(i % 1000)) for i in range(10**7)]
在 Python 3 中。第一个占用 355 MB(查看 Windows 任务管理器中的进程)。第二个只需要 47 MB。此外:
>>> sys.getsizeof(x)
40764032
>>> sum(map(sys.getsizeof, x[:1000]))
27890
所以 40 MB 用于引用字符串的列表(不足为奇,因为有 1000 万个引用,每个引用有 4 个字节)。而弦本身的总和只有 27 KB。
进一步改进
正如在实验中所看到的,您的大部分 RAM 使用可能不是来自字符串,而是来自您的列表对象。您的 markers
列表对象以及代表您的行的所有列表对象。特别是如果您使用的是 64 位 Python,我怀疑您会这样做。
为了减少开销,您可以为行使用元组而不是列表,因为它们更轻量级:
sys.getsizeof(['a', 'b', 'c'])
48
>>> sys.getsizeof(('a', 'b', 'c'))
40
我估计您的 2 GB 文件有 8000 万行,这样可以节省 640 MB RAM。如果您运行 64 位 Python,可能会更多。
另一个想法:如果您所有的行都具有相同数量的值(我假设为三个),那么您可以放弃那些 8000 万行列表对象,而改用包含 2.4 亿个字符串值的一维列表。您只需使用 markers[3*i+j]
而不是 markers[i][j]
访问它。而且它可以节省几 GB 的 RAM。
关于python - 文件数据到数组正在使用大量内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38164418/