Quelle est la manière la plus efficace d'écrire des boucles "for" dans Matlab?

12

J'ai lu que si, par exemple, j'ai une double forboucle qui court sur les index d'une matrice, alors mettre l'index courant de la colonne dans la boucle externe est plus efficace. Par exemple:

a=zeros(1000);
for j=1:1000
 for i=1:1000
  a(i,j)=1;
 end
end

Quelle est la manière la plus efficace de la coder si j'ai trois forboucles ou plus ?

Par exemple:

a=zeros(100,100,100);
for j=1:100
 for i=1:100
  for k=1:100
   a(i,j,k)=1;
  end
 end
end
TensoR
la source
4
Forles boucles sont très lentes dans MATLAB. Vous devez éviter autant que possible les boucles explicites dans MATLAB. Au lieu de cela, un problème peut généralement être exprimé en termes d'opérations matricielles / vectorielles. C'est la voie MATLABique. Il y a aussi beaucoup de fonctions intégrées pour initialiser les matrices, etc. Par exemple, il y a une fonction, ones () , qui mettra tous les éléments d'une matrice à 1 (par extension, à n'importe quelle valeur par multiplication (un scalaire multiplié par la matrice tout-en-un)). Il fonctionne également sur les tableaux 3D (qui je pense couvre l'exemple ici).
Peter Mortensen
3
@PeterMortensen Par quel facteur (approximativement) l'efficacité des boucles dans Matlab est plus petite par rapport à C et Python? Et pourquoi est-ce que? De plus, l'efficacité des boucles dans Matlab ne s'est-elle pas améliorée au cours des dernières années?
TensoR
3
@PeterMortensen «généralement un problème peut être exprimé en termes d'opérations matricielles / vectorielles» - pour certaines valeurs de «généralement», oui. OMI, il est plus exact de dire que les personnes travaillant dans Matlab et similaires ont une culture de plusieurs décennies d'ignorer tout ce qui ne peut pas être fait avec les opérations matricielles / vectorielles, à tel point que tout leur ressemble à un clou pour ce marteau . Et nous ne devrions pas simplement dire «car les boucles dans Matlab sont lentes» mais «Matlab est lent» (il se trouve qu'il n'est lié qu'à une bibliothèque rapide de primitives LA écrites en C et en Fortran).
leftaroundabout
5
La performance des boucles for est controversée: matlabtips.com/matlab-is-no-longer-slow-at-for-loops
ohreally
@leftaroundabout True. Être préoccupé par la vitesse dans une langue interprétée (ou semi-interprétée) est une indication assez claire que vous avez un problème XY où la solution réelle est "n'utilisez pas cette langue". L'exception est bien sûr si vous utilisez la génération de code dans Simulink, mais alors la question est de savoir ce que le générateur de code construit et son efficacité.
Graham

Réponses:

18

Réponse courte, vous voulez avoir l'index le plus à gauche sur la boucle la plus à l'intérieur. Dans votre exemple, les indices de boucle iraient k, j, i et les indices de tableau seraient i, j, k. Cela a à voir avec la façon dont MATLAB stocke les différentes dimensions en mémoire. Pour en savoir plus, voir # 13 de ce post reddit .

whpowell96
la source
2
Ou utilisez les fonctions intégrées () .
Peter Mortensen
5
L'exemple de @Peter OP n'est presque certainement qu'un exemple jouet d'une boucle for qui fait quelque chose et non le cas d'utilisation réel.
Matt
@Matt Vous avez raison.
TensoR
11

Une réponse un peu plus longue qui explique pourquoi il est plus efficace que l'indice le plus à gauche varie le plus rapidement. Vous devez comprendre deux éléments clés.

Premièrement, MATLAB (et Fortran, mais pas C et la plupart des autres langages de programmation) stocke les tableaux en mémoire dans "l'ordre majeur des colonnes". Par exemple, si A est une matrice 2 x 3 x 10, les entrées seront stockées en mémoire dans l'ordre

A (1,1,1)

A (2,1,1)

A (1,2,1)

A (2,2,1)

A (1,3,1)

A (2,3,1)

A (1,1,2)

A (2,1,2)

...

A (2,3,10)

Ce choix de l'ordre majeur des colonnes est arbitraire - nous pourrions simplement adopter une convention "ordre majeur des lignes", et en fait c'est ce qui se fait en C et dans d'autres langages de programmation.

La deuxième chose importante que vous devez comprendre est que les processeurs modernes n'accèdent pas à la mémoire un emplacement à la fois, mais chargent et stockent plutôt des "lignes de cache" de 64 voire 128 octets contigus (8 ou 16 nombres à virgule flottante double précision) à la fois de mémoire. Ces morceaux de données sont temporairement stockés dans une mémoire cache rapide et réécrits au besoin. (Dans la pratique, l'architecture du cache est maintenant assez compliquée avec jusqu'à 3 ou 4 niveaux de mémoire cache, mais l'idée de base peut être expliquée avec un cache à un niveau du type que les ordinateurs avaient dans ma jeunesse.)

A

Si les boucles sont imbriquées de sorte que la boucle la plus interne met à jour l'indice de ligne, alors les entrées du tableau seront accessibles dans l'ordre A (1,1), A (2,1), A (3,1), ... la première entrée A (1,1) est accessible, le système apportera une ligne de cache contenant A (1,1), A (2,1), ..., A (8,1) dans le cache depuis la mémoire principale . Les 8 itérations suivantes de la boucle la plus intérieure fonctionnent sur ces données sans aucun transfert de mémoire principale supplémentaire.

Si dans l'alternative, nous structurons les boucles de sorte que l'index de colonne varie dans la boucle la plus intérieure, alors les entrées de A seraient accessibles dans l'ordre A (1,1), A (1,2), A (1,3 ), ... Dans ce cas, le premier accès amènerait A (1,1), A (2,1), ..., A (8,1) dans le cache depuis la mémoire principale, mais 7/8 de ces entrées ne seraient pas utilisées. L'accès à A (1,2) dans la deuxième itération apporterait alors 8 entrées supplémentaires depuis la mémoire principale, et ainsi de suite. Au moment où le code s'est mis à travailler sur la ligne 2 de la matrice, l'entrée A (2,1) pourrait bien être supprimée du cache pour faire place à d'autres données nécessaires. En conséquence, le code génère 8 fois plus de trafic que nécessaire.

Certains compilateurs d'optimisation sont capables de restructurer automatiquement les boucles pour éviter ce problème.

De nombreux algorithmes d'algèbre linéaire numérique pour la multiplication matricielle et la factorisation peuvent être optimisés pour fonctionner efficacement avec le schéma de commande ligne par ligne ou colonne par ligne en fonction du langage de programmation. Faire cela dans le mauvais sens peut avoir un impact négatif significatif sur les performances.

Brian Borchers
la source