【问题标题】:cPickle.load() in python consumes a large memorypython中的cPickle.load()消耗大量内存
【发布时间】:2014-04-24 06:40:40
【问题描述】:

我有一个大字典,其结构如下:

dcPaths = {'id_jola_001': CPath instance}

其中CPath是自定义类:

class CPath(object):
    def __init__(self):
        # some attributes
        self.m_dAvgSpeed = 0.0
        ...
        # a list of CNode instance
        self.m_lsNodes = []

其中 m_lsNodes 是 CNode 的列表:

class CNode(object):
    def __init__(self):
        # some attributes
        self.m_nLoc = 0

        # a list of Apps
        self.m_lsApps = []

这里m_lsApps是一个CApp的列表,是另一个自定义类:

class CApp(object):
    def __init__(self):
        # some attributes
        self.m_nCount= 0
        self.m_nUpPackets = 0

我使用 cPickle 序列化这个字典:

def serialize2File(strFileName, strOutDir, obj):
    if len(obj) != 0:
        strOutFilePath = "%s%s" % (strOutDir, strFileName)
        with open(strOutFilePath, 'w') as hOutFile:
            cPickle.dump(obj, hOutFile, protocol=0)
        return strOutFilePath
    else:
        print("Nothing to serialize!")

它工作正常,序列化文件的大小约为 6.8GB。但是,当我尝试反序列化此对象时:

def deserializeFromFile(strFilePath):
    obj = 0
    with open(strFilePath) as hFile:
        obj = cPickle.load(hFile)
    return obj

我发现它消耗超过 90GB 的内存并且需要很长时间。

  1. 为什么会发生这种情况?
  2. 有什么办法可以优化这个吗?

顺便说一句,我使用的是 python 2.7.6

【问题讨论】:

  • 进程在将数据转储到pickle之前消耗了多少内存?
  • 这可能取决于存储类的定义方式,因此添加代码会有所帮助。
  • 你确定是反序列化吗?众所周知,序列化会占用更多内存来处理循环引用。
  • 1.您能告诉我们:最常用的对象是哪种类型? 2. 你能用最低的协议转储它并显示/链接一些更小的块吗? 3. 你使用 Python 3 吗? 4.如果用pickle,问题还存在吗?
  • @bereal 我添加了存储类的代码

标签: python memory pickle


【解决方案1】:

你可以尝试指定pickle协议;最快的是-1(意思是:最新的 协议,如果您使用相同的 Python 版本进行酸洗和解酸,则没有问题。

cPickle.dump(obj, file, protocol = -1)

编辑: 正如 cmets 中所说:load 检测协议本身。

cPickle.load(obj, file)

【讨论】:

  • 我可以使用协议 = 0 转储并使用协议 = -1 加载吗?因为我已经使用 protocol = 0 转储了数据,现在的问题是如何将其加载回来?另外,我在cPickle.load()中找不到协议参数。
  • @ice_lin load 检测协议本身,只能在dump/dumps 上指定。但协议 0 效率不高,至少可以部分解释您看到的问题。
  • @ice_lin:加载使用协议 0 转储的数据,然后使用协议 = -1 重新转储它们...
【解决方案2】:

当你存储复杂的python对象时,python通常会存储很多无用的数据(看__dict__对象属性)。

为了减少非序列化数据的内存消耗,您应该只腌制 python 本机。您可以轻松地在您的类上实现一些方法:object.__getstate__()object.__setstate__(state)

请参阅 Python 文档中的 Pickling and unpickling normal class instances

【讨论】:

  • __dict__ 是表示 python 对象的标准方式,它在酸洗之前也会存在,所以它不会解释内存增加。
  • @KillianDS 但是很多引用的东西也被序列化了,因为不可能在新环境中将自定义对象的引用更新为新的对应对象。 Pickle 做了一些魔法来最小化这种影响(在模块级别解析类型检查),但是在 unpickling 时不需要的实例(如单例)会被序列化和复制。
猜你喜欢
  • 2022-01-16
  • 2014-01-04
  • 2012-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多