Wann ist es in Matlab optimal, bsxfun zu verwenden?


135

Meine Frage: Ich habe festgestellt, dass viele gute Antworten auf Matlab-Fragen zu SO häufig die Funktion verwenden bsxfun. Warum?

Motivation: In der Matlab-Dokumentation für bsxfunfinden Sie das folgende Beispiel:

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

Natürlich könnten wir den gleichen Vorgang ausführen mit:

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

Tatsächlich zeigt ein einfacher Geschwindigkeitstest, dass die zweite Methode etwa 20% schneller ist. Warum also die erste Methode anwenden? Ich vermute, es gibt einige Umstände, unter denen die Verwendung bsxfunviel schneller ist als der "manuelle" Ansatz. Es würde mich wirklich interessieren, ein Beispiel für eine solche Situation und eine Erklärung zu sehen, warum es schneller ist.

Ein letztes Element zu dieser Frage, ebenfalls aus der Matlab-Dokumentation für bsxfun: "C = bsxfun (fun, A, B), wendet die Element-für-Element-Binäroperation, die durch das Funktionshandle fun angegeben ist, auf Arrays A und B mit Singleton an Erweiterung aktiviert. ". Was bedeutet der Ausdruck "mit aktivierter Singleton-Erweiterung"?


4
Beachten Sie, dass die Geschwindigkeit, die Sie erhalten, von dem Test abhängt, den Sie durchführen. Wenn Sie den obigen Code nach dem Neustart von Matlab ausführen und einfach tic...tocum die Zeilen setzen, hängt die Geschwindigkeit des Codes davon ab, dass Funktionen in den Speicher eingelesen werden müssen.
Jonas

@ Jonas Ja, ich habe gerade davon erfahren, indem ich über die timeitFunktion in dem Link gelesen habe, den Sie / Angainor / Dan bereitstellen.
Colin T Bowers

Antworten:


152

Ich benutze drei Gründe bsxfun( Dokumentation , Blog-Link )

  1. bsxfunist schneller als repmat(siehe unten)
  2. bsxfun erfordert weniger Eingabe
  3. Wenn ich bsxfunwie mit benutze accumarray, fühle ich mich gut mit meinem Verständnis von Matlab.

bsxfunrepliziert die Eingabearrays entlang ihrer "Singleton-Dimensionen", dh der Dimensionen, entlang derer die Größe des Arrays 1 ist, so dass sie mit der Größe der entsprechenden Dimension des anderen Arrays übereinstimmen. Dies wird als "Singleton-Expasion" bezeichnet. Abgesehen davon sind die Singleton-Dimensionen diejenigen, die gelöscht werden, wenn Sie anrufen squeeze.

Es ist möglich, dass bei sehr kleinen Problemen der repmatAnsatz schneller ist - aber bei dieser Arraygröße sind beide Vorgänge so schnell, dass es wahrscheinlich keinen Unterschied in Bezug auf die Gesamtleistung macht. Es gibt zwei wichtige Gründe, bsxfundie schneller sind: (1) Die Berechnung erfolgt in kompiliertem Code, was bedeutet, dass die tatsächliche Replikation des Arrays niemals stattfindet, und (2) bsxfunist eine der Multithread-Matlab-Funktionen.

Ich habe einen Geschwindigkeitsvergleich zwischen repmatund bsxfunmit R2012b auf meinem anständig schnellen Laptop durchgeführt.

Geben Sie hier die Bildbeschreibung ein

Für mich bsxfunist das ca. 3 mal schneller als repmat. Der Unterschied wird stärker, wenn die Arrays größer werden

Geben Sie hier die Bildbeschreibung ein

Der Laufzeitsprung von repmaterfolgt bei einer Arraygröße von 1 MB, was möglicherweise etwas mit der Größe meines Prozessor-Cache zu tun hat. bsxfunEin Sprung wird nicht so schlimm, da nur das Ausgabearray zugewiesen werden muss.

Unten finden Sie den Code, den ich für das Timing verwendet habe:

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

Vielen Dank für eine hervorragende Antwort +1. Ich habe dies als Antwort markiert, da es die umfassendste Diskussion ist und (zu diesem Zeitpunkt) auch die meisten Stimmen erhalten hat.
Colin T Bowers

40

In meinem Fall verwende ich, bsxfunweil ich nicht über Spalten- oder Zeilenprobleme nachdenken muss.

Um Ihr Beispiel zu schreiben:

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

Ich muss mehrere Probleme lösen:

1) size(A,1)odersize(A,2)

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

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

4) mean(A)odermean(A,2)

Wenn ich benutze bsxfun, muss ich nur den letzten lösen:

a) mean(A)odermean(A,2)

Du denkst vielleicht, es ist faul oder so, aber wenn ich es benutze bsxfun, habe ich weniger Fehler und programmiere schneller .

Darüber hinaus ist es kürzer, was die Schreibgeschwindigkeit und Lesbarkeit verbessert .


1
Danke für die Antwort Oli. +1, da ich denke, dass diese Antwort zusätzlich zu den Antworten von Angainor und Jonas etwas beigetragen hat. Mir hat besonders gut gefallen, wie Sie die Anzahl der konzeptionellen Probleme dargelegt haben, die in einer bestimmten Codezeile gelöst werden müssen.
Colin T Bowers

16

Sehr interessante Frage! Ich bin kürzlich bei der Beantwortung dieser Frage auf genau diese Situation gestoßen . Betrachten Sie den folgenden Code, der Indizes eines Schiebefensters der Größe 3 durch einen Vektor berechnet 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 diesem Fall bsxfunist fast doppelt so schnell! Es ist nützlich und schnell, da es die explizite Zuweisung von Speicher für Matrizen vermeidetidx0 und idx1diese im Speicher speichert und sie dann erneut liest, um sie hinzuzufügen. Da die Speicherbandbreite ein wertvolles Gut und häufig der Engpass bei heutigen Architekturen ist, möchten Sie sie mit Bedacht einsetzen und den Speicherbedarf Ihres Codes verringern, um die Leistung zu verbessern.

bsxfunDamit können Sie genau das tun: Erstellen Sie eine Matrix, die auf der Anwendung eines beliebigen Operators auf alle Elementpaare zweier Vektoren basiert, anstatt explizit auf zwei Matrizen zu arbeiten, die durch Replizieren der Vektoren erhalten werden. Das ist Singleton-Erweiterung . Sie können es sich auch als das äußere Produkt von BLAS vorstellen:

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

Sie multiplizieren zwei Vektoren, um eine Matrix zu erhalten. Nur dass das äußere Produkt nur eine Multiplikation durchführt und bsxfunbeliebige Operatoren anwenden kann. Nebenbei bemerkt ist es sehr interessant zu sehen, dass bsxfundas so schnell ist wie das BLAS-Außenprodukt. Und BLAS ist in der Regel betrachtet liefert die Leistung ..

Bearbeiten Dank Dans Kommentar ist hier ein großartiger Artikel von Loren , der genau das bespricht.


7
Dieser Artikel könnte relevant sein: blogs.mathworks.com/loren/2008/08/04/…
Dan

@ Dan Danke für eine tolle Referenz.
Angainor

Vielen Dank für eine tolle Antwort Angainor. +1 für die erste, die den Hauptvorteil von bsxfunmit einem guten Beispiel klar ausdrückt.
Colin T Bowers

13

Ab R2016b unterstützt Matlab die implizite Erweiterung für eine Vielzahl von Operatoren, sodass in den meisten Fällen nicht mehr Folgendes erforderlich ist bsxfun:

Bisher war diese Funktionalität über die bsxfunFunktion verfügbar . Es wird jetzt empfohlen, die meisten Verwendungen von bsxfundurch direkte Aufrufe der Funktionen und Operatoren zu ersetzen , die die implizite Erweiterung unterstützen . Im Vergleich zur Verwendung bsxfunbietet die implizite Erweiterung eine schnellere Geschwindigkeit , eine bessere Speichernutzung und eine verbesserte Lesbarkeit des Codes .

Es gibt eine ausführliche Diskussion von Implicit Expansion und seine Leistung auf Loren Blog. Um Steve Eddins von MathWorks zu zitieren :

In R2016b funktioniert die implizite Erweiterung genauso schnell oder schneller als bsxfunin den meisten Fällen. Die besten Leistungssteigerungen für die implizite Erweiterung liegen bei kleinen Matrix- und Arraygrößen. Bei großen Matrixgrößen ist die implizite Expansion in der Regel ungefähr so ​​schnell wie bsxfun.


8

Die Dinge stimmen nicht immer mit den drei gängigen Methoden repmatüberein : Expension by One Indexing und bsxfun. Interessanter wird es, wenn Sie die Vektorgröße noch weiter erhöhen. Siehe Handlung:

Vergleich

bsxfunwird tatsächlich irgendwann etwas langsamer als die beiden anderen, aber was mich überrascht hat, ist, dass bsxfun plötzlich wieder um etwa das Dreifache schneller wird, wenn Sie die Vektorgröße noch weiter erhöhen (> 13E6 Ausgabeelemente). Ihre Geschwindigkeiten scheinen schrittweise zu springen und die Reihenfolge ist nicht immer konsistent. Ich vermute, es könnte auch von der Prozessor- / Speichergröße abhängen, aber im Allgemeinen denke ich, dass ich mich, bsxfunwann immer möglich, daran halten würde.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.