繁体   English   中英

Python 3:在许多小对象上节省RAM内存

[英]Python 3: save RAM memory on many small objects

我正在开发一个在运行时创建数万个小型Python对象的应用程序。 不幸的是,Python对象因占用大量RAM而臭名昭著。 我发现了这篇有关如何解决该问题的有趣文章:

http://www.qtrac.eu/pysavemem.html

本文提供了一些有用的技巧,但并没有完全解释它们。 我不能为某些建议的解决方案而烦恼。 请帮助我获得见识。 我们将一一介绍。


1.没有优化

本文的基线示例是简单的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。


2. __slots__技术

迄今为止, __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

必须事先声明对象的属性x1x2y1y2 您不能向此类创建的对象添加任意额外的数据。
这些实例仅消耗212KB RAM。 内存大小几乎减少了50%。


3.“单一Python对象”技术

到目前为止, Rect()实例将分别产生四个内部对象: x1x2y1y2 随后的新技术尝试做不同的事情。 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"参数是否表示x1x2y1y2的类型为long

  • 文章说,该示例也可以扩展为具有可写属性。 看起来怎么样?


4.“单一Python对象”技术(缩短的代码)

最后,本文提供了一个类似的解决方案,但代码更短:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM