如何减少字典的内存占用?

时间:2018-04-25 17:32:12

标签: python python-3.x dictionary

在我的应用程序中,我需要快速查找属性。在这种情况下,属性是字符串和字典列表的组合。这些属性存储在包装类中。让我们调用这个包装类Plane

class Plane(object):
    def __init__(self, name, properties):
        self.name = name
        self.properties = properties

    @classmethod
    def from_idx(cls, idx):
        if idx == 0:
            return cls("PaperPlane", [{"canFly": True}, {"isWaterProof": False}])
        if idx == 1:
            return cls("AirbusA380", [{"canFly": True}, {"isWaterProof": True}, {"hasPassengers": True}])

为了更好地使用这个类,我添加了一个简单的类方法来通过提供和整数来构造实例。

所以现在在我的应用程序中我有许多飞机,大约10,000,000。可以通过通用唯一ID(uuid)访问这些平面中的每一个。我需要的是快速查找:给定一个uuid,什么是Plane。自然的解决方案是一个字典。在dict中使用uuids生成平面并将此dict存储在文件中的简单类可能如下所示:

class PlaneLookup(object):
    def __init__(self):
        self.plane_dict = {}

    def generate(self, n_planes):
        for i in range(n_planes):
            plane_id = uuid.uuid4().hex
            self.plane_dict[plane_id] = Plane.from_idx(np.random.randint(0, 2))

    def save(self, filename):
        with gzip.open(filename, 'wb') as f:
            pickle.dump(self.plane_dict, f, pickle.HIGHEST_PROTOCOL)

    @classmethod
    def from_disk(cls, filename):
        pl = cls()
        with gzip.open(filename, 'rb') as f:
            pl.plane_dict = pickle.load(f)
        return pl

所以现在发生的事情是,如果我生成一些飞机?

pl = PlaneLookup()
pl.generate(1000000)

会发生什么,大量内存被消耗掉了!如果我用getsize()方法from this question检查pl对象的大小,我的64位机器上的值为1,087,286,831字节。看看htop,我的内存需求似乎更高(约2GB)。 In this question,很好地解释了为什么python词典需要很多内存。

但是,我认为在我的申请中不一定是这种情况。在PlaneLookup.generate()方法中创建的平面对象通常包含相同的属性(即相同的名称和相同的属性)。因此,必须可以在dict中保存此对象一次,并且每当再次创建相同的对象(相同名称,相同属性)时,仅存储对已存在的dict条目的引用。由于一个简单的Plane对象的大小为1147字节(根据getsize()方法),只需保存引用可以节省大量内存!

现在的问题是:我该怎么做?最后,我需要一个函数,它将uuid作为输入,并尽可能快地返回相应的Plane对象。 也许lru_cache可以提供帮助吗?

以下是完整的代码: https://pastebin.com/iTZyQQAU

1 个答案:

答案 0 :(得分:1)

您是否考虑使用另一个带有idx的字典 - >飞机吗?然后在self.plane_dict[plane_uuid]中你只会存储idx而不是object。这将节省内存并加快您的应用程序,但您需要修改查找方法。