Python 3: экономьте оперативную память на многих маленьких объектах - PullRequest
0 голосов
/ 01 декабря 2018

Я работаю над приложением, которое создает десятки тысяч маленьких объектов Python во время выполнения.К сожалению, объекты Python печально известны тем, что потребляют много оперативной памяти.Я нашел эту интересную статью о том, как решить эту проблему:

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-разрядной машине с 64-разрядной версией Python 3 этот объект будет занимать 400 КБ.


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

Необходимо заранее объявить атрибуты объекта x1, x2, y1 и y2.Вы не можете добавить произвольные дополнительные данные к объектам, созданным из этого класса.
Экземпляры будут использовать только 212 КБ ОЗУ.Это почти на 50% меньше объема памяти.


3.Техника «одного объекта Python»

До сих пор экземпляры Rect() порождали четыре внутренних объекта каждый: для x1, x2, y1 и y2.Новая техника, которая следует, пытается сделать по-другому.Вместо четырех объектов создается только один Python-объект *1046*:

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]

В статье говорится, что объем используемой памяти теперь составляет всего 137 КБ.Однако это не объясняет как.Я не могу обернуть голову вокруг некоторых выражений:

  • Что на самом деле делает __slots__ = ("_data",)?

  • Coords являетсячлен класса, а не член экземпляра.Так как же получить разные данные для каждого экземпляра таким образом?

  • Что на самом деле делают эти методы pack() и unpack()?

  • Аргумент "llll" о Struct(), означает ли это, что x1, x2, y1 и y2 имеют тип 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.

1 Ответ

0 голосов
/ 07 июня 2019

Существует еще один подход на основе библиотеки recordclass :

from recordclass import dataobject

class Rectangle(dataobject):
    x1:int
    x2:int
    y1:int
    y2:int

Это решение требует меньше памяти, чем основанное на __slots__.Разница равна размеру PyGC_Head (24 байта на 64-битной платформе).Он также может иметь более быстрый путь создания экземпляра, чем решение на основе __slots__:

class Rectangle(dataobject):
    x1:int
    x2:int
    y1:int
    y2:int
    __options__ = {'argsonly':True}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...