Ma question: J'ai remarqué que beaucoup de bonnes réponses aux questions de Matlab sur SO utilisent fréquemment la fonction bsxfun
. Pourquoi?
Motivation: Dans la documentation Matlab pour bsxfun
, l'exemple suivant est fourni:
A = magic(5);
A = bsxfun(@minus, A, mean(A))
Bien sûr, nous pourrions faire la même opération en utilisant:
A = A - (ones(size(A, 1), 1) * mean(A));
Et en fait, un simple test de vitesse montre que la seconde méthode est environ 20% plus rapide. Alors pourquoi utiliser la première méthode? J'imagine qu'il y a des cas où l'utilisation de bsxfun
sera beaucoup plus rapide que l'approche "manuelle". Je serais vraiment intéressé de voir un exemple d'une telle situation et d'expliquer pourquoi c'est plus rapide.
En outre, un dernier élément de cette question, toujours issu de la documentation Matlab pour bsxfun
: "C = bsxfun (fun, A, B) applique l'opération binaire élément par élément spécifiée par la poignée de fonction fun pour les tableaux. A et B, avec extension singleton activée. ". Que signifie l'expression "avec extension de singleton activée"?
J'utilise bsxfun
( documentation , lien blog ) pour trois raisons
bsxfun
est plus rapide que repmat
(voir ci-dessous)bsxfun
nécessite moins de dactylographiebsxfun
, comme celle de accumarray
, me fait comprendre ma compréhension de Matlab.bsxfun
va répliquer les tableaux en entrée le long de leurs "dimensions singleton", c'est-à-dire les dimensions le long desquelles la taille du tableau est 1, de sorte qu'elles correspondent à la taille de la dimension correspondante de l'autre tableau. C'est ce qu'on appelle "l'expulsion singleton". De plus, les dimensions singleton sont celles qui seront supprimées si vous appelez squeeze
.
Il est possible que pour de très petits problèmes, l’approche repmat
soit plus rapide - mais avec cette taille de tableau, les deux opérations sont si rapides qu’elles ne feront probablement aucune différence en termes de performances globales. Il y a deux raisons importantes pour lesquelles bsxfun
est plus rapide: (1) le calcul s'effectue dans du code compilé, ce qui signifie que la réplication réelle du tableau ne se produit jamais et (2) bsxfun
est l'un de ceux-ci. fonctions multithread de Matlab.
J'ai effectué une comparaison de vitesse entre repmat
et bsxfun
avec R2012b sur mon ordinateur portable assez rapide.
Pour moi, bsxfun
est environ 3 fois plus rapide que repmat
. La différence devient plus prononcée si les baies deviennent plus grandes
Le saut dans le temps d’exécution de repmat
se produit autour d’une taille de tableau de 1 Mo, ce qui pourrait avoir quelque chose à voir avec la taille de mon cache de processeur - bsxfun
n’obtient pas un saut aussi important, car il suffit d'allouer le tableau de sortie.
Ci-dessous, vous trouverez le code que j'ai utilisé pour chronométrer:
n = 300;
k=1; %# k=100 for the second graph
a = ones(10,1);
rr = zeros(n,1);
bb=zeros(n,1);
ntt=100;
tt=zeros(ntt,1);
for i=1:n;
r = Rand(1,i*k);
for it=1:ntt;
tic,
x=bsxfun(@plus,a,r);
tt(it)=toc;
end;
bb(i)=median(tt);
for it=1:ntt;
tic,
y=repmat(a,1,i*k)+repmat(r,10,1);
tt(it)=toc;
end;
rr(i)=median(tt);
end
Dans mon cas, j'utilise bsxfun
car cela me permet d'éviter de penser aux problèmes de colonnes ou de lignes.
Pour écrire votre exemple:
A = A - (ones(size(A, 1), 1) * mean(A));
Je dois résoudre plusieurs problèmes:
1) size(A,1)
ou size(A,2)
2) ones(sizes(A,1),1)
ou ones(1,sizes(A,1))
3) ones(size(A, 1), 1) * mean(A)
ou mean(A)*ones(size(A, 1), 1)
4) mean(A)
ou mean(A,2)
Quand j'utilise bsxfun
, il ne me reste plus qu'à résoudre le dernier:
a) mean(A)
ou mean(A,2)
Vous pourriez penser que c'est paresseux ou quelque chose du genre, mais quand j'utilise bsxfun
, j'ai moins de bugs et I programme plus rapide.
De plus, il est plus court, ce qui améliore vitesse de frappe et lisibilité.
Question très intéressante! Je suis récemment tombé sur une telle situation tout en répondant this question. Considérons le code suivant qui calcule les index d'une fenêtre glissante de taille 3 via un vecteur a
:
a = Rand(1e7,1);
tic;
idx = bsxfun(@plus, [0:2]', 1:numel(a)-2);
toc
% equivalent code from im2col function in MATLAB
tic;
idx0 = repmat([0:2]', 1, numel(a)-2);
idx1 = repmat(1:numel(a)-2, 3, 1);
idx2 = idx0+idx1;
toc;
isequal(idx, idx2)
Elapsed time is 0.297987 seconds.
Elapsed time is 0.501047 seconds.
ans =
1
Dans ce cas, bsxfun
est presque deux fois plus rapide! Il est utile et rapide car il évite l’allocation explicite de mémoire pour les matrices idx0
et idx1
, en les enregistrant dans la mémoire, puis en les lisant à nouveau pour les ajouter. Étant donné que la bande passante mémoire est un atout précieux et qu’elle constitue souvent un goulot d’étranglement pour les architectures actuelles, vous souhaitez l’utiliser à bon escient et réduire les besoins en mémoire de votre code pour améliorer les performances.
bsxfun
vous permet justement de faire cela: créer une matrice basée sur l'application d'un opérateur arbitraire à toutes les paires d'éléments de deux vecteurs, au lieu d'opérer explicitement sur deux matrices obtenues en répliquant les vecteurs. C'est expansion singleton. Vous pouvez également le considérer comme le produit externe de BLAS:
v1=[0:2]';
v2 = 1:numel(a)-2;
tic;
vout = v1*v2;
toc
Elapsed time is 0.309763 seconds.
Vous multipliez deux vecteurs pour obtenir une matrice. Le produit externe ne fait que multiplier, et bsxfun
peut appliquer des opérateurs arbitraires. En passant, il est très intéressant de voir que bsxfun
est aussi rapide que le produit externe BLAS. Et BLAS est généralement considéré comme offrant la performance .
Edit Grâce au commentaire de Dan, voici un excellent article de Loren discutant exactement de cela.
À compter de R2016b, Matlab prend en charge Expansion implicite pour une grande variété d'opérateurs. Par conséquent, dans la plupart des cas, il n'est plus nécessaire d'utiliser bsxfun
:
Auparavant, cette fonctionnalité était disponible via la fonction
bsxfun
. Il est maintenant recommandé de remplacer la plupart des utilisations debsxfun
par des appels directs aux fonctions et opérateurs prenant en charge extension implicite. Par rapport à l'utilisation debsxfun
, développement implicite offre une vitesse plus rapide , meilleure utilisation de la mémoire , et lisibilité améliorée du code .
Il y a un discussion détaillée de Expansion implicite et sa performance sur le blog de Loren. Pour citation Steve Eddins de MathWorks:
En R2016b, expansion implicite fonctionne aussi vite ou plus vite que
bsxfun
dans la plupart des cas. Les meilleurs gains de performances pour expansion implicite concernent les petites tailles de matrice et de tableau. Pour les grandes tailles de matrice, le développement implicite tend à avoir à peu près la même vitesse quebsxfun
.
Les choses ne sont pas toujours compatibles avec les 3 méthodes courantes: repmat
, expansion par index, et bsxfun
. Cela devient plus intéressant lorsque vous augmentez encore la taille du vecteur. Voir l'intrigue:
bsxfun
devient en fait légèrement plus lent que les deux autres à un moment donné, mais ce qui me surprend c'est si vous augmentez encore la taille du vecteur (> 13E6 output elements), bsxfun redevient soudainement plus rapide d'environ 3x. Leurs vitesses semblent sauter par paliers et l'ordre n'est pas toujours cohérent. Je suppose que cela pourrait dépendre de la taille du processeur/de la mémoire aussi, mais en général, je pense que je resterais avec bsxfun
autant que possible.