Tamaño de la matriz numpy / strided / array en la memoria?

Estoy intentando crear matrices de difusión eficientes en números, por ejemplo, un conjunto de matrices shape=[1000,1000,1000] que tienen solo 1000 elementos, pero se repiten 1e6 veces. Esto se puede lograr a través de np.lib.stride_tricks.as_strided y np.broadcast_arrays .

Sin embargo, estoy teniendo problemas para verificar que no haya duplicación en la memoria, y esto es crítico ya que las pruebas que realmente duplican los arreglos en la memoria tienden a bloquear mi máquina sin dejar ningún rastro.

He intentado examinar el tamaño de las matrices utilizando .nbytes , pero eso no parece corresponder al uso de memoria real:

 >>> import numpy as np >>> import resource >>> initial_memuse = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss >>> pagesize = resource.getpagesize() >>> >>> x = np.arange(1000) >>> memuse_x = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss >>> print("Size of x = {0} MB".format(x.nbytes/1e6)) Size of x = 0.008 MB >>> print("Memory used = {0} MB".format((memuse_x-initial_memuse)*resource.getpagesize()/1e6)) Memory used = 150.994944 MB >>> >>> y = np.lib.stride_tricks.as_strided(x, [1000,10,10], strides=x.strides + (0, 0)) >>> memuse_y = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss >>> print("Size of y = {0} MB".format(y.nbytes/1e6)) Size of y = 0.8 MB >>> print("Memory used = {0} MB".format((memuse_y-memuse_x)*resource.getpagesize()/1e6)) Memory used = 201.326592 MB >>> >>> z = np.lib.stride_tricks.as_strided(x, [1000,100,100], strides=x.strides + (0, 0)) >>> memuse_z = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss >>> print("Size of z = {0} MB".format(z.nbytes/1e6)) Size of z = 80.0 MB >>> print("Memory used = {0} MB".format((memuse_z-memuse_y)*resource.getpagesize()/1e6)) Memory used = 0.0 MB 

Entonces .nbytes informa el tamaño “teórico” de la matriz, pero aparentemente no el tamaño real. La comprobación de resource es un poco incómoda, ya que parece que hay algunas cosas que se cargan y almacenan en caché (¿quizás?) Que hacen que el primer paso ocupe una cierta cantidad de memoria, pero los pasos futuros no son necesarios.

tl; dr: ¿Cómo determina el tamaño real de una matriz numpy o vista de matriz en la memoria?

Una forma sería examinar el atributo .base de la matriz, que hace referencia al objeto del cual una matriz “toma prestada” su memoria. Por ejemplo:

 x = np.arange(1000) print(x.flags.owndata) # x "owns" its data # True print(x.base is None) # its base is therefore 'None' # True a = x.reshape(100, 10) # a is a reshaped view onto x print(a.flags.owndata) # it therefore "borrows" its data # False print(a.base is x) # its .base is x # True 

Las cosas son un poco más complicadas con np.lib.stride_tricks :

 b = np.lib.stride_tricks.as_strided(x, [1000,100,100], strides=x.strides + (0, 0)) print(b.flags.owndata) # False print(b.base) #  

Aquí, b.base es una instancia numpy.lib.stride_tricks.DummyArray , que se ve así:

 class DummyArray(object): """Dummy object that just exists to hang __array_interface__ dictionaries and possibly keep alive a reference to a base array. """ def __init__(self, interface, base=None): self.__array_interface__ = interface self.base = base 

Por lo tanto podemos examinar b.base.base :

 print(b.base.base is x) # True 

Una vez que tenga la matriz base, su atributo .nbytes debe reflejar con precisión la cantidad de memoria que ocupa.

En principio, es posible tener una vista de una vista de una matriz, o crear una matriz de pasos desde otra matriz de pasos. Suponiendo que su vista o su matriz de pasos esté respaldada en última instancia por otra matriz de números, podría hacer referencia recursivamente a su atributo .base . Una vez que encuentre un objeto cuyo .base es None , ha encontrado el objeto subyacente del cual su matriz está tomando prestada su memoria:

 def find_base_nbytes(obj): if obj.base is not None: return find_base_nbytes(obj.base) return obj.nbytes 

Como se esperaba,

 print(find_base_nbytes(x)) # 8000 print(find_base_nbytes(y)) # 8000 print(find_base_nbytes(z)) # 8000