2012-07-02 16 views
7

Я пытаюсь точно/окончательно найти разницу в размерах между двумя разными классами в Python. Они оба являются новыми классами стилей, за исключением одного, не имеющего слотов. Я пробовал многочисленные тесты, чтобы определить их разницу в размерах, но они всегда оказываются идентичными в использовании памяти.Измерение размера объекта Точно в Python - Sys.GetSizeOf не работает

До сих пор я пробовал функции sys.GetSizeOf (obj) и heapy's heap() без положительных результатов. Тест-код ниже:

import sys 
from guppy import hpy 

class test3(object): 
    def __init__(self): 
     self.one = 1 
     self.two = "two variable" 

class test4(object): 
    __slots__ = ('one', 'two') 
    def __init__(self): 
     self.one = 1 
     self.two = "two variable" 

test3_obj = test3() 
print "Sizeof test3_obj", sys.getsizeof(test3_obj) 

test4_obj = test4() 
print "Sizeof test4_obj", sys.getsizeof(test4_obj) 

arr_test3 = [] 
arr_test4 = [] 

for i in range(3000): 
    arr_test3.append(test3()) 
    arr_test4.append(test4()) 

h = hpy() 
print h.heap() 

Выход:

Sizeof test3_obj 32 
Sizeof test4_obj 32 

Partition of a set of 34717 objects. Total size = 2589028 bytes. 
Index Count %  Size % Cumulative % Kind (class/dict of class) 
    0 11896 34 765040 30 765040 30 str 
    1 3001 9 420140 16 1185180 46 dict of __main__.test3 
    2 5573 16 225240 9 1410420 54 tuple 
    3 348 1 167376 6 1577796 61 dict (no owner) 
    4 1567 5 106556 4 1684352 65 types.CodeType 
    5  68 0 105136 4 1789488 69 dict of module 
    6 183 1 97428 4 1886916 73 dict of type 
    7 3001 9 96032 4 1982948 77 __main__.test3 
    8 3001 9 96032 4 2078980 80 __main__.test4 
    9 203 1 90360 3 2169340 84 type 
<99 more rows. Type e.g. '_.more' to view.> 

Это все с Python 2.6.0. Я также попытался переопределить SizeOf методов класса, чтобы попытаться определить размер путем суммирования отдельных sizeofs, но это не дало никаких разные результаты:

class test4(object): 
    __slots__ = ('one', 'two') 
    def __init__(self): 
     self.one = 1 
     self.two = "two variable" 
    def __sizeof__(self): 
     return super(test4, self).__sizeof__() + self.one.__sizeof__() + self.two.__sizeof__() 

Результаты с методом SizeOf переопределены:

Sizeof test3_obj 80 
Sizeof test4_obj 80 

ответ

4

sys.getsizeof возвращает число, которое является более специализированным и менее полезным, чем люди думают. Фактически, если вы увеличите количество атрибутов до шести, ваш test3_obj останется на 32, но test4_obj перескакивает до 48 байт. Это связано с тем, что getizeof возвращает размер структуры PyObject, реализующий тип, который для test3_obj не включает в себя dict, содержащий атрибуты, но для test4_obj атрибуты не хранятся в dict, они хранятся в слотах, поэтому они учитываются в размере.

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

Зачем стоит __sizeof__? Чего вы действительно пытаетесь достичь?

+0

Значение sizeof должно было определить, не может ли быть встроенный метод sizeof неправильно измерять размер переменных. –

+0

Итак, что бы вы предложили, это лучший способ определить разницу в размерах между такими объектами простого иона? –

+0

Это зависит от того, почему вы хотите знать размер. Какую проблему ты пытаешься решить? –

0

Сначала проверьте размер процесса Pyton в диспетчере памяти os 'без множества объектов.

Второе сделать много объектов одного вида и снова проверить размер.

В-третьих, сделайте много объектов другого рода и проверьте размер.

Повторите это несколько раз, и если размеры каждого шага остаются примерно одинаковыми, у вас есть что-то сопоставимое.

+0

Мне любопытно, какую точность это меня достало? Также ... Мне нужен эффективный способ запускать это несколько раз, а затем усреднить все это. –

0

Вы можете использовать другую реализацию для получения размера ваших объектов в памяти:

>>> import sys, array 
>>> sizeof = lambda obj: sum(map(sys.getsizeof, explore(obj, set()))) 
>>> def explore(obj, memo): 
    loc = id(obj) 
    if loc not in memo: 
     memo.add(loc) 
     yield obj 
     if isinstance(obj, memoryview): 
      yield from explore(obj.obj, memo) 
     elif not isinstance(obj, (range, str, bytes, bytearray, array.array)): 
      # Handle instances with slots. 
      try: 
       slots = obj.__slots__ 
      except AttributeError: 
       pass 
      else: 
       for name in slots: 
        try: 
         attr = getattr(obj, name) 
        except AttributeError: 
         pass 
        else: 
         yield from explore(attr, memo) 
      # Handle instances with dict. 
      try: 
       attrs = obj.__dict__ 
      except AttributeError: 
       pass 
      else: 
       yield from explore(attrs, memo) 
      # Handle dicts or iterables. 
      for name in 'keys', 'values', '__iter__': 
       try: 
        attr = getattr(obj, name) 
       except AttributeError: 
        pass 
       else: 
        for item in attr(): 
         yield from explore(item, memo) 


>>> class Test1: 
    def __init__(self): 
     self.one = 1 
     self.two = 'two variable' 


>>> class Test2: 
    __slots__ = 'one', 'two' 
    def __init__(self): 
     self.one = 1 
     self.two = 'two variable' 


>>> print('sizeof(Test1()) ==', sizeof(Test1())) 
sizeof(Test1()) == 361 
>>> print('sizeof(Test2()) ==', sizeof(Test2())) 
sizeof(Test2()) == 145 
>>> array_test1, array_test2 = [], [] 
>>> for _ in range(3000): 
    array_test1.append(Test1()) 
    array_test2.append(Test2()) 


>>> print('sizeof(array_test1) ==', sizeof(array_test1)) 
sizeof(array_test1) == 530929 
>>> print('sizeof(array_test2) ==', sizeof(array_test2)) 
sizeof(array_test2) == 194825 
>>> 

Просто убедитесь, что вы не даете никаких бесконечных итераторов этому коду, если вы хотите получить ответ обратно.

+0

«Выход из» Не является ли этот синтаксис специфичным для python3? –

+0

Да, если остальная часть кода может быть запущена через '2to3.py'. Портирование туда, где 'yield from' не доступно, должно быть довольно простым. –

+0

str не следует перебирать, чтобы проверить размеры подстроки одного символа, я предложил изменение, которое учитывает это. – Adirio

0

Я столкнулся с подобной проблемой и в итоге написал своего помощника, чтобы выполнить грязную работу. Проверьте это here