在python中保存和处理内存中的大字典的有效方法


问题内容

正如我进行的一些测试一样,int =>
int(不同值)的3000万个项目的python字典可以很容易地在我的Mac上吃掉2G以上的内存。由于我只使用int到int
dict,所以有比使用python dict更好的解决方案吗?

我需要的一些要求是

  1. 内存效率更高,可保存数以千万计的int到int项
  2. 基本的dict方法,例如通过键获取值并迭代所有项目
  3. 易于序列化为字符串/二进制将是一个加号

更新,4.容易通过给定的键来获取子集,例如d.fromkeys([…])

谢谢。


问题答案:

基于Judy-array的解决方案似乎是我应该考虑的选择。我仍在寻找Python可以使用的良好实现。稍后将更新。

更新,

最后,我在http://code.google.com/p/py-judy/上尝试了Judy数组包装器。那里似乎没有任何文档,但是我尝试仅通过dir(…)其包和对象来找到其方法,但是它可以工作。

同样的实验,使用judy.JudyIntObjectMap,它以标准dict的1/3占用了〜986MB的内存。它还提供JudyIntSet,在某些特殊情况下,与JudyIntObjectMap相比,它不需要引用任何实际的Python对象作为值,因此可以节省更多内存。

(如以下进一步测试所示,JudyArray仅使用几MB到几十MB,实际上〜986MB的大部分实际上是由Python内存空间中的值对象使用的。)

这是一些对您有帮助的代码,

>>> import judy
>>> dir(judy)
['JudyIntObjectMap', 'JudyIntSet', '__doc__', '__file__', '__name__', '__package__']
>>> a=judy.JudyIntObjectMap()
>>> dir(a)
['__class__', '__contains__', '__delattr__', '__delitem__', '__doc__', '__format__', '__getattribute__', '__getitem__', '__hash__', '__init__', '__iter__', '__len__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', '__value_sizeof__', 'by_index', 'clear', 'get', 'iteritems', 'iterkeys', 'itervalues', 'pop']
>>> a[100]=1
>>> a[100]="str"
>>> a["str"]="str"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'non-integer keys not supported'
>>> for i in xrange(30000000):
...     a[i]=i+30000000   #finally eats ~986MB memory
...

更新,

好的,我们测试了一个30M int的JudyIntSet。

>>> a=judy.JudyIntSet()
>>> a.add(1111111111111111111111111)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: we only support integers in the range [0, 2**64-1]

它总共仅使用5.7MB来存储30M顺序的int数组[0,30000000),这可能是由于JudyArray的自动压缩所致。709MB以上是bcz,我使用range(…)而不是更合适的xrange(…)来生成数据。

因此,具有30M int的JudyArray核心的大小完全可以忽略。

如果有人知道更完整的Judy Array包装器实现,请告诉我,因为此包装器仅包装JudyIntObjectMap和JudyIntSet。对于int-int
dict,JudyIntObjectMap仍然需要真正的python对象。如果我们只做counter_add并设置值,那么最好将int值存储在C空间中,而不要使用python对象。希望有人有兴趣创建或介绍一个:)