web-dev-qa-db-fra.com

Dans Matlab, quand est-il optimal d'utiliser bsxfun?

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"?

130
Colin T Bowers

J'utilise bsxfun ( documentation , lien blog ) pour trois raisons

  1. bsxfun est plus rapide que repmat (voir ci-dessous)
  2. bsxfun nécessite moins de dactylographie
  3. L'utilisation de bsxfun, 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.

enter image description here

Pour moi, bsxfun est environ 3 fois plus rapide que repmat. La différence devient plus prononcée si les baies deviennent plus grandes

enter image description here

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
151
Jonas

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é.

40
Oli

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.

16
angainor

À 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 de bsxfun par des appels directs aux fonctions et opérateurs prenant en charge extension implicite. Par rapport à l'utilisation de bsxfun, 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 que bsxfun.

12
nirvana-msu

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:

comparison

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.

8
Justin Wong