¿Cómo 'copiar' la matriz sin crear una matriz temporal en la memoria que causó el desbordamiento de la memoria?

9

Al asignar una matriz a una memoria asignada mucho más grande, matlab de alguna manera la duplicará mientras la 'copia', y si la matriz que se va a copiar es lo suficientemente grande, habrá un desbordamiento de memoria. Este es el código de muestra:

main_mat=zeros(500,500,2000);
n=500;
slice_matrix=zeros(500,500,n);
for k=1:4
    parfor i=1:n
        slice_matrix(:,:,i)=gather(gpuArray(rand(500,500)));
    end
    main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)=slice_matrix; %This is where the memory will likely overflow
end

¿Alguna forma de simplemente 'aplastar' el slice_matrixsobre main_matsin la sobrecarga? Gracias por adelantado.

EDITAR:

El desbordamiento se produjo cuando main_matse asigna de antemano. Si main_matse inicializa con main_mat=zeros(500,500,1);(tamaño más pequeño), el desbordamiento no ocurrirá, pero se ralentizará ya que la asignación no se realiza antes de que se asigne la matriz. Esto reducirá significativamente el rendimiento a medida que kaumenta el rango de aumento.

Gregor Isack
fuente
1
En cuanto a sus bucles: se recomienda establecer el bucle externo en un parforbucle para fines de optimización . Además, parforcopia sus datos a cada trabajador por separado, suponiendo que 4 trabajadores dupliquen sus datos cuatro veces en RAM.
Adriaan
1
¿Cuál es su indicación de que Matlab está duplicando la memoria? ¿Estás usando la memoryfunción? El administrador de tareas? ¿Un error de memoria de Matlab? ¿En qué línea de código está sucediendo?
Eliahu Aaron
Como puede ver donde comenté el código, main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)es donde ocurre el problema de desbordamiento de memoria. Se verifica cuando asigné de main_matantemano, se desbordará, si no lo hago, no lo hará. Matlab devolverá 'error de falta de memoria'.
Gregor Isack
¿Su matriz 500x500x2000 cabe en la memoria? Es ~ 4 Gb. Consulte stackoverflow.com/q/51987892/7328782 para ver por qué el error de falta de memoria solo puede ocurrir al escribir en la matriz.
Cris Luengo
Para comprender mejor su problema, ¿podría insertar un h=h+slice_matrix(end)antes de main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)=slice_matrix;(e inicializar h con 0)? Sospecho que esta línea recién agregada ya causará problemas de memoria.
Daniel

Respuestas:

4

El problema principal es que los números ocupan más espacio que los ceros. main_mat=zeros(500,500,2000);toma poca RAM mientras main_mat = rand(500,500,2000);toma mucho, no importa si usa GPU o parfor (de hecho, parfor le hará usar más RAM). Entonces esto no es una hinchazón antinatural de la memoria. Siguiendo el enlace de Daniel a continuación, parece que la asignación de ceros solo crea punteros a la memoria, y la memoria física se llena solo cuando usa la matriz para "números". Esto es administrado por el sistema operativo. Y se espera para Windows, Mac y Linux, ya sea que lo haga con Matlab u otros lenguajes como C.

Yuval Harpaz
fuente
En este momento ya no entiendo MATLAB. Una vez que escribo los comandos con zerostoda la memoria virtual está realmente asignada, pero no se usa memoria. whosmuestra el mismo tamaño para ambas matrices, mientras que mi sistema operativo muestra un consumo de memoria diferente. Eliminé mi comentario porque su respuesta definitivamente no es incorrecta.
Daniel
3
Encontré algo que explica esto: stackoverflow.com/questions/51987892/…
Daniel
¡Gran respuesta! Gracias.
JLev
@ Gregor: supongo que para confirmar esto, pruébalo en oneslugar de zeros, esto asegura que la memoria esté realmente asignada al momento de llamar a la función respectiva.
Daniel
Cuando entiendo todo bien, la conclusión es: no hay una copia temporal. Las excepciones de falta de memoria surgen porque main_matse le asignan valores distintos de cero. Anteriormente solo se asignaba memoria virtual (espacio de direcciones), ahora se asigna a la memoria física.
Daniel
1

La eliminación parforprobablemente solucionará su problema.

parforNo es útil allí. MATLAB parforno usa el paralelismo de memoria compartida (es decir, no inicia nuevos subprocesos) sino el paralelismo de memoria distribuida (inicia nuevos procesos). Está diseñado para distribuir el trabajo en un conjunto o nodos de trabajo. Y aunque también funciona dentro de un nodo (o una sola computadora de escritorio) para distribuir el trabajo en múltiples núcleos, no es una forma óptima de hacer paralelismo dentro de un nodo.

Esto significa que cada uno de los procesos iniciados parfordebe tener su propia copia slice_matrix, que es la causa de la gran cantidad de memoria utilizada por su programa.

Consulte "Decidir cuándo usar parfor" en la documentación de MATLAB para obtener más información acerca de parforcuándo usarlo.

Cris Luengo
fuente
1
¿Eliminar parfor es la única forma ? El procesamiento funciona mejor cuando lo diseñé de esa manera, ya que todo en el interior parfores intensivo en CPU y GPU, por lo que mejoró significativamente el rendimiento.
Gregor Isack
@GregorIsack: Fui con tu código de ejemplo, no sabía que realmente hacías mucho trabajo dentro del parfor. Si es así, entonces sí, es probable que sea útil. - Tal vez si slice_matrixno es un gpuarrayno se copiará en la tarea.
Cris Luengo
Hmmm, incluso si slice_matrixno es un gpuArray, sigo teniendo síntomas de desbordamiento. Dejaré esta pregunta abierta, veamos si hay alguna solución alternativa. Gracias por la respuesta sin embargo!
Gregor Isack
0

Supongo que su código es solo un código de muestra y que rand()representa una costumbre en su MVE. Así que hay algunos consejos y trucos para el uso de memoria en matlab.

Hay un fragmento de los manuales de capacitación de The MathWorks:

Al asignar una variable a otra en MATLAB, como ocurre al pasar parámetros a una función, MATLAB crea de manera transparente una referencia a esa variable. MATLAB rompe la referencia y crea una copia de esa variable, solo cuando el código modifica uno o más de los valores. Este comportamiento, conocido como copia en escritura o copia diferida , difiere el costo de copiar grandes conjuntos de datos hasta que el código modifique los valores. Por lo tanto, si el código no realiza modificaciones, no hay necesidad de espacio de memoria adicional y tiempo de ejecución para copiar variables.

Lo primero que debe hacer es verificar la eficiencia (memoria) de su código. Incluso el código de excelentes programadores puede optimizarse aún más con (un poco) poder mental. Aquí hay algunos consejos sobre la eficiencia de la memoria.

  • hacer uso de la vectorización Nativ de Matlab, por ejemplo sum(X,2), mean(X,2),std(X,[],2)
  • asegúrese de que matlab no tenga que expandir las matrices (la expansión implícita se modificó recientemente). Podría ser más eficiente usar elbsxfun
  • utilizar operaciones en el lugar, por ejemplo, en x = 2*x+3lugar dex = 2*x+3
  • ...

Tenga en cuenta que el uso óptimo de la memoria no es lo mismo que si quisiera reducir el tiempo de cálculo. Por lo tanto, es posible que desee considerar reducir la cantidad de trabajadores o abstenerse de usar el parforbucle. (Como parforno puede usar la memoria compartida, no hay una función de copia en escritura al usar Parallel Toolbox.

Si desea ver más de cerca su memoria , qué está disponible y qué puede usar Matlab, consulte feature('memstats'). Lo que es interesante para usted es la memoria virtual que es

Memoria total y disponible asociada con todo el proceso de MATLAB. Está limitado por la arquitectura del procesador y el sistema operativo. o usa este comando [user,sys] = memory.

Nodo lateral rápido : Matlab almacena matrices consistentemente en la memoria. Necesita tener un gran bloque de RAM libre para matrices grandes. Esa es también la razón por la que desea asignar variables, porque cambiarlas dinámicamente obliga a Matlab a copiar toda la matriz en un punto más grande en la RAM cada vez que supera el punto actual.

Si realmente tiene problemas de memoria , es posible que solo desee profundizar en el arte de los tipos de datos, como se requiere en los idiomas de nivel inferior. Por ejemplo, puede reducir el uso de su memoria a la mitad utilizando precisión simple directamente desde el principio main_mat=zeros(500,500,2000,'single');, por cierto, esto también funciona con rand(...,'single')más funciones nativas, aunque algunas de las funciones matlab más sofisticadas requieren la entrada de tipo double, que puede molesto de nuevo.

max
fuente
0

Si entiendo correctamente, su problema principal es que parforno permite compartir memoria. Piense en cada trabajador parfor como una instancia de matlab casi separada.

Básicamente, solo sé una solución alternativa para esto (que nunca he probado), que es 'matriz compartida' en Fileexchange: https://ch.mathworks.com/matlabcentral/fileexchange/28572-sharedmatrix

Más soluciones: como sugirieron otros: eliminar parfor es sin duda una solución, obtener más ram, usar matrices altas (que usan discos duros cuando el ram se llena, lea aquí ), dividir las operaciones en trozos más pequeños, por último, pero no menos importante, considere una alternativa que no sea Matlab

usuario2305193
fuente
0

Puede usar el siguiente código. En realidad no necesitas el slice_matrix

main_mat=zeros(500,500,2000);
n=500;
slice_matrix=zeros(500,500,n);
for k=1:4
   parfor i=1:n
       main_mat(:,:,1+(k-1)*n + i - 1) = gather(gpuArray(rand(500,500)));
   end
   %% now you don't need this main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)=slice_matrix; %This is where the memory will likely overflow
end
mayank1513
fuente
No puedes hacer eso dentro de un bucle parfor
Gregor Isack
¿Intentaste eso?
mayank1513
Hay una razón por la que saqué eso del bucle parfoor. No probé exactamente el mismo código, pero sabía que no funcionaría debido a la indexación.
Gregor Isack