2012-07-02 24 views
7

Estoy tratando de encontrar de manera precisa/definitiva las diferencias de tamaño entre dos clases diferentes en Python. Ambas son clases de estilo nuevas, excepto una que no tiene ranuras definidas. He intentado numerosas pruebas para determinar su diferencia de tamaño, pero siempre terminan siendo idénticas en el uso de la memoria.Medir el tamaño del objeto con precisión en Python - Sys.GetSizeOf no funciona

Hasta ahora he intentado sys.GetSizeOf (obj) y la función heapy heap(), sin resultados positivos. Código de ensayo es el siguiente:

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() 

Salida:

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.> 

Todo esto es con Python 2.6.0. También he tratado de anular sizeof métodos de la clase para tratar de determinar el tamaño sumando los sizeofs individuales, pero que no dió ningún resultado diferentes:

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__() 

resultados con el método sizeof anulados:

Sizeof test3_obj 80 
Sizeof test4_obj 80 

Respuesta

4

sys.getsizeof devuelve un número que es más especializada y menos útil que la gente piensa. De hecho, si aumenta el número de atributos a seis, su test3_obj permanece en 32, pero test4_obj salta a 48 bytes. Esto se debe a que getsizeof devuelve el tamaño de la estructura PyObject implementando el tipo, que para test3_obj no incluye el dict que contiene los atributos, pero para test4_obj, los atributos no se almacenan en un dict, se almacenan en ranuras, por lo que ellos son contabilizados en el tamaño.

Pero una clase definida con __slots__ toma menos memoria que una clase sin, precisamente porque no hay ningún dict que contenga los atributos.

¿Por qué anular __sizeof__? ¿Qué estás realmente tratando de lograr?

+0

El tamaño de la anulación fue para ver si el tamaño del método incorporado no midió correctamente el tamaño de las variables. –

+0

Entonces, ¿qué sugeriría es la mejor manera de determinar las diferencias de tamaño entre tales objetos simples? –

+0

Eso depende de por qué quieres saber el tamaño. ¿Que problema estas tratando de resolver? –

0

Primero compruebe el tamaño del proceso Pyton en el administrador de memoria de su sistema operativo sin muchos objetos.

Segundo haz muchos objetos de un tipo y comprueba el tamaño de nuevo.

Tercero hacer muchos objetos del otro tipo y comprobar el tamaño.

Repita esto algunas veces y si los tamaños de cada paso permanecen igual, tiene algo comparable.

+0

Tengo curiosidad sobre qué tipo de precisión me puede dar esto? Además ... Necesitaría una forma eficiente de ejecutar esto varias veces, y luego promediarlo todo. –

0

Es posible que desee utilizar una aplicación diferente para conseguir el tamaño de los objetos en la memoria:

>>> 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 
>>> 

Sólo asegúrese de que usted no da ningún iteradores infinitas a este código si desea una respuesta de vuelta.

+0

"Rendimiento de" ¿No es esa sintaxis específica de python3? –

+0

Sí, para cuando el resto del código se pueda ejecutar a través de '2to3.py'. La transferencia a la que 'yield from' no está disponible debería ser bastante fácil. –

+0

str no se debe iterar para verificar el tamaño de sus subcadenas de un char, propuse una edición que tenga esto en cuenta. – Adirio

0

Me encontré con un problema similar y terminé escribiendo mi propia ayuda para hacer el trabajo sucio. Verifique here

Cuestiones relacionadas