【问题标题】:Memory efficiency: One large dictionary or a dictionary of smaller dictionaries?内存效率:一个大字典还是小字典的字典?
【发布时间】:2010-10-14 20:36:03
【问题描述】:

我正在用 Python (2.6) 编写一个应用程序,需要我使用字典作为数据存储。

我很好奇拥有一个大字典是否更节省内存,或者将其分解为许多(很多)较小的字典,然后拥有一个包含对所有较小字典的引用的“索引”字典字典。

我知道列表和字典通常会产生很多开销。我在某处读到,python 在内部分配了足够的空间,字典/列表项的数量是 2 的幂。

我对 python 还很陌生,所以我不确定是否还有其他类似的意外内部复杂性/惊喜,这对于我应该考虑的普通用户来说并不明显。

其中一个困难是知道 2 系统的力量如何计算“项目”?每个 key:pair 是否计为 1 个项目?知道这一点似乎很重要,因为如果您有一个 100 项的整体字典,那么将分配 100^2 项空间。如果您有 100 个单项字典(1 个键:对),那么每个字典只会分配 1^2(也就是没有额外分配)?

任何明确列出的信息都会非常有帮助!

【问题讨论】:

  • 更新:我知道它的实际数据量是相同的。但这更多是蟒蛇内部机制的问题。有没有办法玩弄它的分配系统,让一种方法比另一种更有效?
  • 您为什么要做出这个决定?你试过一本大字典吗?你没记忆了?是不是太慢了?在某些东西被破坏(即不起作用)之前,这听起来像是过早的优化。
  • 我敢肯定现在担心还为时过早。但我希望有一种公认的“最有效”的方式,并希望从公认的方式开始,这样如果它不能满足我的需求,我就不必进行重大重写。

标签: python memory dictionary performance


【解决方案1】:

三个建议:

  1. 使用一本字典。
    它更容易,更直接,并且其他人已经为您优化了这个问题。在您实际测量代码并将性能问题追溯到这部分之前,您没有理由不做简单直接的事情。

  2. 稍后优化。
    如果您真的担心性能问题,那么将问题抽象为一个类来包装您最终使用的任何查找机制并编写代码来使用该类。如果您发现需要其他数据结构以获得更高的性能,您可以稍后更改实现。

  3. 阅读哈希表。
    字典是hash tables,如果你担心它们的时间或空间开销,你应该阅读它们是如何实现的。这是基本的计算机科学。简短的是哈希表是:

    • 平均情况 O(1) 查找时间
    • O(n) 空间(预计大约 2n,取决于各种参数)

    我不知道你在哪里读到它们是 O(n^2) 空间,但如果它们是,那么它们就不会像当今大多数语言那样被广泛、实际使用.哈希表的这些优良特性有两个优点:

    1. O(1) 查找时间意味着您不会为拥有更大的字典而支付查找时间成本,因为查找时间不取决于大小。
    2. O(n) 空间意味着您将字典分解成更小的部分不会有任何收获。空间与元素的数量成线性关系,所以很多小字典不会比一个大字典占用更少的空间,反之亦然。如果它们是 O(n^2) 空间,这将是不正确的,但幸运的是,它们不是。

    这里还有一些可能会有所帮助的资源:

    • Wikipedia article on Hash Tables 很好地列出了哈希表中使用的各种查找和分配方案。
    • GNU Scheme documentation 很好地讨论了您可以期望哈希表占用多少空间,包括正式讨论为什么 “哈希表使用的空间量与表”。这可能会让您感兴趣。

    如果您发现确实需要优化字典实现,可以考虑以下几点:

    • 这里是 Python 字典的 C 源代码,以防您需要所有详细信息。这里有丰富的文档:
    • 这里有一个python implementation,以防您不喜欢阅读 C。
      (感谢Ben Peterson
    • Java Hashtable class docs 讨论了负载因子的工作原理,以及它们如何影响散列占用的空间。请注意,在您的负载系数和您需要重新散列 的频率之间进行权衡。重新散列的成本可能很高。

【讨论】:

【解决方案2】:

如果您使用的是 Python,那么您真的不应该首先担心这类事情。只需按照最适合您的需要的方式构建您的数据结构,而不是计算机的。

这有点过早优化的味道,而不是性能提升。如果确实存在瓶颈,请分析您的代码,但在此之前,只需让 Python 完成它的工作并专注于实际的编程任务,而不是底层机制。

【讨论】:

    【解决方案3】:

    “简单”通常比“聪明”好,特别是如果您没有经过测试的理由超越“简单”。无论如何,“内存效率”是一个模棱两可的术语,当您考虑持久化、序列化、缓存、交换以及其他人已经考虑过的一大堆其他东西时,需要权衡取舍,因此在大多数情况下您不会需要。

    认为“正确处理它的最简单方法”会在很久以后进行优化。

    【讨论】:

      【解决方案4】:

      过早的优化bla bla,不要这样做bla bla。

      我认为你误解了两个额外分配的力量。我认为它只是两个的乘数。 x*2,不是 x^2。

      我在各种 python 邮件列表中多次看到这个问题。

      关于记忆,这里是一个这样的讨论的释义版本(有问题的帖子想要存储数亿个整数):

      1. 如果您只想测试成员资格,set() 比 dict() 更节省空间
      2. gmpy 有一个用于存储密集整数集的位向量类型类
      3. 字典保持在 50% 和 30% 之间为空,一个条目大约为 12 个字节(尽管实际数量会因平台而有所不同)。

      所以,你拥有的对象越少,你要使用的内存就越少,你要做的查找就越少(因为你必须在索引中查找,然后在索引中进行第二次查找实际值)。

      像其他人说的,profile看你的瓶颈。保持成员资格 set() 和值 dict() 可能会更快,但您会使用更多内存。

      我还建议将其重新发布到特定于 python 的列表中,例如 comp.lang.python,其中有比我知识渊博得多的人,他们会给你各种有用的信息。

      【讨论】:

        【解决方案5】:

        如果您的字典太大以至于无法放入内存,您可能需要查看ZODB,这是一个非常成熟的 Python 对象数据库。

        数据库的“根”与字典具有相同的接口,您不需要一次将整个数据结构加载到内存中,例如您可以通过提供开始和结束键来迭代结构的一部分。

        它还提供事务和版本控制。

        【讨论】:

          【解决方案6】:

          老实说,无论是在性能还是内存使用方面,您都无法分辨出两者的区别。除非您处理数千万或更多的项目,否则性能或内存影响只是噪音。

          从您第二句话的措辞看来,一本大字典似乎是您的第一个倾向,并且与您要解决的问题更匹配。如果这是真的,那就去吧。关于 Python,您会发现,每个人都认为“正确”的解决方案几乎总是尽可能清晰和简单。

          【讨论】:

            【解决方案7】:

            通常,字典的字典除了性能原因之外很有用。即,它们允许您存储有关数据的上下文信息,而无需在对象本身上有额外的字段,并使查询数据子集的速度更快。

            就内存使用而言,一个大字典比多个小字典使用更少的内存是有道理的。请记住,如果您要嵌套字典,则每增加一层嵌套都会使您需要分配的字典数量大致翻倍。

            在查询速度方面,由于需要的查找次数增加,多个 dicts 将花费更长的时间。

            所以我认为回答这个问题的唯一方法是让您分析自己的代码。但是,我的建议是使用使您的代码最干净和最容易维护的方法。在 Python 的所有功能中,字典可能是为了获得最佳性能而进行了最严格的调整。

            【讨论】:

            • “较大的字典显然比较小的字典具有更长的查找时间”:错误。哈希表是平均的。案例 O(1) 时间。
            • “一个大字典比多个小字典使用更少的内存是有道理的”:这也是错误的。哈希表是 O(n) 空间。一本大字典和多本小字典的大小没有显着差异。见下文。
            • @tgambin - 正如我在答案中所说,空间效率是由于创建了多个字典。当然,当您分配更多对象时,将需要额外的空间。不过,您对查找速度是正确的。我修改了答案。
            • @Daniel:除非您为每个映射分配一个字典或接近它,否则每个额外对象的微小开销都无关紧要。字典里面的内容占主导地位,他将拥有与一个或多个字典相同数量的映射。
            • @Daniel:“每增加一层嵌套,您需要分配的字典数量大约会增加一倍”。如果你正在做这样的事情,那么你最终会得到用于 n 个映射的 O(nlogn) 空间,其中 log(n) 来自额外的字典。
            猜你喜欢
            • 1970-01-01
            • 2015-05-15
            • 2012-09-17
            • 2012-04-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-09-21
            • 1970-01-01
            相关资源
            最近更新 更多