文件数据到阵列使用了大量内存

hal*_*izh 3 python memory memory-management

我正在使用带有制表符分隔值的大型文本文件并将它们添加到数组中.

当我在一个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()
Run Code Online (Sandbox Code Playgroud)

无法按原样对我的2 GB文件运行此代码.文件看起来像这样:

per1    1029292 string1 euqye
per1    1029292 string2 euqys
Run Code Online (Sandbox Code Playgroud)

我的问题是:

什么在使用所有这些记忆?

什么是更有效的方式来做这个记忆明智?

Ste*_*ann 6

"什么在使用这些记忆?"

Python对象有开销.查看一些字符串实际占用的字节数:

Python 2:

>>> import sys
>>> map(sys.getsizeof, ('', 'a', u'ä'))
[21, 22, 28]
Run Code Online (Sandbox Code Playgroud)

Python 3:

>>> import sys
>>> list(map(sys.getsizeof, ('', 'a', 'ä')))
[25, 26, 38]
Run Code Online (Sandbox Code Playgroud)

"明智地做这个记忆的更有效方法是什么?"

在评论中你说有很多重复的值,所以字符串实习(只存储每个不同字符串值的一个副本)可能会有很大帮助.试试这个:

Python 2:

            markers.append(map(intern, line.rstrip().split('\t')))
Run Code Online (Sandbox Code Playgroud)

Python 3:

            markers.append(list(map(sys.intern, line.rstrip().split('\t'))))
Run Code Online (Sandbox Code Playgroud)

注意我也习惯从线上line.rstrip()删除尾部\n.


实验

我试过了

>>> x = [str(i % 1000) for i in range(10**7)]
Run Code Online (Sandbox Code Playgroud)

>>> import sys
>>> x = [sys.intern(str(i % 1000)) for i in range(10**7)]
Run Code Online (Sandbox Code Playgroud)

在Python 3中.第一个需要355 MB(查看Windows任务管理器中的过程).第二个只需47 MB​​.此外:

>>> sys.getsizeof(x)
40764032
>>> sum(map(sys.getsizeof, x[:1000]))
27890
Run Code Online (Sandbox Code Playgroud)

所以40 MB用于引用字符串的列表(毫不奇怪,因为每个字节有1000万个引用,每个字节有四个字节).琴弦本身总共只有27 K B.


进一步改进

如实验中所示,您的大部分RAM使用可能不是来自字符串,而是来自列表对象.无论您的markers列表对象,以及所有那些列表对象代表你行.特别是如果你使用64位Python,我怀疑你这样做.

为了减少这种开销,你可以使用元组代替行的列表,因为它们更轻量级:

sys.getsizeof(['a', 'b', 'c'])
48
>>> sys.getsizeof(('a', 'b', 'c'))
40
Run Code Online (Sandbox Code Playgroud)

我估计你的2 GB文件有8000万行,这样可以节省640 MB RAM.如果运行64位Python,可能会更多.

另一个想法:如果你的所有行都有相同数量的值(假设有三个),那么你可以放弃那些8000万行列表对象并使用一个240万字符串值的一维列表.您只需要使用markers[3*i+j]而不是访问它markers[i][j].它可以节省几GB的RAM.