Python程序使用了过多的内存。

4
我从Heapy得到了这些结果,但是不清楚它们的确切含义。
 Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
     0 262539  59 36961284  48  36961284  48 dict (no owner)
     1  65536  15 34340864  45  71302148  93 dict of myobj.Container
     2  65536  15  2097152   3  73399300  96 myobj.Container

myobj是一个类,包含大约20个True/False值和20个数字值(这些值都可以存储在2字节中)。

我有一个256*256大小的数组。我真的不明白为什么它们会消耗35或70 MB的内存。如果可能的话,我想将其降低到10 MB以下。

对象内部的许多数据都组织成字典以便于访问。这些字典本身不会改变,而且毫无意义。它们会导致很大的开销吗?

将所有数据打包到1个数字中是否有益,使用位运算符?我应该能够将整个对象的数据存储在32或64字节中。我希望编译器会像其他语言一样自动执行此类操作,但它似乎做了相反的事情。

该类继承内置类型对象,没有任何原因,只是为了使用装饰器。这会导致很大的开销吗?

还好奇“dict(无所有者)”是什么意思,以及它占用了另外一半内存。

编辑:sys.getsizeof(myobj.Container)确实报告了450字节!这太疯狂了。我只使用字典是因为我需要根据索引访问数据。就我所知,编译器应该摆脱这些结构并直接访问值。有更好的方法吗?(我不认为列表是答案)


1
你可能想考虑在http://codereview.stackexchange.com上发布你的代码(或其中的一部分)。在那里,你将得到有关你实际代码的评论,这比仅基于你的描述和堆积结果的猜测要好。 - Winston Ewert
1
就我而言,编译器应该摆脱结构体并直接访问值。Python 几乎不进行任何优化。你认为是常量的东西在运行时可能会改变,例如 len(x) 可以返回不同的结果,即使 x 是相同的(因为你可以随时替换 len)。 - jfs
1个回答

6
Python无法消除这样的结构体开销,很抱歉。它的动态性使得这种编译器优化变得困难。但是我不知道任何一种语言可以消除通过将事物保留在字典中引入的开销。
dict(无所有者)可能包括您在对象内部创建的所有字典。它们被标记为无所有者,因为它们不是对象实例的字典。
你可以做的:
如果添加类属性__slots__ = ('the','names','of','fields'),python将使用更高效的类实现。它会摆脱用于保存属性的字典。
如果您的字典可以重写以使用列表,那么情况将会得到改善。列表比字典更节省内存。
为了获得最佳效率,您应该重新设计系统以使用numpy数组。您类中的每个属性都将成为一个大小为256 * 256的数组。在这种情况下,每个元素将以非常高效的空间方式存储。
或者,您可以检查PyPy。它提供了Python的替代实现,具有JIT以及各种时间/空间优化,可能会有所帮助。
sys.getsizeof没有报告您认为的内容。sys.getsizeof(myobj.Container)报告类对象的大小,而不是实际Container对象的大小。您想要sys.getsizeof(myobj.Container())或类似的东西。即使如此也不准确,因为它不包括除基本对象以外的任何内容。它不考虑保存属性的字典。它只会报告您报告中第三行的大小。

为这个类和另一个类定义了__slots__,内存使用量从100 MB减少到35 MB。我很惊讶Python自己不能做到这一点,并且字典浪费了这么多空间,这让我感到惊讶。你认为这通常适用于字典(因此我应该查看其他30 MB未解释的部分),还是仅适用于对象的__dict__?据我所知,它们正在分配具有巨大额外空间的字典,这是不必要的。是否有一种方法来定义有限大小的字典?如果内存使用仍然是问题,也会查看numpy数组。谢谢! - user1012037
1
当使用 __slots__ 时,不能在对象上随意添加属性;不使用 __slots__ 可以获得更大的灵活性,但会占用更多内存。现在,在大多数情况下,对象具有固定的属性列表,因此人们可能认为默认应该使用 slots;然而,我想这可能意味着对语言本身的重大更改。 - Eric O. Lebigot
升级到了Numpy数组,内存从100MB降到了5MB。 :) - user1012037
@user1012037,字典的优化是为了速度而非内存。它们在内部有很多空间来减少哈希冲突的概率。这对所有字典都是适用的。PyPy会自动为您执行类似于__slots__的操作。然而,将相同的优化应用于CPython会有一些问题,使其更加困难。 - Winston Ewert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接