Arrière-plan
Ma question est motivée par de simples observations, qui quelque peu ébranler les croyances et les hypothèses souvent lieu/fait par des utilisateurs de MATLAB:
- MATLAB est très bien optimisé quand il s'agit de la intégré dans les fonctions et les fondamentaux de la langue fonctionnalités, telles que l'indexation des vecteurs et des matrices.
- Des boucles dans MATLAB sont lent (malgré le JIT) et doit généralement être évitée si l'algorithme peut être exprimé dans une native, "vectorisé" manière.
La ligne du bas: core MATLAB fonctionnalité est efficace et en essayant de surpasser l'aide de code MATLAB est difficile, si pas impossible.
Enquête sur la performance de vecteur d'indexation
L'exemple de code est indiqué ci-dessous sont aussi fondamentaux qu'il obtient: - je attribuer une valeur scalaire à toutes les entrées. Tout d'abord, j'alloue un vecteur vide x
:
tic; x = zeros(1e8,1); toc
Elapsed time is 0.260525 seconds.
Ayant x
je voudrais mettre toutes ses entrées à la même valeur. Dans la pratique, vous le feriez différemment, par exemple, x = value*ones(1e8,1)
, mais le point ici est d'étudier les performances de vecteur d'indexation. La façon la plus simple est d'écrire:
tic; x(:) = 1; toc
Elapsed time is 0.094316 seconds.
Appelons ça de la méthode 1 (à partir de la valeur attribuée à l' x
). Il semble être très rapide (plus rapide, à moins que l'allocation de mémoire). Parce que la seule chose que je fais ici est d'exploiter la mémoire, je peux estimer l'efficacité de ce code par le calcul de la obtenu efficace de la bande passante de la mémoire et de la comparer à du matériel de bande passante mémoire de mon ordinateur:
eff_bandwidth = numel(x) * 8 bytes per double * 2 / time
Ci-dessus, je multiplie par 2
car, à moins que l'ESS streaming est utilisé, les valeurs de réglage dans la mémoire exige que le vecteur est à la fois en lecture et en écriture à la mémoire. Dans l'exemple ci-dessus:
eff_bandwidth(1) = 1e8*8*2/0.094316 = 17 Gb/s
FLUX étalonnés bande passante de la mémoire de mon ordinateur est autour de 17,9 Gb/s, donc en effet - MATLAB offre de proximité à des performances de pointe dans ce cas! Pour l'instant, donc bon.
La méthode 1 est adapté si vous souhaitez définir tous les éléments du vecteur à une certaine valeur. Mais si vous voulez accéder à des éléments de chaque step
des entrées, vous devez remplacer le :
avec, par exemple, 1:step:end
. Ci-dessous est une conséquence directe de la vitesse de comparaison avec la méthode 1:
tic; x(1:end) = 2; toc
Elapsed time is 0.496476 seconds.
Bien que vous ne vous attendez pas à effectuer toutes différentes, la méthode 2 est clairement un gros problème: facteur 5 de ralentissement pour aucune raison. Mon soupçon est que dans ce cas MATLAB explicitement attribue l'indice du vecteur (1:end
). C'est un peu confirmé par l'utilisation explicite de la taille de vecteur au lieu de end
:
tic; x(1:1e8) = 3; toc
Elapsed time is 0.482083 seconds.
Les méthodes 2 et 3 de réaliser tout aussi mauvais.
Une autre possibilité est de créer explicitement un indice vectoriel id
et l'utiliser pour indexer x
. Cela vous donne le plus flexible des fonctions d'indexation. Dans notre cas:
tic;
id = 1:1e8; % colon(1,1e8);
x(id) = 4;
toc
Elapsed time is 1.208419 seconds.
Maintenant que c'est vraiment quelque chose d'12 fois ralentissement par rapport à la méthode 1! Je comprends qu'il doit faire pire que la méthode 1 en raison de l'augmentation de la mémoire utilisée pour id
,, mais pourquoi est-il tellement pire que les méthodes 2 et 3?
Nous allons essayer de donner les boucles d'essayer - comme sans espoir que cela puisse paraître.
tic;
for i=1:numel(x)
x(i) = 5;
end
toc
Elapsed time is 0.788944 seconds.
Une grande surprise - une boucle bat un vectorized
4 de la méthode, mais il est encore plus lent que les méthodes 1, 2 et 3. Il s'avère que dans ce cas particulier vous pouvez faire mieux:
tic;
for i=1:1e8
x(i) = 6;
end
toc
Elapsed time is 0.321246 seconds.
Et c'est sans doute le plus bizarre, les résultats de cette étude - une MATLAB écrit boucle de manière significative surpasse natif vecteur d'indexation. Qui devrait certainement pas être ainsi. Notez que le JIT ed boucle est toujours 3 fois plus lent que le théorique crête presque obtenus par la méthode 1. Donc, il y a encore beaucoup de place pour l'amélioration. C'est juste surprenant (un mot plus fort serait plus approprié) que d'habitude "vectorisé' indexation (1:end
) est encore plus lente.
Questions
- est simple indexation dans MATLAB très inefficace (les méthodes 2, 3 et 4 sont plus lente que la méthode 1), ou ai-je raté quelque chose?
- pourquoi est-méthode 4 (beaucoup) plus lent que les méthodes 2 et 3?
- pourquoi l'aide d'
1e8
au lieu denumel(x)
comme une boucle lié accélérer le code par un facteur 2?
Modifier Après la lecture de Jonas commentaire, voici une autre façon de le faire à l'aide de logique indices:
tic;
id = logical(ones(1, 1e8));
x(id) = 7;
toc
Elapsed time is 0.613363 seconds.
Beaucoup mieux que la méthode 4.
Pour plus de commodité:
function test
tic; x = zeros(1,1e8); toc
tic; x(:) = 1; toc
tic; x(1:end) = 2; toc
tic; x(1:1e8) = 3; toc
tic;
id = 1:1e8; % colon(1,1e8);
x(id) = 4;
toc
tic;
for i=1:numel(x)
x(i) = 5;
end
toc
tic;
for i=1:1e8
x(i) = 6;
end
toc
end