In Matlab, quando è ottimale usare bsxfun?


135

La mia domanda: ho notato che molte buone risposte alle domande di Matlab su SO utilizzano spesso la funzione bsxfun. Perché?

Motivazione: Nella documentazione di Matlab per bsxfun, viene fornito il seguente esempio:

A = magic(5);
A = bsxfun(@minus, A, mean(A))

Ovviamente potremmo fare la stessa operazione usando:

A = A - (ones(size(A, 1), 1) * mean(A));

E in effetti un semplice test di velocità dimostra che il secondo metodo è circa il 20% più veloce. Quindi perché usare il primo metodo? Immagino ci siano alcune circostanze in cui l'utilizzo bsxfunsarà molto più veloce dell'approccio "manuale". Sarei davvero interessato a vedere un esempio di tale situazione e una spiegazione sul perché sia ​​più veloce.

Inoltre, un ultimo elemento di questa domanda, sempre dalla documentazione di Matlab per bsxfun: "C = bsxfun (divertimento, A, B) applica l'operazione binaria elemento per elemento specificata dalla funzione handle divertente agli array A e B, con singleton espansione abilitata. ". Cosa significa la frase "con espansione singleton abilitata"?


4
Nota che la lettura della velocità che ottieni dipende dal test che esegui. Se esegui il codice sopra dopo aver riavviato Matlab e hai semplicemente inserito tic...tocle righe, la velocità del codice dipenderà dalla necessità di leggere le funzioni in memoria.
Jonas,

@Jonas Sì, ho appena saputo questo leggendo la timeitfunzione nel link che tu / angainor / Dan fornisci.
Colin T Bowers,

Risposte:


152

Esistono tre motivi per cui utilizzo bsxfun( documentazione , collegamento al blog )

  1. bsxfunè più veloce di repmat(vedi sotto)
  2. bsxfun richiede meno digitazione
  3. Usare bsxfun, come usare accumarray, mi fa stare bene con la mia comprensione di Matlab.

bsxfunreplicherà le matrici di input lungo le loro "dimensioni singleton", cioè le dimensioni lungo le quali la dimensione della matrice è 1, in modo che corrispondano alla dimensione della dimensione corrispondente dell'altra matrice. Questo è ciò che viene chiamato "singleton expasion". Per inciso, le dimensioni singleton sono quelle che verranno eliminate se si chiama squeeze.

È possibile che per problemi molto piccoli, l' repmatapproccio sia più veloce, ma a quella dimensione dell'array, entrambe le operazioni sono così veloci che probabilmente non farà alcuna differenza in termini di prestazioni complessive. Esistono due motivi importanti per bsxfunaccelerare: (1) il calcolo avviene in codice compilato, il che significa che la replica effettiva dell'array non si verifica mai e (2) bsxfunè una delle funzioni Matlab multithread.

Ho eseguito un confronto di velocità tra repmate bsxfuncon R2012b sul mio laptop abbastanza veloce.

inserisci qui la descrizione dell'immagine

Per me, bsxfunè circa 3 volte più veloce di repmat. La differenza diventa più pronunciata se le matrici diventano più grandi

inserisci qui la descrizione dell'immagine

Il salto in fase di esecuzione si repmatverifica attorno a una dimensione di array di 1 Mb, che potrebbe avere qualcosa a che fare con la dimensione della cache del mio processore - bsxfunnon fa male di un salto, perché deve solo allocare l'array di output.

Di seguito trovi il codice che ho usato per i tempi:

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

Grazie per l'ottima risposta +1. Ho segnato questa risposta in quanto è la discussione più completa e ha anche (a questo punto) ricevuto il punteggio più alto.
Colin T Bowers,

40

Nel mio caso, lo uso bsxfunperché mi evita di pensare ai problemi di colonna o riga.

Per scrivere il tuo esempio:

A = A - (ones(size(A, 1), 1) * mean(A));

Devo risolvere diversi problemi:

1) size(A,1)oppuresize(A,2)

2) ones(sizes(A,1),1)oppureones(1,sizes(A,1))

3) ones(size(A, 1), 1) * mean(A)oppuremean(A)*ones(size(A, 1), 1)

4) mean(A)oppuremean(A,2)

Quando uso bsxfun, devo solo risolvere l'ultimo:

a) mean(A)omean(A,2)

Potresti pensare che sia pigro o qualcosa del genere, ma quando uso bsxfun, ho meno bug e programma più velocemente .

Inoltre, è più corto, il che migliora la velocità e la leggibilità della digitazione .


1
Grazie per la risposta Oli. +1 poiché penso che questa risposta abbia contribuito in qualche modo oltre alle risposte di Angainor e Jonas. Mi è particolarmente piaciuto il modo in cui hai esposto il numero di problemi concettuali che devono essere risolti in una determinata riga di codice.
Colin T Bowers,

16

Domanda molto interessante! Di recente mi sono imbattuto in una situazione del genere mentre rispondevo a questa domanda. Si consideri il seguente codice che calcola gli indici di una finestra scorrevole di dimensione 3 attraverso un vettore 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

In questo caso bsxfunè quasi due volte più veloce! È utile e veloce perché evita l'allocazione esplicita della memoria per le matrici idx0e idx1, salvandole nella memoria, e poi rileggendole solo per aggiungerle. Poiché la larghezza di banda della memoria è una risorsa preziosa e spesso il collo di bottiglia delle architetture odierne, si desidera utilizzarla con saggezza e ridurre i requisiti di memoria del codice per migliorare le prestazioni.

bsxfunti consente di fare proprio questo: creare una matrice basata sull'applicazione di un operatore arbitrario a tutte le coppie di elementi di due vettori, invece di operare esplicitamente su due matrici ottenute replicando i vettori. Questa è l' espansione singleton . Puoi anche pensarlo come il prodotto esterno di BLAS:

v1=[0:2]';
v2 = 1:numel(a)-2;
tic;
vout = v1*v2;
toc
Elapsed time is 0.309763 seconds.

Moltiplichi due vettori per ottenere una matrice. Solo che il prodotto esterno esegue solo la moltiplicazione e bsxfunpuò applicare operatori arbitrari. Come nota a margine, è molto interessante vedere che bsxfunè veloce come il prodotto esterno BLAS. E BLAS è generalmente considerato per fornire le prestazioni ..

Modifica Grazie al commento di Dan, ecco un ottimo articolo di Loren che ne discute esattamente.


7
Questo articolo potrebbe essere pertinente: blogs.mathworks.com/loren/2008/08/04/…
Dan

@Dan Grazie per un ottimo riferimento.
angainor,

Grazie per l'ottima risposta angainor. +1 per essere il primo a dichiarare chiaramente il vantaggio principale di bsxfunun buon esempio.
Colin T Bowers,

13

A partire da R2016b, Matlab supporta l' espansione implicita per un'ampia varietà di operatori, quindi nella maggior parte dei casi non è più necessario utilizzare bsxfun:

In precedenza, questa funzionalità era disponibile tramite la bsxfunfunzione. Si consiglia ora di sostituire la maggior parte degli usi bsxfuncon chiamate dirette alle funzioni e agli operatori che supportano l' espansione implicita . Rispetto all'utilizzo bsxfun, l' espansione implicita offre una maggiore velocità , un migliore utilizzo della memoria e una migliore leggibilità del codice .

C'è una discussione dettagliata di Implicit Expansion e delle sue prestazioni sul blog di Loren. Per citare Steve Eddins di MathWorks:

In R2016b, l' espansione implicita funziona più velocemente o più velocemente che bsxfunnella maggior parte dei casi. I migliori miglioramenti delle prestazioni per l' espansione implicita sono con matrici di dimensioni ridotte e dimensioni di array. Per matrici di grandi dimensioni, l'espansione implicita tende ad avere approssimativamente la stessa velocità di bsxfun.


8

Le cose non sono sempre coerenti con i 3 metodi comuni repmat:, espansione per indicizzazione e bsxfun. Diventa piuttosto più interessante quando si aumenta ulteriormente la dimensione del vettore. Vedi trama:

confronto

bsxfunin realtà diventa leggermente più lento degli altri due ad un certo punto, ma ciò che mi ha sorpreso è se aumenti ancora di più le dimensioni del vettore (> 13E6 elementi di output), bsxfun improvvisamente diventa di nuovo più veloce di circa 3x. Le loro velocità sembrano saltare in passi e l'ordine non è sempre coerente. Suppongo che potrebbe dipendere anche dalla dimensione del processore / della memoria, ma in generale penso che mi atterrerei bsxfunquando possibile.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.