jka*_*imi 4 python memory nlp utf-8 python-2.7
我有一个 543 MB 的 txt 文件,其中包含一行空格分隔的 utf-8 标记:
aaa algeria americansamoa appliedethics accessiblecomputing ada anarchism ...
Run Code Online (Sandbox Code Playgroud)
但是,当我将此文本数据加载到 python 列表中时,它使用了 ~8 GB 的内存(~900 MB 用于列表,~8 GB 用于令牌):
with open('tokens.txt', 'r') as f:
tokens = f.read().decode('utf-8').split()
import sys
print sys.getsizeof(tokens)
# 917450944 bytes for the list
print sum(sys.getsizeof(t) for t in tokens)
# 7067732908 bytes for the actual tokens
Run Code Online (Sandbox Code Playgroud)
我预计内存使用量大约为文件大小 + 列表开销 = 1.5 GB。为什么令牌在加载到列表中时会消耗更多内存?
两个原因:
CPython 中的每个字符串在其 C 对象头文件中都有相当多的样板文件;在 Python 2 64 位系统上,空unicode对象使用 52 个字节,这是每个 unicode对象的固定开销,甚至在计算它包含的数据之前。如果您有 1.14M 个unicode对象(不是像 那样的单例u''),那么仅每个对象的开销就使用了近 6 GB。
您使用的是 Python 2 和decodeing from strto unicode,这取决于您对 Python 2 的构建配置,每个字符使用固定的 2 或 4 个字节,即使对于纯 ASCII 字符串;根据您的数字,您使用的是 4 个字节/字符的系统。因此,数据不会超过对象标头开销 543 MB,而是需要超过 2 GB。
头文件的问题在很大程度上是无法解决的(Python 对象总是会在头文件上浪费几十个字节);每个 Python 对象都有很高的固定开销(如前所述,sys.getsizeof(u'')在我的 x64 系统上是 52,尽管只存储了 8 个字节的“真实”数据,即str长度)。
但是由于您的输入主要是 ASCII,您可以通过迁移到 Python 3 来减少内存使用量;在现代 Py3(3.3+ IIRC)中,它们使用动态大小的存储str;一个str只使用ASCII / Latin-1字符将每个字符使用一个字节(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))
Run Code Online (Sandbox Code Playgroud)
并且您应该看到字符串的内存使用量减少了,在字符串较长的情况下会显着减少(例如,在我的 Linux x64 安装中,u'examplestring'在 Py2 上使用 4 个字节/字符编译为 104 个字节unicode,而 Py3 上仅为 62 字节)。
另外,作为一种廉价的黑客,你可以尝试从转换回unicode到str上的Py2当你知道它是纯ASCII; 在 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)
Run Code Online (Sandbox Code Playgroud)
这应该为每个对象的标头节省约 1.7 GB,并在数据存储上节省约 1.5 GB,以换取可能让您了解 Py2 具有的str/unicode互操作性怪癖(并且是分离bytes和导入的很大一部分动机str) py 3)。
| 归档时间: |
|
| 查看次数: |
288 次 |
| 最近记录: |