[英]Python 3: save RAM memory on many small objects
我正在开发一个在运行时创建数万个小型Python对象的应用程序。 不幸的是,Python对象因占用大量RAM而臭名昭著。 我发现了这篇有关如何解决该问题的有趣文章:
http://www.qtrac.eu/pysavemem.html
本文提供了一些有用的技巧,但并没有完全解释它们。 我不能为某些建议的解决方案而烦恼。 请帮助我获得见识。 我们将一一介绍。
本文的基线示例是简单的Rect
类:
class Rect:
def __init__(self, x1, y1, x2, y2):
self.x1 = x1
self.x2 = x2
self.y1 = y1
self.y2 = y2
在运行64位Python 3的64位计算机上,此对象将消耗400KB。
迄今为止, __slots__
技术是最简单的优化。 这是文章中的示例:
class Rect:
__slots__ = ("x1", "x2", "y1", "y2")
def __init__(self, x1, y1, x2, y2):
self.x1 = x1
self.x2 = x2
self.y1 = y1
self.y2 = y2
必须事先声明对象的属性x1
, x2
, y1
和y2
。 您不能向此类创建的对象添加任意额外的数据。
这些实例仅消耗212KB RAM。 内存大小几乎减少了50%。
到目前为止, Rect()
实例将分别产生四个内部对象: x1
, x2
, y1
和y2
。 随后的新技术尝试做不同的事情。 Python object gets created: 代替了四个对象,仅创建了 Python对象:
class Rect:
__slots__ = ("_data",)
# We are not limited to using the same types; could mix any
# fixed-width types we want. And, of course, we can add extra
# items to the struct later if need be.
Coords = struct.Struct("llll")
def __init__(self, x1, y1, x2, y2):
self._data = Rect.Coords.pack(x1, y1, x2, y2)
@property
def x1(self):
return Rect.Coords.unpack(self._data)[0]
@property
def x2(self):
return Rect.Coords.unpack(self._data)[1]
@property
def y1(self):
return Rect.Coords.unpack(self._data)[2]
@property
def y2(self):
return Rect.Coords.unpack(self._data)[3]
该文章指出,消耗的内存现在只有137KB。 但是,它没有解释如何。 我无法绕过某些表达式:
__slots__ = ("_data",)
实际作用是什么?
Coords
是类成员,而不是实例成员。 那么,如何以这种方式为每个实例获取不同的数据呢?
这些pack()
和unpack()
方法实际上是做什么的?
Struct()
的"llll"
参数是否表示x1
, x2
, y1
和y2
的类型为long
?
文章说,该示例也可以扩展为具有可写属性。 看起来怎么样?
最后,本文提供了一个类似的解决方案,但代码更短:
def _make_unpacker(index):
return lambda self: operator.itemgetter(index)(
Rect.Coords.unpack(self._data))
class Rect:
__slots__ = ("_data",)
Coords = struct.Struct("llll")
def __init__(self, x1, y1, x2, y2):
self._data = Rect.Coords.pack(x1, y1, x2, y2)
x1 = property(_make_unpacker(0))
x2 = property(_make_unpacker(1))
y1 = property(_make_unpacker(2))
y2 = property(_make_unpacker(3))
这个解决方案对我来说还不清楚。
您为解释这些优化技术所做的努力将不胜感激! 如果适用,请随时提出其他解决方案。 我个人使用最新的Python 3.7版本。
在记录类库的基础上还有另一种方法:
from recordclass import dataobject
class Rectangle(dataobject):
x1:int
x2:int
y1:int
y2:int
与基于__slots__
的解决方案相比,此解决方案所需的内存更少。 差异等于PyGC_Head
的大小(在64位平台上为24个字节)。 它也可能比基于__slots__
的解决方案具有更快的实例创建路径:
class Rectangle(dataobject):
x1:int
x2:int
y1:int
y2:int
__options__ = {'argsonly':True}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.