内存优化的Python技巧

时间:2010-06-11 08:22:46

标签: python optimization memory-management

我需要优化应用程序的RAM使用率 请不要在讲课时告诉我在编写Python时我不应该关心内存。我有一个内存问题,因为我使用非常大的默认字典(是的,我也想要快速)。我目前的内存消耗是350MB并且还在增长。我已经不能使用共享主机了,如果我的Apache打开更多进程,内存会翻倍并且会增加三倍......而且它很昂贵 我已经完成了广泛的分析,我确切地知道我的问题在哪里 我有几个带有Unicode密钥的大型(> 100K条目)词典。字典从140字节开始并且增长很快,但更大的问题是密钥。 Python优化了内存中的字符串(或者我已经读过),因此查找可以进行ID比较('实际')。不确定这对于unicode字符串也是如此(我无法'实习'它们) 存储在字典中的对象是元组列表(an_object,int,int)。

  

my_big_dict [some_unicode_string] .append((my_object,an_int,another_int))

我已经发现分成几个词典是值得的,因为元组占用了很多空间...
我发现在使用它们作为键之前我可以通过散列字符串来节省RAM! 但是,遗憾的是,我遇到了32位系统的生日碰撞。 (附带问题:我可以在32位系统上使用64位密钥字典吗?)

Linux(生产)和Windows上的Python 2.6.5。 有关优化字典/列表/元组的内存使用的任何提示? 我甚至想过使用C - 我不在乎这段很小的代码是否丑陋。这只是一个独特的地方。

提前致谢!

7 个答案:

答案 0 :(得分:11)

我建议如下:将所有值存储在DB中,并使用字符串哈希作为键保留内存中的字典。如果发生冲突,则从DB获取值,否则(绝大多数情况下)使用字典。实际上,它将是一个巨大的缓存。

Python中字典的一个问题是它们占用了大量空间:即使是int-int字典,在32位系统上每个键值对使用 45-80字节。同时,array.array('i')每一个整数只使用 8个字节,并且只需一点​​记账,就可以实现一个相当快速的基于数组的 int→int 字典。

一旦有了int-int字典的内存效率实现,将 string→(object,int,int)字典拆分为三个字典,并使用散列而不是完整字符串。您将获得一个 int→object 和两个 int→int 词典。模拟 int→object 字典,如下所示:保留对象列表并将对象的索引存储为 int→int 字典的值。

我确实意识到要获得基于数组的字典需要相当多的编码。我遇到了类似于你的问题,我已经实现了一个相当快速,非常高效,通用的hash-int字典。 Here's my code(BSD许可证)。它是基于数组的(每对8个字节),它负责密钥散列和冲突检查,它在写入期间保持数组(实际上是几个较小的数组),并在读取时进行二进制搜索。您的代码缩减为:

dictionary = HashIntDict(checking = HashIntDict.CHK_SHOUTING)
# ...
database.store(k, v)
try:
    dictionary[k] = v
except CollisionError:
    pass
# ...
try:
    v = dictionary[k]
except CollisionError:
    v = database.fetch(k)

checking参数指定发生冲突时会发生什么:CHK_SHOUTING在读取和写入时引发CollisionErrorCHK_DELETING在读取时返回None并保持静默写入时,CHK_IGNORING不进行冲突检查。

以下是对我的实施的简要说明,欢迎提供优化提示!顶级数据结构是数组的常规字典。每个数组最多包含2^16 = 65536个整数对(2^32的平方根)。密钥k和对应的值v都存储在k/65536 - 数组中。数组按需初始化并按键排序。每次读写都执行二进制搜索。碰撞检查是一种选择。如果启用,则尝试覆盖现有密钥将从字典中删除密钥和关联值,将密钥添加到一组冲突密钥,并(再次,可选)引发异常。

答案 1 :(得分:4)

对于Web应用程序,您应该使用数据库,就像您正在使用的方式一样,您为每个apache进程创建了一个dict副本,这非常浪费。如果服务器上有足够的内存,则数据库表将缓存在内存中(如果没有足够的表副本,请将更多RAM放入服务器)。只需记住在数据库表中放置正确的索引,否则会导致性能下降。

答案 2 :(得分:2)

我遇到过一些情况,我需要根据几个元数据属性对不同方法进行排序和过滤的大型对象集合。我不需要它们中较大的部分,所以我将它们转储到磁盘上。

由于数据类型如此简单,快速的SQLite数据库可能会解决您的所有问题,甚至可以加快速度。

答案 3 :(得分:1)

使用shelve或数据库存储数据而不是内存中的字典。

答案 4 :(得分:1)

如果您想继续使用内存数据存储,可以尝试memcached之类的内容。

这样,您可以使用来自所有Python进程的单个内存中键/值存储。

有几个python memcached客户端库。

答案 5 :(得分:1)

如果您可以选择在共享主机上使用它,那么

Redis将是一个很好的选择 - 类似于memcached,但针对数据结构进行了优化。 Redis还支持python绑定。

我每天都会使用它来进行数字运算,但是作为数据存储区在生产系统中使用它,并且不能足够推荐它。

另外,您是否可以选择在nginx后面代理您的应用而不是使用Apache?您可能会发现(如果允许)此代理/ Web应用程序安排对资源的负担较少。

祝你好运。

答案 6 :(得分:0)

如果您想进行广泛的优化并完全控制内存使用,您还可以编写C / C ++模块。使用Swig可以轻松完成Python中的代码包装,与纯C Python模块相比,性能开销较小。