在构建列表的大型字典时爆炸内存使用

时间:2018-04-05 22:51:12

标签: python dictionary memory out-of-memory

我从文本标记化过程中生成大量嵌套列表,并希望将它们存储在dict中,其中的键与源dict中字符串的键相关。

源词典的例子:

{
    '1' : 'the horse',
    '2' : 'grass is green',
    ...
}

所需输出的示例,其中整数是标记化和散列过程的输出:

{
    '1' : [[1342, 24352, 524354, 356345],
           [35663, 53635, 25245, 457577]],
    '2' : [[43412, 324423, 66546, 86887],
           [398908, 46523, 432432, 9854],
           [87667, 34423, 132132, 35454]],
    ...
}

当我遍历我的源dict时,为我的标记化函数提供值并将键,标记化值对分配给一个新的dict,我的新dict使用的是大量的内存,比实际大空间它应该占用。

这里有一些模拟代码说明了我的问题:

import gc
import numpy as np
import matplotlib.pyplot as plt
import sys
import json

import os
import psutil

pid = os.getpid()
py = psutil.Process(pid)

def memory_use():
    memoryUse = py.memory_info()[0]/2.**30  # memory use in GB
    return memoryUse

def tokenize_sim():
    # returns a list of 30 lists of 4 random ints
    # (simulates 30 tokenized words)
    return [[int(n) for n in np.random.randint(low=0, high=1e6, size=4)] for i in range(31)]

memory = []
tokens = dict()
for i in range(800001):
    tokens[i] = tokenize_sim()
    if i % 50000 == 0:
        memoryUse = memory_use()
        print(i, '- memory use:', memoryUse)
        memory.append(memoryUse)

plt.figure()
plt.plot(np.arange(17)*50, memory)
plt.grid()
plt.xlabel('Thousands of iterations')
plt.ylabel('Memory used in GB')
plt.show()

print('System size in bytes:', sys.getsizeof(tokens))

这是内存使用情况:

Linear growth of memory usage

sys.getsizeof(tokens)返回41943144个字节。我尝试将此dict写入json文件,并使用821 MB。这些都没有接近6 GB的内存,而这正在吞噬。

我在这里缺少什么?我猜它有一些内存分配问题,但我还没有设法找到任何解决方案。我需要处理大约1200万个条目的源字典,而我的64 GB内存似乎不足以构建列表的字典。任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:1)

除了绘制内存使用情况之外,我无法真正掌握你正在做的事情(可能想要删除与问题无关的代码),但我可以给你一些如何处理批量的一般指示数据和为什么它会占用你所有的记忆。

原因。就效率和速度而言,Python并不是一种真正的高性能语言。在Python中,一切都是对象,每个对象都有自己的属性。当您创建嵌套的属性列表时,父列表,所有嵌套列表和每个小整数都有自己的东西附加到它们 - 他们自己的元数据和描述。当你在不同的对象上调用dir()时,你可以想象出来 - 例如,Python中的一个整数有69种不同的方法和属性。所有这些都必须适合内存,这就是为什么你的内存比JSON格式的实际大小更快地吞噬,因为JSON格式没有任何关于数据的元数据。

如何打击?有些语言可以更好地处理大量数据,因为它们不是那么开发人员友好,并且每一步都会关注你。您可以切换到C并使用8 GB RAM完成任务 但我不建议转换语言,只是使用一些更好的做法。现在你把所有数据都保存在列表和词组中(?),这些数据确实效率不高。查看 numpy pandas 可以为您做什么 - 它们完全适用于此类用例。它们在C中实现,它提供了更好的性能,同时拥有一个使用相当方便的Python API。

如果这还不够,那就去做你正在做的事情。如果你想要的只是一个庞大的列表列表,其中包含一些整数,你可以在迭代中完成它,同时按间隔保存所有列表,同时你不需要在内存中全部使用它。如果Python本机垃圾收集还不够,您可以尝试通过del巨大的变量并调用垃圾收集来强制Python释放内存。

import gc
del big_bad_variable
gc.collect()