我试图从包含大约300万个json序列化对象的文件中加载数据集.每个对象都是一个包含各种类型的大型嵌套字典 - 整数,浮点数,列表和其他字典.
磁盘上文件的大小约为60GB.我有超过128GB的内存,所以我应该能够将整个内存放入内存中.但是,当我使用以下代码将数据加载到大型字典中时,已使用内存的大小增加到至少110GB(它甚至可能会变大但我在脚本增长之前就停止了.)
什么可以解释我在尝试加载此数据时看到的内存开销?为什么60GB磁盘会在内存中转换为110GB或更多?据我所知,这里唯一的开销应该是为对象创建列表容器,以及在结果字典中为这些列表分配一个键名.这不可能占据数据本身几乎两倍的内存 - 可以吗?
def load_by_geohash(file, specificity=7):
results = defaultdict(list)
filename = os.path.join(DATADIR, file)
with open(filename, 'r') as f:
updates = (json.loads(line) for line in f)
for update in updates:
geo_hash = update['geohash'][:specificity]
results[geo_hash].append(update)
return results
Run Code Online (Sandbox Code Playgroud)
是的,很容易就可以.考虑一个字符串列表的简单情况:
>>> import json
>>> from sys import getsizeof
>>> x = ['a string', 'another string', 'yet another']
>>> sum(map(getsizeof, x)) + getsizeof(x)
268
>>> len(json.dumps(x).encode())
45
>>>
Run Code Online (Sandbox Code Playgroud)
在Python中,一切都是对象.因此,每个(好的,大多数)单个对象至少具有 sys.getsizeof(object())开销.注意,我的系统上有一个空字符串:
>>> getsizeof('')
49
Run Code Online (Sandbox Code Playgroud)
请注意,dict对象的这种差异甚至更大,请考虑:
>>> d
{'a': 'a string', 'b': 'another string', 'c': 'yet another'}
>>> sum(map(getsizeof, d)) + sum(map(getsizeof, d.values())) + getsizeof(d)
570
>>> len(json.dumps(d).encode())
60
Run Code Online (Sandbox Code Playgroud)
对于空字典来说,这是非常巨大的:
>>> getsizeof({}), len(json.dumps({}).encode())
(240, 2)
Run Code Online (Sandbox Code Playgroud)
现在,有各种选项可以更紧凑地存储您的数据.但这取决于您的用例.
这是一个关于许多字典的内存使用情况的相关问题.还有一个使用numpy数组和namedtuple对象来更紧凑地存储数据的示例.请注意,使用namedtuple对象可能是您需要的,节省的内存可能很大,因为您不需要为键存储实际的字符串对象.如果您的子词典结构是常规的,我建议用update嵌套namedtuple对象替换那些嵌套的词典.