Como 'copiar' a matriz sem criar uma matriz temporária na memória que causou estouro de memória?

9

Ao atribuir uma matriz a uma memória alocada muito maior, o matlab de alguma forma a duplicará enquanto a 'copia' e, se a matriz a ser copiada for grande o suficiente, haverá excesso de memória. Este é o código de exemplo:

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

Alguma maneira de simplesmente 'esmagar' o slice_matrixpara o main_matsem a sobrecarga? Desde já, obrigado.

EDITAR:

O estouro ocorreu quando main_maté alocado previamente. Se main_matfor inicializado com main_mat=zeros(500,500,1);(tamanho menor), o estouro não ocorrerá, mas diminuirá a velocidade, pois a alocação não é feita antes que a matriz seja atribuída a ela. Isso reduzirá significativamente o desempenho conforme o intervalo de kaumentos.

Gregor Isack
fonte
11
Quanto aos seus loops: é recomendável definir o loop externo para um parforloop para fins de otimização . Além disso, parforcopia seus dados para cada trabalhador separado, supondo que 4 trabalhadores dupliquem seus dados quatro vezes na RAM.
Adriaan
11
Qual é a sua indicação de que o Matlab está realmente duplicando a memória? Você está usando a memoryfunção? O gerente de tarefas? Um erro de memória do Matlab? Em que linha de código está acontecendo?
Eliahu Aaron
Como você pode ver onde comentei o código, main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)é onde ocorre o problema de estouro de memória. É verificado quando eu aloquei o main_matantemão, ele irá estourar, se não o fizer, não. O Matlab retornará 'erro de falta de memória'.
Gregor Isack
Sua matriz de 500x500x2000 cabe na memória? É ~ 4 GB. Consulte stackoverflow.com/q/51987892/7328782 para saber por que o erro de falta de memória pode ocorrer apenas ao gravar na matriz.
Cris Luengo
Para entender melhor o seu problema, você poderia inserir um h=h+slice_matrix(end)antes de main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)=slice_matrix;(e inicializar h com 0)? Suspeito que esta nova linha adicionada já cause problemas de memória.
22419 Daniel

Respostas:

4

A questão principal é que os números ocupam mais espaço que zeros. consome main_mat=zeros(500,500,2000);pouca RAM e main_mat = rand(500,500,2000);consome muito, não importa se você usa GPU ou parfor (na verdade, o parfor fará com que você use mais RAM). Portanto, este não é um inchaço artificial da memória. Seguindo o link de Daniel abaixo, parece que a atribuição de zeros cria apenas ponteiros para a memória, e a memória física é preenchida apenas quando você usa a matriz para "números". Isso é gerenciado pelo sistema operacional. E isso é esperado para Windows, Mac e Linux, seja com Matlab ou com outros idiomas como C.

Yuval Harpaz
fonte
No momento, não entendo mais o MATLAB. Depois que eu digito os comandos, zerostoda a memória virtual é realmente alocada, mas nenhuma memória é usada. whosmostra o mesmo tamanho para ambas as matrizes, enquanto meu sistema operacional mostra um consumo de memória diferente. Excluí meu comentário porque sua resposta definitivamente não está errada.
Daniel
3
Eu encontrei algo explicando isso: stackoverflow.com/questions/51987892/…
Daniel
Ótima resposta! Obrigado.
JLev 2/01
@ Gregor: Acho que para confirmar isso, tente com em onesvez de zeros, isso garante que a memória seja realmente alocada no momento da chamada da respectiva função.
Daniel
Quando entendo tudo certo, a conclusão é: não há cópia temporária. As exceções de falta de memória surgem porque main_matsão atribuídos valores diferentes de zero. Anteriormente, apenas a memória virtual (espaço de endereço) era atribuída, agora é atribuída à memória física.
Daniel
1

A remoção parforprovavelmente corrigirá seu problema.

parfornão é útil lá. O MATLAB parfornão usa paralelismo de memória compartilhada (ou seja, não inicia novos threads), mas sim paralelismo de memória distribuída (inicia novos processos). Ele foi projetado para distribuir trabalho por um conjunto ou nós de trabalho. E, embora também funcione em um nó (ou em um único computador desktop) para distribuir o trabalho em vários núcleos, não é uma maneira ideal de fazer paralelismo em um nó.

Isso significa que cada um dos processos iniciados por parforprecisa ter sua própria cópia slice_matrix, o que é a causa da grande quantidade de memória usada pelo seu programa.

Consulte "Decidir quando usar parfor" na documentação do MATLAB para saber mais sobre parfore quando usá-lo.

Cris Luengo
fonte
11
A remoção parfor é a única maneira ? O processamento funciona melhor quando eu o projetei dessa maneira, uma vez que tudo dentro parforé intensivo em CPU e GPU, portanto, melhorou significativamente o desempenho.
Gregor Isack
@ GregorIsack: Eu fui com seu código de exemplo, não sabia que você realmente fazia muito trabalho dentro do parfor. Se sim, então sim, é provavelmente útil. - Talvez, se slice_matrixnão for um gpuarray, não seja copiado na tarefa.
Cris Luengo
Hmmm, mesmo que slice_matrixnão seja um gpuArray, eu ainda estou com um sintoma de transbordamento. Vou deixar essa pergunta em aberto, vamos ver se há alguma solução alternativa. Obrigado pela resposta!
Gregor Isack
0

Presumo que seu código seja apenas um exemplo de código e que rand()represente um costume no seu MVE. Portanto, existem algumas dicas e truques para o uso da memória no matlab.

Há um trecho dos manuais de treinamento do MathWorks:

Ao atribuir uma variável a outra no MATLAB, como ocorre ao passar parâmetros para uma função, o MATLAB cria de forma transparente uma referência a essa variável. O MATLAB quebra a referência e cria uma cópia dessa variável, apenas quando o código modifica um ou mais dos valores. Esse comportamento, conhecido como cópia na gravação ou cópia lenta , adia o custo de copiar grandes conjuntos de dados até que o código modifique um valor. Portanto, se o código não executar modificações, não será necessário espaço extra na memória e tempo de execução para copiar variáveis.

A primeira coisa a fazer seria verificar a eficiência (memória) do seu código. Até o código de excelentes programadores pode ser otimizado ainda mais com (um pouco) poder do cérebro. Aqui estão algumas dicas sobre eficiência de memória

  • Faça uso da vetorização nativ de Matlab, por exemplo sum(X,2), mean(X,2),std(X,[],2)
  • verifique se o matlab não precisa expandir matrizes (a expansão implícita foi alterada recentemente). Pode ser mais eficiente usar obsxfun
  • use operações no local, por exemplo, em x = 2*x+3vez dex = 2*x+3
  • ...

Esteja ciente de que o melhor em relação ao uso da memória não é o mesmo como se você desejasse reduzir o tempo de computação. Portanto, convém reduzir o número de trabalhadores ou deixar de usar o parforloop. (Como parfornão é possível usar a memória compartilhada, não há recurso de copiar na gravação usando o Parallel Toolbox.

Se você deseja examinar mais de perto sua memória , o que está disponível e pode ser usado pelo Matlab, confira feature('memstats'). O que é interessante para você é a memória virtual que é

Memória total e disponível associada a todo o processo MATLAB. É limitado pela arquitetura do processador e sistema operacional. ou use este comando [user,sys] = memory.

Nó lateral rápido : o Matlab armazena matrizes de forma consistente na memória. Você precisa ter um grande bloco de RAM livre para matrizes grandes. Essa também é a razão pela qual você deseja alocar variáveis, porque alterá-las dinamicamente força o Matlab a copiar toda a matriz para um ponto maior na RAM toda vez que ele ultrapassa o ponto atual.

Se você realmente tem problemas de memória , pode apenas querer explorar a arte dos tipos de dados - como é necessário nos idiomas de nível inferior. Por exemplo, você pode reduzir o uso de memória pela metade usando precisão única diretamente desde o início main_mat=zeros(500,500,2000,'single');- aliás, isso também funciona com rand(...,'single')e mais funções nativas - embora algumas das funções mais sofisticadas do matlab exijam entrada do tipo double, que você pode upcast novamente.

max
fonte
0

Se bem entendi, o seu problema principal é que parfornão permite compartilhar memória. Pense em cada parfor worker como quase uma instância separada do matlab.

Basicamente, existe apenas uma solução alternativa para isso que eu conheço (que nunca tentei), que é 'matriz compartilhada' no Fileexchange: https://ch.mathworks.com/matlabcentral/fileexchange/28572-sharedmatrix

Mais soluções: como outros sugeriram: remover o parfor é certamente uma solução, obtenha mais memória RAM, use matrizes altas (que usam discos rígidos quando a memória RAM está cheia, leia aqui ), divida operações em partes menores, por último, mas não menos importante, considere uma alternativa diferente de Matlab.

user2305193
fonte
0

Você pode usar o seguinte código. Você realmente não precisa do 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
fonte
Você não pode fazer isso dentro de um loop parfor
Gregor Isack 06/01
Você tentou isso?
mayank1513 6/01
Há uma razão pela qual eu mudei isso para fora do loop parfoor. Não tentei exatamente o mesmo código, mas sabia que não funcionaria por causa da indexação.
Gregor Isack