Tamaño en memoria de una estructura de Python

118

¿Existe una referencia para el tamaño de la memoria de la estructura de datos de Python en plataformas de 32 y 64 bits?

De lo contrario, sería bueno tenerlo en SO. ¡Cuanto más exhaustivo, mejor! Entonces, ¿cuántos bytes utilizan las siguientes estructuras de Python (según el lentipo de contenido y cuando sea relevante)?

  • int
  • float
  • referencia
  • str
  • cadena unicode
  • tuple
  • list
  • dict
  • set
  • array.array
  • numpy.array
  • deque
  • objeto de clases de nuevo estilo
  • objeto de clases de estilo antiguo
  • ... y todo lo que me estoy olvidando!

(Para los contenedores que solo guardan referencias a otros objetos, obviamente no queremos contar el tamaño del elemento en sí, ya que podría ser compartido).

Además, ¿hay alguna forma de que un objeto utilice la memoria en tiempo de ejecución (recursivamente o no)?

LeMiz
fuente
Se pueden encontrar muchas explicaciones útiles aquí stackoverflow.com/questions/1059674/python-memory-model . Sin embargo, me gustaría ver una descripción general más sistemática
LeMiz
3
Para una matriz NumPy a, use a.nbytes.
Será
Si está interesado en una vista gráfica de esto, hice un gráfico una vez: stackoverflow.com/a/30008338/2087463
tmthydvnprt

Respuestas:

145

La recomendación de una pregunta anterior sobre esto fue usar sys.getsizeof () , citando:

>>> import sys
>>> x = 2
>>> sys.getsizeof(x)
14
>>> sys.getsizeof(sys.getsizeof)
32
>>> sys.getsizeof('this')
38
>>> sys.getsizeof('this also')
48

Podrías tomar este enfoque:

>>> import sys
>>> import decimal
>>> 
>>> d = {
...     "int": 0,
...     "float": 0.0,
...     "dict": dict(),
...     "set": set(),
...     "tuple": tuple(),
...     "list": list(),
...     "str": "a",
...     "unicode": u"a",
...     "decimal": decimal.Decimal(0),
...     "object": object(),
... }
>>> for k, v in sorted(d.iteritems()):
...     print k, sys.getsizeof(v)
...
decimal 40
dict 140
float 16
int 12
list 36
object 8
set 116
str 25
tuple 28
unicode 28

2012-09-30

python 2.7 (linux, 32 bits):

decimal 36
dict 136
float 16
int 12
list 32
object 8
set 112
str 22
tuple 24
unicode 32

python 3.3 (linux, 32 bits)

decimal 52
dict 144
float 16
int 14
list 32
object 8
set 112
str 26
tuple 24
unicode 26

2016-08-01

OSX, Python 2.7.10 (predeterminado, 23 de octubre de 2015, 19:19:21) [GCC 4.2.1 Compatible con Apple LLVM 7.0.0 (clang-700.0.59.5)] en darwin

decimal 80
dict 280
float 24
int 24
list 72
object 16
set 232
str 38
tuple 56
unicode 52
hughdbrown
fuente
1
Gracias, y perdón por el engaño de la segunda pregunta ... lástima que estoy usando 2.5 y no 2.6 ...
LeMiz
¡Olvidé que tenía una caja virtual con un ubuntu reciente! Eso es extraño, sys.getsizeof (dict) es 136 para mí (python 2.6 ejecutándose en una máquina virtual kubuntu, alojada en OS X, así que no estoy seguro de nada)
LeMiz
@LeMiz: Para mí (Python 2.6, Windows XP SP3), sys.getsizeof (dict) -> 436; sys.getsizeof (dict ()) -> 140
John Machin
LeMiz-Kubuntu: python2.6 Python 2.6.2 (release26-maint, 19 de abril de 2009, 01:56:41) [GCC 4.3.3] en linux2 Escriba "ayuda", "derechos de autor", "créditos" o "licencia" para más información. >>> import sys >>> sys.getsizeof (dict) 436 >>> sys.getsizeof (dict ()) 136
LeMiz
1
no deben ser los valores 0, 0.0, ''y u''para mantener la coherencia?
SilentGhost
37

He estado usando pympler felizmente para tales tareas. Es compatible con muchas versiones de Python; ¡el asizeofmódulo en particular se remonta a la 2.2!

Por ejemplo, usando el ejemplo de hughdbrown pero con from pympler import asizeofal principio y print asizeof.asizeof(v)al final, veo (sistema Python 2.5 en MacOSX 10.5):

$ python pymp.py 
set 120
unicode 32
tuple 32
int 16
decimal 152
float 16
list 40
object 0
dict 144
str 32

Claramente hay alguna aproximación aquí, pero lo he encontrado muy útil para el análisis y ajuste de huellas.

Alex Martelli
fuente
1
Algunas curiosidades: la mayoría de ustedes números son 4 más altos; el objeto es 0; y el decimal es aproximadamente 4 veces mayor según su estimación.
hughdbrown
1
Sí. El "4 más alto" en realidad parece "redondear a un múltiplo de 8", lo que creo que es correcto para la forma en que se comporta malloc aquí. No tengo idea de por qué decimal se distorsiona tanto (con pympler en 2.6 también).
Alex Martelli
2
En realidad, debería usar pympler.asizeof.flatsize () para obtener una funcionalidad similar a sys.getsizeof (). También hay un parámetro align = que puede usar (que es 8 por defecto como señaló Alex).
Pankrat
@AlexMartelli ¡Hola Alex! .. Por qué el tamaño mínimo de un carácter en Python es de 25 bytes. >>> getsizeof('a')da 25y >>> getsizeof('ab')da 26`
Grijesh Chauhan
1
Supongo que el tamaño está en bytes, pero por qué no está escrito en ninguna parte, incluso en pythonhosted.org/Pympler
Zhomart
35

Todas estas respuestas recopilan información de tamaño superficial. Sospecho que los visitantes de esta pregunta terminarán aquí buscando responder a la pregunta: "¿Qué tan grande es este objeto complejo en la memoria?"

Aquí hay una gran respuesta: https://goshippo.com/blog/measure-real-size-any-python-object/

El remate:

import sys

def get_size(obj, seen=None):
    """Recursively finds size of objects"""
    size = sys.getsizeof(obj)
    if seen is None:
        seen = set()
    obj_id = id(obj)
    if obj_id in seen:
        return 0
    # Important mark as seen *before* entering recursion to gracefully handle
    # self-referential objects
    seen.add(obj_id)
    if isinstance(obj, dict):
        size += sum([get_size(v, seen) for v in obj.values()])
        size += sum([get_size(k, seen) for k in obj.keys()])
    elif hasattr(obj, '__dict__'):
        size += get_size(obj.__dict__, seen)
    elif hasattr(obj, '__iter__') and not isinstance(obj, (str, bytes, bytearray)):
        size += sum([get_size(i, seen) for i in obj])
    return size

Usado así:

In [1]: get_size(1)
Out[1]: 24

In [2]: get_size([1])
Out[2]: 104

In [3]: get_size([[1]])
Out[3]: 184

Si desea conocer más profundamente el modelo de memoria de Python, hay un gran artículo aquí que tiene un fragmento de código de "tamaño total" similar como parte de una explicación más larga: https://code.tutsplus.com/tutorials/understand-how- mucha-memoria-sus-objetos-python-use - cms-25609

Kobold
fuente
Entonces, ¿esto debería producir la cantidad total de memoria utilizada por, por ejemplo, un dictado que contiene múltiples matrices y / u otros dictados?
Charly Empereur-mot
1
@ CharlyEmpereur-mot sí.
Kobold
Gran respuesta. Sin embargo, no parece funcionar para objetos cython compilados. En mi caso, este método devuelve 96un puntero a un objeto cython en memoria
ferdynator
8

Prueba el generador de perfiles de memoria. perfilador de memoria

Line #    Mem usage  Increment   Line Contents
==============================================
     3                           @profile
     4      5.97 MB    0.00 MB   def my_func():
     5     13.61 MB    7.64 MB       a = [1] * (10 ** 6)
     6    166.20 MB  152.59 MB       b = [2] * (2 * 10 ** 7)
     7     13.61 MB -152.59 MB       del b
     8     13.61 MB    0.00 MB       return a
Tampa
fuente
1
La precisión parece ser de 1/100 MB o 10,24 bytes. Esto está bien para el macroanálisis, pero dudo que tal precisión conduzca a una comparación precisa de las estructuras de datos como se pregunta en la pregunta.
Zoran Pavlovic
7

También puedes usar el módulo guppy .

>>> from guppy import hpy; hp=hpy()
>>> hp.heap()
Partition of a set of 25853 objects. Total size = 3320992 bytes.
 Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
     0  11731  45   929072  28    929072  28 str
     1   5832  23   469760  14   1398832  42 tuple
     2    324   1   277728   8   1676560  50 dict (no owner)
     3     70   0   216976   7   1893536  57 dict of module
     4    199   1   210856   6   2104392  63 dict of type
     5   1627   6   208256   6   2312648  70 types.CodeType
     6   1592   6   191040   6   2503688  75 function
     7    199   1   177008   5   2680696  81 type
     8    124   0   135328   4   2816024  85 dict of class
     9   1045   4    83600   3   2899624  87 __builtin__.wrapper_descriptor
<90 more rows. Type e.g. '_.more' to view.>

Y:

>>> hp.iso(1, [1], "1", (1,), {1:1}, None)
Partition of a set of 6 objects. Total size = 560 bytes.
 Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
     0      1  17      280  50       280  50 dict (no owner)
     1      1  17      136  24       416  74 list
     2      1  17       64  11       480  86 tuple
     3      1  17       40   7       520  93 str
     4      1  17       24   4       544  97 int
     5      1  17       16   3       560 100 types.NoneType
Omid Raha
fuente
0

También se puede hacer uso del tracemallocmódulo de la biblioteca estándar de Python. Parece funcionar bien para objetos cuya clase está implementada en C (a diferencia de Pympler, por ejemplo).

zahypeti
fuente
-1

Cuando utiliza la dir([object])función incorporada, puede obtener la __sizeof__de la función incorporada.

>>> a = -1
>>> a.__sizeof__()
24
hola dios
fuente