Я работаю над приложением, которое создает десятки тысяч маленьких объектов 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.