W Matlabie, kiedy optymalne jest używanie bsxfun?

135

Moje pytanie: Zauważyłem, że wiele dobrych odpowiedzi na pytania Matlab dotyczące SO często korzysta z tej funkcji bsxfun. Czemu?

Motywacja: w dokumentacji Matlab dla bsxfunpodano następujący przykład:

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

Oczywiście moglibyśmy wykonać tę samą operację używając:

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

W rzeczywistości prosty test szybkości pokazuje, że druga metoda jest o około 20% szybsza. Dlaczego więc używać pierwszej metody? Domyślam się, że istnieją okoliczności, w których używanie bsxfunbędzie znacznie szybsze niż podejście „ręczne”. Bardzo chciałbym zobaczyć przykład takiej sytuacji i wyjaśnienie, dlaczego jest szybszy.

Również ostatni element tego pytania, ponownie z dokumentacji Matlab dla bsxfun: "C = bsxfun (fun, A, B) stosuje binarną operację element po elemencie określoną przez funkcję handle fun do tablic A i B, z singletonem rozszerzenie włączone. ”. Co oznacza wyrażenie „z włączonym rozwijaniem singletona”?

Colin T. Bowers
źródło
4
Zwróć uwagę, że szybkość odczytu zależy od przeprowadzonego testu. Jeśli uruchomisz powyższy kod po ponownym uruchomieniu Matlaba i po prostu umieścisz tic...tocwokół linii, szybkość kodu będzie zależeć od konieczności wczytywania funkcji do pamięci.
Jonas
@Jonas Tak, właśnie dowiedziałem się o tym, czytając o timeitfunkcji w linku, który podajesz / angainor / Dan.
Colin T Bowers

Odpowiedzi:

152

Są trzy powody, dla których używam bsxfun( dokumentacja , link do bloga )

  1. bsxfunjest szybszy niż repmat(patrz poniżej)
  2. bsxfun wymaga mniej pisania
  3. Używanie bsxfun, podobnie jak używanie accumarray, sprawia, że ​​czuję się dobrze ze zrozumieniem Matlab.

bsxfunpowielą tablice wejściowe wzdłuż ich „pojedynczych wymiarów”, tj. wymiarów, wzdłuż których rozmiar tablicy wynosi 1, tak aby pasowały do ​​rozmiaru odpowiadającego wymiaru drugiej tablicy. Nazywa się to „pojedynczą ekspansją”. Na marginesie, pojedyncze wymiary to te, które zostaną odrzucone, jeśli zadzwonisz squeeze.

Możliwe, że w przypadku bardzo małych problemów repmatpodejście jest szybsze - ale przy tym rozmiarze macierzy obie operacje są tak szybkie, że prawdopodobnie nie będzie miało żadnego wpływu na ogólną wydajność. Są dwa ważne powodybsxfun jest szybsze: (1) obliczenia się w skompilowanym kodzie, co oznacza, że ​​rzeczywista replikacja tablicy nigdy się nie dzieje, oraz (2) bsxfunjest jedną z wielowątkowych funkcji Matlaba.

Przeprowadziłem porównanie prędkości między repmatiz bsxfunR2012b na moim przyzwoicie szybkim laptopie.

wprowadź opis obrazu tutaj

U mnie bsxfunto około 3 razy szybciej niż repmat. Różnica staje się bardziej wyraźna, gdy tablice stają się większe

wprowadź opis obrazu tutaj

Skok w czasie wykonywania repmat miejsce wokół rozmiaru tablicy 1 MB, co może mieć coś wspólnego z rozmiarem pamięci podręcznej procesora - bsxfunnie jest tak zły jak skok, ponieważ wystarczy przydzielić tablicę wyjściową.

Poniżej znajduje się kod, którego użyłem do pomiaru czasu:

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
Jonas
źródło
Dziękuję za doskonałą odpowiedź +1. Oznaczyłem tę odpowiedź, ponieważ jest to najbardziej wszechstronna dyskusja i również (w tym momencie) otrzymała najwięcej pozytywnych głosów.
Colin T Bowers
40

W moim przypadku używam bsxfun ponieważ pozwala mi to uniknąć myślenia o problemach z kolumnami lub wierszami.

Aby napisać swój przykład:

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

Muszę rozwiązać kilka problemów:

1) size(A,1)lubsize(A,2)

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

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

4) mean(A)lubmean(A,2)

Kiedy używam bsxfun , po prostu muszę rozwiązać ten ostatni:

a) mean(A)lubmean(A,2)

Możesz pomyśleć, że jest leniwy czy coś, ale kiedy używam bsxfun, mam mniej błędów i szybciej programuję .

Ponadto jest krótsza, co poprawia szybkość pisania i czytelność .

Oli
źródło
1
Dzięki za odpowiedź Oli. +1, ponieważ myślę, że ta odpowiedź wniosła coś poza odpowiedziami angainora i Jonasa. Szczególnie podobał mi się sposób, w jaki przedstawiłeś liczbę problemów koncepcyjnych, które muszą zostać rozwiązane w danej linii kodu.
Colin T Bowers
16

Bardzo ciekawe pytanie! Właśnie taką sytuację natknąłem się ostatnio, odpowiadając na to pytanie. Rozważmy następujący kod, który oblicza indeksy przesuwanego okna o rozmiarze 3 poprzez wektor 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

W tym przypadku bsxfunjest prawie dwa razy szybszy! Jest użyteczny i szybki, ponieważ pozwala uniknąć jawnego przydzielania pamięci dla macierzy idx0i idx1zapisywania ich w pamięci, a następnie ponownego ich czytania, aby je dodać. Ponieważ przepustowość pamięci jest cennym atutem i często wąskim gardłem w dzisiejszych architekturach, chcesz z niej mądrze korzystać i zmniejszyć wymagania dotyczące pamięci kodu, aby poprawić wydajność.

bsxfunpozwala tylko na to: stworzyć macierz opartą na zastosowaniu dowolnego operatora do wszystkich par elementów dwóch wektorów, zamiast operować jawnie na dwóch macierzach uzyskanych przez replikację wektorów. To jest pojedyncza ekspansja . Możesz również pomyśleć o nim jako o produkcie zewnętrznym firmy BLAS:

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

Mnożymy dwa wektory, aby otrzymać macierz. Tylko tyle, że iloczyn zewnętrzny wykonuje tylko mnożenie i bsxfunmoże stosować dowolne operatory. Na marginesie, bardzo interesujące bsxfunjest to, że jest tak szybki, jak produkt zewnętrzny BLAS. Zwykle uważa się, że BLAS zapewnia wydajność.

Edytuj Dzięki komentarzowi Dana, oto świetny artykuł Lorena omawiający dokładnie to.

angainor
źródło
7
Ten artykuł może być trafny: blogs.mathworks.com/loren/2008/08/04/…
Dan
@Dan Dzięki za świetne referencje.
angainor
Dzięki za świetną odpowiedź angainor. +1 za bycie pierwszym, który jasno określi główną zaletę bsxfunz dobrym przykładem.
Colin T Bowers
13

Od R2016b Matlab obsługuje niejawną ekspansję dla wielu różnych operatorów, więc w większości przypadków nie jest już konieczne używanie bsxfun:

Wcześniej ta funkcjonalność była dostępna za pośrednictwem bsxfunfunkcji. Obecnie zaleca się zastąpienie większości zastosowań programu bsxfunbezpośrednimi wywołaniami funkcji i operatorów obsługujących niejawne rozwinięcie . W porównaniu do używania bsxfun, niejawna rozbudowa zapewnia większą szybkość , lepsze wykorzystanie pamięci i lepszą czytelność kodu .

Jest to szczegółowe omówienie z niejawnego Rozbudowa i jego wydajność na blogu Loren. Aby zacytować Steve Eddins od MathWorks:

W R2016b niejawna ekspansja działa równie szybko lub szybciej niż bsxfunw większości przypadków. Największy wzrost wydajności w przypadku niejawnej ekspansji dotyczy małych macierzy i rozmiarów tablic. W przypadku dużych rozmiarów macierzy niejawna ekspansja ma mniej więcej taką samą prędkość jak bsxfun.

nirvana-msu
źródło
8

Rzeczy nie zawsze są zgodne z trzema typowymi metodami: repmatrozszerzanie o indeksowanie i bsxfun. Robi się to bardziej interesująco, gdy jeszcze bardziej zwiększysz rozmiar wektora. Zobacz działkę:

porównanie

bsxfunw rzeczywistości staje się nieco wolniejszy niż pozostałe dwa w pewnym momencie, ale co mnie zaskoczyło, to jeśli zwiększysz rozmiar wektora jeszcze bardziej (> 13E6 elementów wyjściowych), bsxfun nagle znów stanie się szybszy o około 3x. Wydaje się, że ich prędkości skaczą stopniowo, a kolejność nie zawsze jest stała. Domyślam się, że może to być również zależne od rozmiaru procesora / pamięci, ale ogólnie myślę, że trzymałbym się, bsxfungdy tylko jest to możliwe.

Justin Wong
źródło