Comment «copier» la matrice sans créer une matrice temporaire en mémoire qui a provoqué un débordement de mémoire?

9

En affectant une matrice à une mémoire allouée beaucoup plus grande, matlab la dupliquera d'une manière ou d'une autre tout en la «copiant», et si la matrice à copier est suffisamment grande, il y aura un dépassement de mémoire. Voici l'exemple de code:

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

Est-il possible de simplement `` casser '' le slice_matrixsur le main_matsans les frais généraux? Merci d'avance.

ÉDITER:

Le débordement s'est produit lors de l' main_matallocation préalable. Si main_matest initialisé avec main_mat=zeros(500,500,1);(taille plus petite), le débordement ne se produira pas, mais il sera ralenti car l'allocation n'est pas effectuée avant que la matrice ne lui soit affectée. Cela réduira considérablement les performances à mesure que la plage d' kaugmentations.

Gregor Isack
la source
1
Quant à vos boucles: il est recommandé de définir la boucle externe sur une parforboucle à des fins d'optimisation . De plus, parforcopie vos données sur chaque travailleur distinct, supposant ainsi que 4 travailleurs dupliquent vos données quatre fois dans la RAM.
Adriaan
1
Quelle est votre indication que Matlab duplique réellement la mémoire? Utilisez-vous la memoryfonction? Le gestionnaire de tâches? Une erreur de mémoire de Matlab? À quelle ligne de code se produit-il?
Eliahu Aaron
Comme vous pouvez voir où j'ai commenté le code, main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)c'est là que se produit le problème de débordement de mémoire. C'est vérifié quand j'ai alloué le main_matpré, ça va déborder, si je ne le fais pas, ça ne va pas. Matlab renverra «erreur de mémoire insuffisante».
Gregor Isack
Votre matrice 500x500x2000 tient-elle en mémoire? C'est ~ 4 Go. Voir stackoverflow.com/q/51987892/7328782 pour savoir pourquoi l'erreur de mémoire insuffisante ne peut se produire que lors de l'écriture dans la baie.
Cris Luengo
Pour mieux comprendre votre problème, pourriez-vous insérer un h=h+slice_matrix(end)avant main_mat(:,:,1+(k-1)*n:1+(k-1)*n+n-1)=slice_matrix;(et initialiser h avec 0)? Je soupçonne que cette ligne nouvellement ajoutée entraînera déjà des problèmes de mémoire.
Daniel

Réponses:

4

Le problème principal est que les nombres prennent plus de place que les zéros. main_mat=zeros(500,500,2000);prend peu de RAM tout en en main_mat = rand(500,500,2000);prend beaucoup, peu importe si vous utilisez GPU ou parfor (en fait, parfor vous fera utiliser plus de RAM). Ce n'est donc pas un gonflement artificiel de la mémoire. En suivant le lien de Daniel ci-dessous, il semble que l'affectation de zéros ne crée que des pointeurs vers la mémoire, et la mémoire physique n'est remplie que lorsque vous utilisez la matrice pour les "nombres". Ceci est géré par le système d'exploitation. Et il est prévu pour Windows, Mac et Linux, que vous le fassiez avec Matlab ou d'autres langues telles que C.

Yuval Harpaz
la source
Pour l'instant, je ne comprends plus MATLAB. Une fois que j'ai tapé les commandes, zerostoute la mémoire virtuelle est réellement allouée, mais aucune mémoire n'est utilisée. whosmontre la même taille pour les deux matrices, tandis que mon os montre une consommation de mémoire différente. J'ai supprimé mon commentaire car votre réponse n'est certainement pas fausse.
Daniel
3
J'ai trouvé quelque chose expliquant cela: stackoverflow.com/questions/51987892/…
Daniel
Très bonne réponse! Merci.
JLev
@Gregor: Je suppose que pour confirmer cela, essayez-le avec onesau lieu de zeros, cela garantit que la mémoire est réellement allouée au moment d'appeler la fonction respective.
Daniel
Quand je comprends bien, la conclusion est: il n'y a pas de copie temporaire. Les exceptions de mémoire insuffisante se produisent car des main_matvaleurs non nulles sont attribuées. Auparavant, seule la mémoire virtuelle (espace d'adressage) était affectée, elle est désormais affectée à la mémoire physique.
Daniel
1

La suppression résoudra parforprobablement votre problème.

parforn'y est pas utile. MATLAB parforn'utilise pas le parallélisme de mémoire partagée (c'est-à-dire qu'il ne démarre pas de nouveaux threads) mais plutôt le parallélisme de mémoire distribué (il démarre de nouveaux processus). Il est conçu pour distribuer le travail sur un ensemble ou des nœuds de travail. Et bien qu'il fonctionne également au sein d'un nœud (ou d'un seul ordinateur de bureau) pour distribuer le travail sur plusieurs cœurs, ce n'est pas un moyen optimal de faire du parallélisme au sein d'un nœud.

Cela signifie que chacun des processus démarrés par parfordoit avoir sa propre copie de slice_matrix, ce qui est la cause de la grande quantité de mémoire utilisée par votre programme.

Voir "Décider quand utiliser parfor" dans la documentation MATLAB pour en savoir plus parforet quand l'utiliser.

Cris Luengo
la source
1
Le retrait parfor est-il le seul moyen ? Le traitement fonctionne mieux lorsque je l'ai conçu de cette façon, car tout à l'intérieur parforest gourmand en CPU et en GPU, il a donc considérablement amélioré les performances.
Gregor Isack
@GregorIsack: Je suis allé avec votre exemple de code, je ne savais pas que vous aviez fait beaucoup de travail à l'intérieur du parfor. Si oui, alors oui, c'est probablement utile. - Peut-être que si ce slice_matrixn'est pas le cas, gpuarrayil ne sera pas copié dans le devoir.
Cris Luengo
Hmmm même si ce slice_matrixn'est pas le cas gpuArray, j'ai toujours un symptôme de débordement. Je vais laisser cette question ouverte, voyons s'il y a une autre solution. Merci pour la réponse!
Gregor Isack
0

Je suppose que votre code n'est qu'un exemple de code et qu'il rand()représente une coutume dans votre MVE. Il y a donc quelques conseils et astuces pour l'utilisation de la mémoire dans matlab.

Il y a un extrait des manuels de formation de MathWorks:

Lors de l'affectation d'une variable à une autre dans MATLAB, comme cela se produit lors du passage de paramètres dans une fonction, MATLAB crée de manière transparente une référence à cette variable. MATLAB rompt la référence et crée une copie de cette variable, uniquement lorsque le code modifie une ou plusieurs des valeurs. Ce comportement, appelé copie sur écriture ou copie paresseuse , reporte le coût de copie de grands ensembles de données jusqu'à ce que le code modifie une valeur. Par conséquent, si le code n'effectue aucune modification, il n'est pas nécessaire d'espace mémoire supplémentaire et de temps d'exécution pour copier les variables.

La première chose à faire serait de vérifier l'efficacité (mémoire) de votre code. Même le code d'excellents programmeurs peut être encore optimisé avec (un peu) la puissance du cerveau. Voici quelques conseils concernant l'efficacité de la mémoire

  • faire usage de la vectorisation nativ de Matlab, par exemple sum(X,2), mean(X,2),std(X,[],2)
  • assurez-vous que matlab n'a pas à étendre les matrices (l' expansion implicite a été modifiée récemment). Il pourrait être plus efficace d’utiliser lebsxfun
  • utiliser des opérations sur place, par exemple x = 2*x+3plutôt quex = 2*x+3
  • ...

Sachez que l'optimisation de l'utilisation de la mémoire n'est pas la même chose que si vous souhaitez réduire le temps de calcul. Par conséquent, vous pouvez envisager de réduire le nombre de travailleurs ou de ne pas utiliser la parforboucle. (Comme il parforest impossible d'utiliser la mémoire partagée, il n'y a pas de fonction de copie sur écriture avec l'utilisation de Parallel Toolbox.

Si vous voulez regarder de plus près votre mémoire , ce qui est disponible et qui peut être utilisé par Matlab, consultez feature('memstats'). Ce qui est intéressant pour vous, c'est la mémoire virtuelle qui est

Mémoire totale et disponible associée à l'ensemble du processus MATLAB. Il est limité par l'architecture du processeur et le système d'exploitation. ou utilisez cette commande [user,sys] = memory.

Noeud latéral rapide : Matlab stocke les matrices de manière cohérente en mémoire. Vous devez avoir un grand bloc de RAM libre pour les grandes matrices. C'est également la raison pour laquelle vous souhaitez allouer des variables, car les modifier dynamiquement oblige Matlab à copier la matrice entière vers un emplacement plus grand dans la RAM à chaque fois qu'elle dépasse le spot actuel.

Si vous avez vraiment des problèmes de mémoire , vous voudrez peut-être simplement creuser dans l'art des types de données - comme cela est requis dans les langues de niveau inférieur. Par exemple, vous pouvez réduire de moitié votre utilisation de la mémoire en utilisant la simple précision directement depuis le début main_mat=zeros(500,500,2000,'single');- btw, cela fonctionne également avec rand(...,'single')et plus de fonctions natives - bien que certaines des fonctions matlab les plus sophistiquées nécessitent une entrée de type double, que vous pouvez refoulé.

max
la source
0

Si je comprends bien, votre principal problème est qu'il parforne permet pas de partager la mémoire. Considérez chaque travailleur de parfor comme une instance matlab distincte.

Il existe essentiellement une seule solution de contournement pour cela que je connais (que je n'ai jamais essayé), à savoir la `` matrice partagée '' sur Fileexchange: https://ch.mathworks.com/matlabcentral/fileexchange/28572-sharedmatrix

Plus de solutions: comme d'autres l'ont suggéré: supprimer parfor est certainement une solution, obtenir plus de RAM, utiliser des tableaux de grande taille (qui utilisent des disques durs lorsque le RAM est plein, lisez ici ), divisez les opérations en petits morceaux, enfin et surtout, envisagez une alternative autre que Matlab.

user2305193
la source
0

Vous pouvez utiliser le code suivant. Vous n'avez en fait pas besoin de la 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
la source
Vous ne pouvez pas faire cela à l'intérieur d'une boucle parfor
Gregor Isack
Avez-vous essayé cela?
Mayank1513
Il y a une raison pour laquelle je l'ai retiré de la boucle Parfoor. Je n'ai pas essayé exactement le même code, mais je savais que cela ne fonctionnerait pas à cause de l'indexation.
Gregor Isack