3 votes

Optimisation de la multiplication par éléments de la matrice MATLAB

Je dois optimiser un morceau de code MATLAB. Le code est simple, mais il fait partie d'une unité de calcul, qui l'appelle ~8000 fois (sans redondance) (cette unité de calcul est utilisée ~10-20K fois dans des cas réels). L'ensemble du code MATLAB est assez long et complexe (pour un physicien comme moi), mais le profileur MATLAB affirme que le segment suivant est responsable de près de la moitié du temps d'exécution ( !).

Le code consiste essentiellement à multiplier par élément chaque permutation de 3 matrices de 3 groupes (A, B, C) et à les additionner avec une certaine pondération. Le groupe A a une seule matrice, le groupe B a 4 matrices et le groupe C en a 7.

J'ai essayé quelques techniques de vectorisation* mais j'ai obtenu au mieux le même temps d'exécution.

À l'aide du profileur MATLAB, j'ai vérifié le temps total passé à chaque ligne (pour les 8 000 appels) - je l'ai écrit dans les commentaires.

for idx_b = 1:4

B_MAT=B_Container_Cell{idx_b};

for idx_c = 1:7

    C_MAT = C_Container_Cell{idx_b}(:,:,idx_c); % 60 sec

    ACB=A_MAT.*C_MAT.*B_MAT; % 20 sec

    Coeff_x = Coeff_x_Cell{idx_b}(p1,p2,idx_c,p3);
    Coeff_y = Coeff_y_Cell{idx_b}(p1,p2,idx_c,p3);
    Coeff_z = Coeff_z_Cell{idx_b}(p1,p2,idx_c,p3);

    Sum_x = Sum_x+Coeff_x.*ACB; % 15 sec
    Sum_y = Sum_y+Coeff_y.*ACB; % 15 sec
    Sum_z = Sum_z+Coeff_z.*ACB; % 15 sec

end

fin

Quelques connaissances préalables -
A_MAT est une matrice complexe double constante de 1024x1024 définie à l'extérieur de la boucle.
B_MAT est une matrice double de 1024x1024, essentiellement clairsemée (seulement des valeurs 0 et 1, les valeurs 1 représentent ~5% du total des éléments).
C_MAT est un double complexe de 1024x1024

Sum_x/ Sum_y / Sum_z ont été correctement initiés
Coeff_X / Coeff_y / Coeff_z sont des doubles scalaires
p1,p2,p3 sont des paramètres (constants pour ce segment de code)

Quelqu'un sait-il pourquoi l'opération la plus coûteuse est l'affectation des variables ? (J'ai essayé de sauter l'affectation et de remplacer C_MAT directement par son expression, mais cela aggrave les performances).


Tentative de vectorisation
La technique que j'ai essayé est d'utiliser cat, reshape et repmat pour créer 3 matrices 2D géantes, de les multiplier par élément, puis de les superposer (avec reshape) et de les additionner via la dimension correspondante. La première matrice était A répétée 4*7=28 fois, la deuxième était les 4 matrices B répétées 7 fois et la troisième était toutes les matrices C réparties (=28 matrices).


Exemple d'entrée
Le code sur les points suivants enlace génère des exemples de fichiers d'entrée. Le temps d'exécution avec ces variables (sur mon ordinateur) est de ~0.38 sec (le code original+variables ~0.42, la différence à mon avis est due au fait que le conteneur C Cell réel est très grand, donc l'extraction prend plus de temps).

2voto

Divakar Points 20144

Étant donné que les tableaux des tableaux de cellules d'entrée sont de taille identique, il serait préférable que les entrées soient stockées sous forme de tableaux multidimensionnels plutôt que de tableaux de cellules afin de tirer parti des techniques vectorielles de MATLAB qui, dans ce cas, seraient les suivantes indexing pour extraire des éléments spécifiques et matrix-multiplication pour la réduction de la somme. Ainsi, lors de la formation des entrées, nous pourrions chercher à former des tableaux multidimensionnels correspondant aux entrées : B_Container_Cell , C_Container_Cell , Coeff_x_Cell , Coeff_y_Cell y Coeff_z_Cell . Il s'agit de 1D réseaux de cellules avec B_Container_Cell contenant 2D et les autres ont 3D tableaux. Ainsi, lorsque l'on utilise des tableaux multidimensuels, ils constituent une dimension supplémentaire, c'est-à-dire qu'ils sont 3D y 4D respectivement.

Pour simuler leurs formats de tableaux multidimensionnels, convertissons les tableaux de cellules donnés avec la concaténation en utilisant la fonction cat le long de leur last+1 dimension, comme suit -

Bm = cat(3,B_Container_Cell{:});
Cm = cat(4,C_Container_Cell{:});

Cx = cat(4,Coeff_x_Cell{:});
Cy = cat(4,Coeff_y_Cell{:});
Cz = cat(4,Coeff_z_Cell{:});

Enfin, la solution vectorisée permettant d'utiliser ces tableaux multidimensionnels et d'obtenir les résultats souhaités -

%// Get ACB across all iterations and reshaped into (Nx28) shaped array
Ar = reshape(bsxfun(@times,bsxfun(@times,Cm,permute(Bm,[1,2,4,3])),A_MAT),[],28);

%// Use matrix-multiplication to sum reduce sliced versions of Cx, Cy and
%// Cz, to get respectived summed outputs
sz = size(A_MAT); %// Output array size
Sum_x_out = reshape(Ar*reshape(Cx(p1,p2,:,:),[],1),sz);
Sum_y_out = reshape(Ar*reshape(Cy(p1,p2,:,:),[],1),sz);
Sum_z_out = reshape(Ar*reshape(Cz(p1,p2,:,:),[],1),sz);

Notez qu'il ne ressemble pas au paramètre p3 a été utilisé.

Résultats des tests d'exécution (pour les échantillons d'entrée énumérés) -

--------------------------------- With Original Approach
Elapsed time is 2.412417 seconds.
--------------------------------- With Proposed Approach
Elapsed time is 1.572035 seconds.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X