Разве Пеп 412 сделать резервные слоты?



PEP 412 , реализованный в Python 3.3, вводит улучшенную обработку словарей атрибутов, эффективно уменьшая объем памяти экземпляров класса. __slots__ был разработан для той же цели, так есть ли смысл в использовании __slots__ еще?



В попытке самостоятельно найти ответ я провожу следующий тест, но результаты не имеют особого смысла:

class Slots(object):
__slots__ = ['a', 'b', 'c', 'd', 'e']
def __init__(self):
self.a = 1
self.b = 1
self.c = 1
self.d = 1
self.e = 1

class NoSlots(object):
def __init__(self):
self.a = 1
self.b = 1
self.c = 1
self.d = 1
self.e = 1


Python 3.3 Результаты:



>>> sys.getsizeof([Slots() for i in range(1000)])
Out[1]: 9024
>>> sys.getsizeof([NoSlots() for i in range(1000)])
Out[1]: 9024


Python 2.7 Результаты:



>>> sys.getsizeof([Slots() for i in range(1000)])
Out[1]: 4516
>>> sys.getsizeof([NoSlots() for i in range(1000)])
Out[1]: 4516


Я бы так и сделал. я ожидал, что размер будет отличаться, по крайней мере, для Python 2.7, поэтому я предполагаю, что с тестом что-то не так.

558   2  

2 ответов:

Нет, PEP 412 не делает __slots__ избыточным.


Во-первых, Армин Риго прав, что вы не измеряете его должным образом. То, что вам нужно измерить, - это размер объекта плюс значения, плюс сам __dict__ (только для NoSlots) и ключи (только для NoSlots).

Или вы можете сделать то, что он предлагает:

cls = Slots if len(sys.argv) > 1 else NoSlots
def f():
    tracemalloc.start()
    objs = [cls() for _ in range(100000)]
    print(tracemalloc.get_traced_memory())
f()

Когда я запускаю это на 64-битном CPython 3.4 на OS X, я получаю 8824968 для NoSlots и 25624872 для Slots. Итак, похоже, что экземпляр NoSlots занимает 88 байт, в то время как экземпляр Slots занимает 256 байт.


Как это возможно?

Потому что между __slots__ и разделением ключа __dict__ все еще есть две разницы.

Во-первых, хэш-таблицы, используемые словарями, хранятся ниже 2/3rds полных, и они растут экспоненциально и имеют минимальный размер, поэтому у вас будет некоторое дополнительное пространство. И нетрудно вычислить, сколько места, посмотрев на красиво прокомментированный источник : у вас будет 8 ведер хэша вместо 5 слотов указателей. Во-вторых, словарь сам по себе не свободен; он имеет стандартный заголовок объекта, счетчик и два указателя. Это может показаться не очень много, но когда вы говорите об объекте, который имеет только несколько атрибутов (обратите внимание, что большинство объектов имеют только несколько атрибутов...), заголовок dict может иметь такое же значение, как и хэш-таблица.

И, конечно, в вашем примере, значения, так что единственная стоимость, связанная здесь, - это сам объект, плюс 5 слотов или 8 хэш-ведер и заголовок dict, так что разница довольно драматична. В реальной жизни __slots__ редко будет , что большая польза.


Наконец, обратите внимание, что PEP 412 только утверждает:

Бенчмаркинг показывает, что использование памяти для объектно-ориентированных программ сокращается на 10-20%

Думать о том, где вы используете __slots__. Либо экономия настолько велика, что не использовать __slots__ было бы смешно, либо вам действительно нужно выжать эти последние 15%. Или вы создаете ABC или другой класс, который, как вы ожидаете, будет подклассом кто-знает-чего, и подклассы могут нуждаться в экономии. Во всяком случае, в этих случаях тот факт, что вы получаете половину выгоды без __slots__ или даже две трети выгоды, все еще редко будет достаточным; вам все еще нужно будет использовать __slots__.

Реальный выигрыш-в тех случаях, когда его не стоит использовать __slots__; вы получите небольшую выгоду бесплатно.

(Кроме того, есть определенно некоторые программисты, которые злоупотребляют и, возможно, это изменение сможет убедить некоторых из них вложить свою энергию в микро-оптимизацию чего-то еще, не столь несущественного, если Вам ПОВЕЗЕТ.)

Проблема в том, что sys.getsizeof() редко возвращает то, что вы ожидаете. Например, в этом случае он подсчитывает "размер" объекта без учета размера его __dict__. Я предлагаю вам повторить попытку, измерив реальное использование памяти при создании 100 000 экземпляров.

Обратите также внимание, что поведение Python 3.3 было вдохновлено PyPy, в котором __slots__ не имеет никакого значения, поэтому я ожидал бы, что это не будет иметь никакого значения и в Python 3.3. Насколько я могу судить, __slots__ теперь почти никогда не используется.

Comments

    Ничего не найдено.