提高 Python 中超大字典的性能

Posted

技术标签:

【中文标题】提高 Python 中超大字典的性能【英文标题】:Improving performance of very large dictionary in Python 【发布时间】:2013-04-21 20:04:24 【问题描述】:

我发现如果我一开始初始化一个空字典,然后在for循环中向字典中添加元素(大约110,000个键,每个键的值是一个列表,也在循环中增加),速度像 for 循环一样下降。

我怀疑问题是,字典在初始化时不知道键的数量并且它没有做一些非常聪明的事情,所以存储冲突可能变得非常频繁并且速度变慢。

如果我知道键的数量以及这些键的确切含义,python 中是否有任何方法可以使 dict(或哈希表)更有效地工作?依稀记得,如果知道key,就可以巧妙地设计hash函数(完美hash?),提前分配空间。

【问题讨论】:

哈希表的性能可以通过删除/减少冲突来提高。这可以通过预先分配最佳数量的桶来实现,或者从一组已知的密钥中创建一个完美的哈希函数。不幸的是,Python 字典无法让您对哈希表的内部进行低级访问,因此您无法以这种方式对其进行微调。 这个字典需要多少内存? (你是说列表的大小在增加吗?)可以用pympler 来衡量。如果大小导致 Python 使用交换内存,您可能会看到速度急剧下降。 【参考方案1】:

如果我知道键的数量以及这些键的确切含义,有吗 在 python 中使字典(或哈希表)工作得更多的任何方式 有效率的?我依稀记得,如果你知道钥匙,你可以 巧妙地设计散列函数(完美散列?)并分配 预先留出空间。

Python 没有公开预调整大小选项来加速字典的“成长阶段”,也没有提供对字典中“位置”的任何直接控制。

也就是说,如果密钥总是预先知道,您可以将它们存储在set 中,并使用dict.fromkeys() 从集合中构建您的字典。该类方法是optimized to pre-size the dictionary based on the set size,它可以填充字典而无需对 __hash__() 进行任何新调用:

>>> keys = 'red', 'green', 'blue', 'yellow', 'orange', 'pink', 'black'
>>> d = dict.fromkeys(keys)  # dict is pre-sized to 32 empty slots

如果您的目标是减少冲突,您可以对字典中的插入顺序进行实验,以尽量减少堆积。 (查看 Knuth 的 TAOCP 中的 Brent's variation on Algorithm D 以了解这是如何完成的)。

通过为字典检测纯 Python 模型(例如 this one),可以计算替代插入顺序的探测器的加权平均数。例如,插入 dict.fromkeys([11100, 22200, 44400, 33300]) 平均每次查找 1.75 个探测。这超过了 dict.fromkeys([33300, 22200, 11100, 44400]) 每次查找的 2.25 次平均探测。

另一个“诀窍”是通过将字典欺骗为increasing its size without adding new keys 来增加字典的冗余度:

 d = dict.fromkeys(['red', 'green', 'blue', 'yellow', 'orange'])
 d.update(dict(d))     # This makes room for additional keys
                       # and makes the set collision-free.

最后,您可以为您的密钥引入自己的自定义 __hash__(),以消除所有冲突(可能使用完美的哈希生成器,例如 gperf)。

【讨论】:

哎呀,为什么这没有得到更多的支持?我猜 Ray 的分数已经够多了 :)。

以上是关于提高 Python 中超大字典的性能的主要内容,如果未能解决你的问题,请参考以下文章

Python 3字典迭代中的性能:dict [key] vs. dict.items()

Python数据结构与算法 列表和字典性能比较

Python数据结构与算法 列表和字典性能比较

《Python高性能编程》——列表元组集合字典特性及创建过程

python 性能优化技巧

Python中字典和集合的区别与联系