2012-09-06 4 views
1

Dans mon programme, j'ai besoin de calculer la somme:Est-il possible de rendre ce code MATLAB vectorisé plus rapide?

sum.

Je calcule cette somme 2500 fois avec de nouvelles valeurs de C et z.

Argument z peut être un vecteur. J'ai écrit simple pour boucle et le code de la version vectorisée comme suit:

K = 200; 
n_z = 40000; 
C = ones(K,1); % an example, in real life the arey some coefficients (at next call will be new) 
k = 0:K-1; 
z = linspace(0, 2*pi, n_z); % at next call will be new 

tic; 
    my_sum_for = zeros(1, K); 
    for i=1:n_z 
     my_sum_for(i) = C' * tan(k' * z(i)); 
    end 
toc; % Elapsed time is 1.820485 seconds. 

tic; 
    my_sum = C' * tan(k' * z); 
toc; % Elapsed time is 0.160924 seconds. 

la version est plus rapide vectorisé, mais pas assez. Est-il possible d'améliorer la version vectorisée?

Après la réponse de Dominique Jacquel J'ai cette version vectorisée, il est plus rapide:

K = 200; 
    n_z = 40000; 
    C = ones(K,1)'; % an example, in real life they are some coefficients (at next call will be new) 
    k = (0:K-1)'; 
    z = linspace(0, 2*pi, n_z); % at next call will be new 

    tic; 
     my_sum_for = zeros(1, K); 
     for i=1:n_z 
      my_sum_for(i) = C * tan(k * z(i)); 
     end 
    toc; % Elapsed time is 1.521587 seconds. 

    tic; 
     my_sum = C * tan(k * z); 
    toc; % Elapsed time is 0.125468 seconds. 

Est-il possible d'améliorer la version vectorisée encore plus (bsxfun, arrayfun ou quelque chose)? Le temps de 250 secondes est encore lent pour moi (c'est 75% de toutes les computations).

+0

'all (my_sum_for == my_sum) -> ans = 0' ... donc, vous n'avez pas vérifié cela? Il me semble que vous faites une transposition étrange qui n'est pas nécessaire .., –

+1

J'ai vérifié cela en calculant la norme (my_sum_for, my_sum) = 1.7861e-10. Ce n'est pas nouveau pour moi que les versions vectorisées et en boucle du même code produisent des résultats légèrement différents. – N0rbert

+0

Le contenu actuel de 'C' est-il vraiment juste? Et il y a quelques éléments répétés dans la matrice 'k * z' que vous pourriez ignorer en calculant le' tan' de, mais étant donné les valeurs de 'K' et' n_Z', il n'y a pas grand chose à y sauvegarder. –

Répondre

6

Je pense que vous êtes assez proche des limites matérielles ici. Les multiplications de matrices dans Matlab sont faites avec le BLAS libraries, qui s'est avéré difficile à battre quand il s'agit de performance. AFAIK, la fonction tangente a dedicated hardware pour calculer sa valeur. En outre, Matlab distribue automatiquement les fonctions trigonométriques de grandes matrices sur plusieurs cœurs, donc il y a peu de choses à améliorer ici.

Aussi, corrigez-moi si je me trompe, mais étant donné les frais généraux de données probables et les problèmes de mémoire, je pense que ce calcul serait effectivement plus lent sur les GPU.

Maintenant, si vous comparez ces:

tic; 
for ii = 1:10 
    my_sum = C * tan(k * z); 
end 
toc 

tic; 
for ii = 1:10 
    my_sum_notan = C * k * z; 
end 
toc 

Vous verrez que toute la douleur vient de la fonction tangente, de sorte que vous feriez mieux de se concentrer sur ce point. Comme vous pouvez le lire here, accélérer les fonctions trigonométriques n'est fondamentalement possible que si vous sacrifiez une certaine précision.

Dans tout ce que vous devez vous poser ces questions:

  1. Êtes-vous prêt à renoncer à double précision complète, ou sont, disons, 6 chiffres « assez près »? Vous ne pouvez pas reformuler le problème pour que la tangente soit calculée par la suite? ou avant? Ou de toute façon sur une quantité significativement plus petite d'éléments?Dans le problème comme indiqué, ce n'est évidemment pas possible, mais je ne connais pas le code complet - il pourrait y avoir de bonnes trig-identités qui pourraient s'appliquer à votre problème.

  2. Compte tenu de tout ce qui précède, est-ce que la quantité d'effort nécessaire pour optimiser cela dépasse réellement le temps d'exécution plus long? 250 secondes ne sonnent pas trop mal comparé à l'écriture de fonctions MEX personnalisées, difficilement portables, qui mettent en œuvre des fonctions trigonométrées mais rapides.

+0

Bon moyen de montrer le coupable ici (tan)! Vous obtenez un vote de moi :) Êtes-vous positif MATLAB sera automatiquement multithread opérations trigonométriques? Même sans une boîte à outils parallèle installée? – zeFrenchy

+0

@DominiqueJacquel C'était une nouvelle fonctionnalité dans R2007a (voir [ici] (http://www.mathworks.nl/support/solutions/en/data/1-4PG4AN/?solution=1-4PG4AN) par exemple). Vous pouvez le voir dans le gestionnaire de tâches/top si vous faites le calcul dans une boucle while infinie; vous verrez que MATLAB utilise la plupart/tous les cœurs. –

+0

@Rody Oldenhuis Nous vous remercions de votre réponse complète. Parfois, je pense au calcul de tâches en Fortran/C pur ou en utilisant GotoBLAS/OpenBLAS, elles sont plus rapides (en temps d'exécution) mais le développement d'un tel code est plus lent. – N0rbert

3

Faites comme de nombreuses opérations de matrice que dès le départ possible (transposition dans ce cas) pour gagner du temps dans la boucle

K = 200; 
n_z = 40000; 
C = ones(K,1)'; 
k = (0:K-1)'; 
z = linspace(0, 2*pi, n_z); 

tic; 
    my_sum_for = zeros(1, K); 
    for i=1:n_z 
     my_sum_for(i) = C * tan(k * z(i)); 
    end 
toc 

tic; 
    my_sum = C * tan(k * z); 
toc; 

mes temps d'exécution avant

Elapsed time is 1.266158 seconds. 
Elapsed time is 0.531173 seconds. 

et après

Elapsed time is 0.496803 seconds. 
Elapsed time is 0.185396 seconds. 
+0

Merci beaucoup. C'est vraiment plus rapide. – N0rbert

3

Voici une version qui tourne un peu plus vite sur mon ordinateur:

k = repmat((0:K-1)', 1, n_z); 
z = repmat(linspace(0, 2*pi, n_z), K, 1); 
C = ones(1, K); 
tic 
my_sum = C*tan(k.*z); 
toc 

Essentiellement, au lieu d'un produit extérieur de k et z j'opérez sur des matrices.

Première version

Elapsed time is 0.652923 seconds. 
Elapsed time is 0.240300 seconds. 

Après la réponse de Dominique Jacquel

Elapsed time is 0.376218 seconds. 
Elapsed time is 0.214047 seconds. 

Ma version

Elapsed time is 0.168535 seconds. 

Vous devrez peut-être ajouter le coût de repmats, mais vous pouvez peut-être faire une fois seulement, je ne connais pas le reste du code.

Je suis entièrement d'accord avec Rody Oldenhuis. La majorité du travail réside dans la fonction tangente. Je peux vous en dire plus. Le calcul de k. * z est très efficace et ne peut pas être amélioré beaucoup. Si vous calculez la bande passante de la mémoire, elle atteint environ 10 Go/s sur mon ordinateur. Le pic que je peux obtenir est d'environ 16 Go/s, donc c'est proche. Pas beaucoup de possibilités là-bas. Pareil avec C * T. C'est une simple multiplication matrice-vecteur BLAS2, qui est liée à la mémoire. Pour les tailles de système que vous montrez, le surcoût de MATLAB n'est pas trop important.

Édition: comme l'a mentionné Rody, les nouvelles versions de MATLAB font déjà parallelize tan(). Donc pas beaucoup ici non plus.

Vous pouvez seulement espérer améliorer le bronzage() - en le faisant fonctionner en parallèle. Après tout, c'est une tâche trivialement parallélisable ... Envisagez d'exporter juste cela vers un fichier MEX, qui utilisera OpenMP. Travail très simple, beaucoup d'accélération si vous avez quelques cœurs de rechange.

+0

Merci. Quelles sont les nouvelles versions intéressantes de MATLAB (testé sur 2008b) ne supporte pas le drapeau 'mcc -x':' Erreur: -x n'est plus supporté. Le compilateur MATLAB ne génère plus de fichiers MEX car il n'y a plus d'avantage de performance à le faire: le MATLAB JIT accélère les fichiers M par défaut. Pour masquer les algorithmes propriétaires, utilisez la fonction PCODE. ' Peut-être que je vais essayer d'appeler externe self-made __tan __- fonction. – N0rbert

+1

Je voulais écrire votre propre fonction MEX et la compiler en utilisant mex. De cette façon, vous pouvez ignorer toute l'activité repmat/k * z. Cela ne fait qu'alimenter la bande passante de la mémoire et affecte le temps d'exécution. k * z est une opération BLAS2. Vous n'avez pas besoin de créer explicitement la matrice. Vous pouvez calculer ses entrées à la volée. – angainor

+0

Merci, je vais essayer. – N0rbert

Questions connexes