Vous pouvez obtenir de l'idée par l'exécution d'autres versions de votre code. Explicitement prendre en compte l'écriture des calculs, au lieu d'utiliser une fonction dans votre boucle
tic
Soln3 = ones(T, N);
for t = 1:T
for n = 1:N
Soln3(t, n) = 3*x(t, n)^2 + 2*x(t, n) - 1;
end
end
toc
Le temps de calcul sur mon ordinateur:
Soln1 1.158446 seconds.
Soln2 10.392475 seconds.
Soln3 0.239023 seconds.
Oli 0.010672 seconds.
Maintenant, tout le totalement "vectorisé" solution est clairement le plus rapide, vous pouvez voir que la définition d'une fonction à appeler pour chaque x entrée est une énorme surcharge. Juste en écrivant explicitement le calcul nous a facteur 5 speedup. Je suppose que cela montre que MATLABs compilateur JIT n'a pas les fonctions inline. En fonction de la réponse par gnovice là, il est effectivement préférable d'écrire une fonction normale à la place d'un anonyme. Essayez-la.
Prochaine étape: supprimer (vectorisation) la boucle interne:
tic
Soln4 = ones(T, N);
for t = 1:T
Soln4(t, :) = 3*x(t, :).^2 + 2*x(t, :) - 1;
end
toc
Soln4 0.053926 seconds.
Un autre facteur 5 speedup: il y a quelque chose dans ces déclarations disant: vous devez éviter les boucles dans MATLAB... Ou est-il vraiment? Jetez un oeil à ceci, alors
tic
Soln5 = ones(T, N);
for n = 1:N
Soln5(:, n) = 3*x(:, n).^2 + 2*x(:, n) - 1;
end
toc
Soln5 0.013875 seconds.
Beaucoup plus proche de la "pleinement" vectorisé version. Matlab magasins de matrices colonnes. Vous devriez toujours (si possible) de la structure de vos calculs pour être vectorisé 'colonnes'.
Nous pouvons revenir à Soln3 maintenant. La boucle de commande, il est "de la ligne sage". Permet de changer
tic
Soln6 = ones(T, N);
for n = 1:N
for t = 1:T
Soln6(t, n) = 3*x(t, n)^2 + 2*x(t, n) - 1;
end
end
toc
Soln6 0.201661 seconds.
Mieux, mais toujours très mauvais. Boucle unique de bien - être. Double boucle - bad. Je suppose que MATLAB fait un peu de travail décent sur l'amélioration de la performance de boucles, mais encore la boucle de surcharge est là. Si vous avez des lourds travaux à l'intérieur, vous auriez pas remarqué. Mais depuis ce calcul est la mémoire de la bande passante limitée, vous voyez la boucle de frais généraux. Et vous aurez encore plus voir clairement les frais généraux de l'appel à Func1.
Donc, qu'est-ce qu'arrayfun? Pas de fonction inlinig il soit, donc beaucoup de frais généraux. Mais pourquoi autant de pire qu'une double boucle imbriquée? En fait, le sujet de l'utilisation de cellfun/arrayfun a été largement discuté à de nombreuses reprises (par exemple ici, ici, ici et ici). Ces fonctions sont simplement ralentir, vous ne pouvez pas les utiliser pour de telles grain fin des calculs. Vous pouvez les utiliser dans le code de la brièveté et de la fantaisie des conversions entre les cellules et de tableaux. Mais la fonction doit être plus lourd que ce que vous avez écrit:
tic
Soln7 = arrayfun(@(a)(3*x(:,a).^2 + 2*x(:,a) - 1), 1:N, 'UniformOutput', false);
toc
Soln7 0.016786 seconds.
Notez que Soln7 est une cellule maintenant.. parfois c'est utile. Code de la performance est assez bonne aujourd'hui, et si vous avez besoin de cellule en sortie, vous n'avez pas besoin de convertir votre matrice après que vous avez utilisé entièrement vectorisé solution.
Alors, pourquoi est-arrayfun plus lente qu'une simple structure de boucle? Malheureusement, il est impossible pour nous de le dire, car il n'y a pas de code source disponible. Vous pouvez seulement deviner que depuis arrayfun est un objectif général de la fonction, qui gère tous les types de différentes structures de données et arguments, il n'est pas forcément très rapide dans les cas simples, que vous pouvez exprimer directement comme des nids de boucles. D'où la surcharge venons, nous ne pouvons pas savoir. Pourrait la surcharge être évités par une meilleure mise en œuvre? Peut-être pas. Mais, malheureusement, la seule chose que nous pouvons faire est d'étudier la performance pour identifier les cas dans lesquels il fonctionne bien, et ceux où elle ne l'est pas.
Mise à jour Depuis le moment de l'exécution de ce test est court, pour obtenir des résultats fiables, j'ai ajouté maintenant une boucle autour de l'tests:
for i=1:1000
% compute
end
Certains horaires indiqués ci-dessous:
Soln5 8.192912 seconds.
Soln7 13.419675 seconds.
Oli 8.089113 seconds.
Vous voyez que le arrayfun est toujours mauvais, mais au moins pas de trois ordres de grandeur pire que le vectorisé solution. Sur l'autre main, une seule boucle avec les colonnes de calculs est aussi rapide que le entièrement vectorisé version... Qui a été fait sur un seul PROCESSEUR. Résultats pour Soln5 et Soln7 ne changent pas si je passe à 2 cœurs - En Soln5 je dois utiliser un parfor pour l'obtenir parallélisée. Oublier speedup... Soln7 ne pas s'exécuter en parallèle parce que arrayfun ne pas s'exécuter en parallèle. Le silo vectorisé version sur l'autre main:
Oli 5.508085 seconds.