python内存使用:txt文件比包含文件文本的python列表小得多
我有一个543 MB txt文件,其中包含一行空格分隔的utf-8令牌:python内存使用:txt文件比包含文件文本的python列表小得多,python,python-2.7,memory,utf-8,nlp,Python,Python 2.7,Memory,Utf 8,Nlp,我有一个543 MB txt文件,其中包含一行空格分隔的utf-8令牌: aaa algeria americansamoa appliedethics accessiblecomputing ada anarchism ... 但是,当我将这些文本数据加载到python列表中时,它使用了约8GB的内存(列表约900MB,令牌约8GB): 我预计内存使用量大约为文件大小+列表开销=1.5GB。为什么令牌在加载到列表中时会消耗更多内存?两个原因: CPython中的每个字符串在其C对象头中都有相
aaa algeria americansamoa appliedethics accessiblecomputing ada anarchism ...
但是,当我将这些文本数据加载到python列表中时,它使用了约8GB的内存(列表约900MB,令牌约8GB):
我预计内存使用量大约为文件大小+列表开销=1.5GB。为什么令牌在加载到列表中时会消耗更多内存?两个原因:
unicode
对象使用52个字节,这是每个unicode
对象的固定开销,甚至在您计算它包含的数据之前。如果您有1.14Municode
对象(不是像u'
那样的单例对象),那么您仅在每个对象开销上就使用了近6GBstr
解码到unicode
,这取决于Python2的构建配置,每个字符使用固定的2或4个字节,即使对于纯ASCII字符串也是如此;根据您的数字,您使用的是4字节/字符系统。因此,它不需要超出对象头开销543MB的数据,而需要超过2GB的标题sys.getsizeof(u')
在我的x64系统上是52,尽管只存储了8个字节的“真实”数据,str
的长度)
但是,由于您的输入主要是ASCII码,您可以通过使用Python 3来减少内存使用;在现代Py3(3.3+IIRC)中,它们使用动态大小的存储来存储str
;仅使用ASCII/latin-1字符的str
将使用每个字符一个字节(latin-1使固定开销略高于ASCII,但每个字符的成本仍然为1),而不是两个或四个(基本多语言平面中的任何内容都将使用每个字符两个字节,而不是四个;只有非BMP字符串需要每个字符四个字节). str
的头也稍微小一些(sys.getsizeof(“”)=49
,而不是52),因此您希望头的内存消耗减少约350 MB,而更紧凑的数据存储的内存消耗减少1.5 GB(因为它主要是ASCII)
只需使用Py 3并将代码更改为:
with open('tokens.txt', 'r', encoding='utf-8') as f:
tokens = f.read().split()
import sys
print(sys.getsizeof(tokens))
print(sum(sys.getsizeof(t) for t in tokens))
您应该看到字符串的内存使用量大大减少,对于较长的字符串(例如,在我的Linux x64安装中,u'examplestring'
在Py2上是104字节,使用4字节/charunicode
,在Py3上只有62字节)
或者,作为一个廉价的黑客,当您知道Py2是纯ASCII时,您可以尝试将Py2上的unicode
转换回str
;在Py2上,这两种类型在很大程度上是可互操作的,str
的每个对象开销较小(37字节比52字节),并且只使用一个字节/字符。手动从unicode
转换回ASCII是可行的,尽管这会降低速度。为此,请将代码更改为:
# Open in binary mode
with open('tokens.txt', 'rb') as f:
# Defer decode and only do it for str with non-ASCII bytes
# producing list of mostly ASCII str with a few unicode objects
# when non-ASCII appears
tokens = [w.decode('utf-8') if max(w) > '\x7f' else w
for w in f.read().split()]
import sys
print sys.getsizeof(tokens)
print sum(sys.getsizeof(t) for t in tokens)
这将为每个对象头节省约1.7 GB的空间,在数据存储上节省约1.5 GB的空间,作为交换,您可能会遇到Py2所具有的
str
/unicode
互操作性怪癖(这也是Py3中分隔字节和str
的主要动机)。如果您好奇的话,x64 Py2unicode
使用的52个字节是:16个字节用于通用非GC-ed对象头(由一个8字节的refcnt和一个指向类对象的8字节指针组成),8个字节用于长度,8个字节用于指向数据的指针,8个字节用于缓存哈希存储,8个字节用于指向编码(str
)的(最初为空)指针用于缓冲区协议黑客Py2允许的数据版本。添加字符数组的四个字节(对于空的unicode
,它只是NUL终止符),在存储任何实际数据之前,这是52个字节。
# Open in binary mode
with open('tokens.txt', 'rb') as f:
# Defer decode and only do it for str with non-ASCII bytes
# producing list of mostly ASCII str with a few unicode objects
# when non-ASCII appears
tokens = [w.decode('utf-8') if max(w) > '\x7f' else w
for w in f.read().split()]
import sys
print sys.getsizeof(tokens)
print sum(sys.getsizeof(t) for t in tokens)