python - importar - ¿Cómo puedo leer y escribir de manera eficiente archivos que son demasiado grandes para caber en la memoria?
archivos python 3 (2)
Estoy tratando de calcular la similitud de coseno de 100,000 vectores, y cada uno de estos vectores tiene 200,000 dimensiones.
Al leer otras preguntas, sé que memmap , PyTables y h5py son mis mejores opciones para manejar este tipo de datos, y actualmente estoy trabajando con dos memmaps; uno para leer los vectores, otro para almacenar la matriz de similitudes de coseno.
Aquí está mi código:
import numpy as np
import scipy.spatial.distance as dist
xdim = 200000
ydim = 100000
wmat = np.memmap(''inputfile'', dtype = ''d'', mode = ''r'', shape = (xdim,ydim))
dmat = np.memmap(''outputfile'', dtype = ''d'', mode = ''readwrite'', shape = (ydim,ydim))
for i in np.arange(ydim)):
for j in np.arange(i+1,ydim):
dmat[i,j] = dist.cosine(wmat[:,i],wmat[:,j])
dmat.flush()
Actualmente, htop informa que estoy usando 224G de memoria VIRT, y 91.2G de memoria RES que está subiendo constantemente. Me parece que, al final del proceso, toda la matriz de salida se almacenará en la memoria, que es algo que estoy tratando de evitar.
PREGUNTA: ¿Es este un uso correcto de los memmaps, estoy escribiendo en el archivo de salida de una manera eficiente de la memoria (con lo que quiero decir que solo las partes necesarias de los archivos de entrada y salida, es decir, dmat[i,j]
y wmat[:,i/j]
, se almacenan en la memoria)?
Si no, ¿qué hice mal y cómo puedo solucionarlo?
Gracias por cualquier consejo que pueda tener!
EDIT: Me acabo de dar cuenta de que htop está reportando el uso total de la memoria del sistema en 12G, así que parece que está funcionando después de todo ... ¿alguien por ahí que pueda iluminarme? RES está ahora en 111G ...
EDIT2: el memmap se crea a partir de una matriz 1D que consta de lotes y lotes de decimales largos muy cercanos a 0, que se conforma a las dimensiones deseadas. El memmap entonces se ve así.
memmap([[ 9.83721223e-03, 4.42584107e-02, 9.85033578e-03, ...,
-2.30691545e-07, -1.65070799e-07, 5.99395837e-08],
[ 2.96711345e-04, -3.84307391e-04, 4.92968462e-07, ...,
-3.41317722e-08, 1.27959347e-09, 4.46846438e-08],
[ 1.64766260e-03, -1.47337747e-05, 7.43660202e-07, ...,
7.50395136e-08, -2.51943163e-09, 1.25393555e-07],
...,
[ -1.88709000e-04, -4.29454722e-06, 2.39720287e-08, ...,
-1.53058717e-08, 4.48678211e-03, 2.48127260e-07],
[ -3.34207882e-04, -4.60275148e-05, 3.36992876e-07, ...,
-2.30274532e-07, 2.51437794e-09, 1.25837564e-01],
[ 9.24923862e-04, -1.59552854e-03, 2.68354822e-07, ...,
-1.08862665e-05, 1.71283316e-07, 5.66851420e-01]])
En términos de uso de memoria, no hay nada particularmente malo con lo que estás haciendo en este momento. Las matrices de mapas memmados se manejan a nivel del sistema operativo: los datos que se escriben generalmente se guardan en un búfer temporal y solo se ingresan en el disco cuando el sistema operativo lo considera necesario. Su sistema operativo nunca debe permitirle quedarse sin memoria física antes de vaciar el búfer de escritura.
Aconsejaría no llamar al flush
en cada iteración ya que esto anula el propósito de permitir que su sistema operativo decida cuándo escribir en el disco para maximizar la eficiencia. En este momento solo estás escribiendo valores flotantes individuales a la vez.
En términos de eficiencia de E / S y CPU, operar en una sola línea a la vez es casi subóptimo. Las lecturas y escrituras son generalmente más rápidas para grandes bloques de datos contiguos, y de la misma manera, su cálculo probablemente será mucho más rápido si puede procesar muchas líneas a la vez utilizando la vectorización. La regla general es procesar una parte tan grande de su matriz como encajará en la memoria (incluidas las matrices intermedias que se crean durante su cálculo).
Este es un ejemplo que muestra cuánto puede acelerar las operaciones en matrices de mapas de memo procesándolas en trozos del tamaño adecuado.
Otra cosa que puede hacer una gran diferencia es el diseño de memoria de sus matrices de entrada y salida. De forma predeterminada, np.memmap
le proporciona una np.memmap
C-contigua (fila-mayor). Por lo tanto, el acceso a wmat
por columna será muy ineficiente, ya que se está dirigiendo a ubicaciones no adyacentes en el disco. Estaría mucho mejor si wmat
fuera F-contiguo (columna-mayor) en el disco, o si accediera por fila.
El mismo consejo general se aplica al uso de HDF5 en lugar de memmaps, aunque tenga en cuenta que con HDF5 tendrá que manejar usted mismo toda la administración de la memoria.
Los mapas de memoria son exactamente lo que su nombre indica: asignaciones de sectores de disco (virtuales) a páginas de memoria. La memoria es gestionada por el sistema operativo bajo demanda. Si hay suficiente memoria, el sistema mantiene partes de los archivos en la memoria, tal vez llenando toda la memoria, si no queda suficiente, el sistema puede descartar las páginas leídas del archivo o intercambiarlas por espacio de intercambio. Normalmente puede confiar en que el sistema operativo es lo más eficiente posible.