WICHTIG: Der Betrieb von goMatlab.de wird privat finanziert fortgesetzt. - Mehr Infos...

Mein MATLAB Forum - goMatlab.de

Mein MATLAB Forum

 
Gast > Registrieren       Autologin?   

Partner:




Forum
      Option
[Erweitert]
  • Diese Seite per Mail weiterempfehlen
     


Gehe zu:  
Neues Thema eröffnen Neue Antwort erstellen

For loop vektorisieren...

 

Willa
Forum-Fortgeschrittener

Forum-Fortgeschrittener



Beiträge: 80
Anmeldedatum: 23.05.08
Wohnort: Bremen
Version: Willa v1.0
     Beitrag Verfasst am: 11.11.2010, 12:13     Titel: For loop vektorisieren...
  Antworten mit Zitat      
Hi!
Ich möchte zwei synthetische Partikelbilder (A & B) mit Matlab erstellen. Nur leider dauert das erstellen bei ganz kleinen Testbildern (200x200 px) mit nur 1000 Partikeln bereits 4 Sekunden. Am Ende möchte ich gerne 1000 x 1000 px große Bilder mit 15000 Partikeln erstellen. Das dürfte ja mehrere Tage dauern...
Folgenden Code habe ich geschrieben:
Code:
A=zeros(200,200);
B=A;
partAm=1000; % Amount of Particles
Z=0.2; % sheet thickness
dt=20; % particle diameter
ddt=10; % particle diameter variation
z0=abs(randn(partAm,1)); % normal distributed sheet intensity
I0=255*exp(-(Z^2./(0.125*z0))); % particle intensity
I0(I0>255)=255;
I0(I0<0)=0;
d=randn(partAm,1)/2; % particle diameter distribution
d=dt+d*ddt;
d(d<0)=0;
x0=rand(partAm,1)*size(A,2); % x position of the particles in A
y0=rand(partAm,1)*size(A,1); % y position of the particles in A
offsetx=0.2; % x offset for image B
offsety=2; % y offset for image B
tic
for n=1:partAm % for each particle
    for i=1:size(A,1)
        for j=1:size(A,2)
            A(i,j)=A(i,j)+I0(n)*exp((-(i-x0(n))^2-(j-y0(n))^2)/((1/8*d(n)))); % place particle with gaussian intensity profile
            B(i,j)=B(i,j)+I0(n)*exp((-(i-x0(n)+offsetx)^2-(j-y0(n)+offsety)^2)/((1/8*d(n))));
            if A(i,j)>255
                A(i,j)=255;
            end
            if B(i,j)>255
                B(i,j)=255;
            end
        end
    end
end
toc
imshow(uint8(A))
figure;imshow(uint8(B))
 

Probiert ihn doch einfach mal aus um zu sehen was ich mit "Partikelbilder" meine.
Wichtig ist hier nur der for loop (besonders die inneren beiden Schleifen). Für jeden der kleinen Partikel wird jedesmal das ganze Bild durchlaufen. Das dauert natürlich ewig. Fällt jemandem von euch ein wie man das schneller machen könnte? Kann das nicht irgendwie vektorisiert werden...?

Danke schonmal für eure Tipps & viele Grüße,
William
_________________

Viele Grüße,
William
Meine Micro Air Vehicles
Private Nachricht senden Benutzer-Profile anzeigen


Jan S
Moderator

Moderator


Beiträge: 11.057
Anmeldedatum: 08.07.10
Wohnort: Heidelberg
Version: 2009a, 2016b
     Beitrag Verfasst am: 11.11.2010, 14:34     Titel: Re: For loop vektorisieren...
  Antworten mit Zitat      
Hallo Willa,

Ich empfehle vor der Vektorisierung erstmal die Standard-Methoden der effizienten Programmierung anzuwenden:
1. So viele Berechnungen aus den Schleifen auslagern wie möglich.
In Deinem Beipiel wird "1/8*d(n)" in jeder Iteration neu berechnet, dabei ändert sich der Wert ja nur in der äußersten Schleife. Also definiere eine temporäre Variable dort:

2. Die Multiplikation ist schneller als die Division. Also definiere gleich den reziproken Wert der temporären Variable:
Code:
...
for n=1:partAm
  r = 8.0 / d(n);  % Oder diese Rechnung auch gleich nach aussen!
  ...
            A(i,j)=A(i,j)+I0(n)*exp((-(i-x0(n))^2-(j-y0(n))^2) * r);
 


3. Wenn man ein Array elementweise bearbeitet, kann auf die Elemente schneller zugegriffen werden, die im Speicher benachbart sind, also die erste Dimension. Deshalb wird die Schleife über die erste Dimension nach innen gelegt, und die über die zweite Dimension nach aussen. Also "for i" und "for j" vertauschen.

4. Der Ausdruck "(j-y0(n))^2)" ändert sich danach auch nicht mehr in der inneren Schleife, also wird das nur noch in der "for j"-Schleife berechnet und wieder temporär gespeichert.

5. SIZE ist ein Befehl, der Zeit benötigt. Es ist daher schneller die Dimension der Matrix einmal abzufragen und temporär zu speichern.

6. Die Begrenzung der maximalen Werte auf 255 kann auch einmalig zum Schluß durchgeführt werden.

Damit kommen wir zu:
Code:

function WillaTest

s1 = 200;
s2 = 200;
A = zeros(s1,s2);
B = zeros(s1,s2);
partAm=1000; % Amount of Particles
Z=0.2; % sheet thickness
dt=20; % particle diameter
ddt=10; % particle diameter variation
z0=abs(randn(partAm,1)); % normal distributed sheet intensity
I0=255*exp(-(Z^2./(0.125*z0))); % particle intensity
I0(I0>255)=255;
I0(I0<0)=0;
d=randn(partAm,1)/2; % particle diameter distribution
d=dt+d*ddt;
d(d<0)=0;
x0=rand(partAm,1)*s2; % x position of the particles in A
y0=rand(partAm,1)*s1; % y position of the particles in A
offsetx=0.2; % x offset for image B
offsety=2; % y offset for image B
rd = -8.0 ./ d;
tic
for n=1:partAm % for each particle
   r = rd(n);
   for j = 1:s2
      rj = (j-y0(n))^2;
      rjo = (j-y0(n)+offsety)^2;
      for i = 1:s1
         ri  = -(i-x0(n))^2;   % Nur zur Übersichtlichkeit temporär
         rio = -(i-x0(n)+offsetx)^2;
         A(i,j) = A(i,j) + I0(n) * exp((ri - rj) * r);
         B(i,j) = B(i,j) + I0(n) * exp((rio -rjo) * r);
      end
   end
end
A(A > 255) = 255;
B(B > 255) = 255;
toc
imshow(uint8(A))
figure;imshow(uint8(B))

Damit kommt ich auf meinem betagten 1.5GHz PentiumM schon mal von 9.8sec auf 5.7sec. Das ist zwar nicht umwerfend viel schneller, aber für das einfache Umsortieren von Befehlen schon ganz nett. Zudem ist der Code (meiner Meinung nach) übersichtlicher geworden, was das Vektorisieren erleichtert.

Die Multiplikation mit "I0(n)" nach den Schleifen auszuführen bringt übrigens nichts. Matlab's JIT-Compiler ist da bereits sehr effizient.

Eine Vektorisierung schicke ich im nächsten Posting.

Gruß, Jan
Private Nachricht senden Benutzer-Profile anzeigen
 
Willa
Themenstarter

Forum-Fortgeschrittener

Forum-Fortgeschrittener



Beiträge: 80
Anmeldedatum: 23.05.08
Wohnort: Bremen
Version: Willa v1.0
     Beitrag Verfasst am: 11.11.2010, 15:03     Titel:
  Antworten mit Zitat      
Hi Jan,
vielen Dank für deine ausführliche Antwort! Ich werde deine Vorschläge schnellstmöglich umsetzen. Mittlerweile habe ich einen Weg gefunden, der meinen Code 173 mal schneller laufen lässt. Ich gehe nicht das ganze Bild durch für jeden Partikel, sondern immer nur einen kleinen Bereich (setzt sich aus Partikelgröße und Versatz zusammen) um den zu zeichnenden Partikel. mal sehen wie schnell es wird wenn ich deine Vorschläge umgesetzt habe....
_________________

Viele Grüße,
William
Meine Micro Air Vehicles
Private Nachricht senden Benutzer-Profile anzeigen
 
Jan S
Moderator

Moderator


Beiträge: 11.057
Anmeldedatum: 08.07.10
Wohnort: Heidelberg
Version: 2009a, 2016b
     Beitrag Verfasst am: 11.11.2010, 16:15     Titel:
  Antworten mit Zitat      
Hallo Willa,

nun in einem zweiten Schritt die Vektorisierung:
Code:
...
tic
dn = -8 ./ d;
k1 = transpose(1:s1);
k2 = 1:s2;
for n=1:partAm % for each particle
   ri  = (k1 - x0(n)) .^ 2;
   rio = (k1 - (x0(n) + offsetx)) .^ 2;
   rj  = (k2 - y0(n)) .^ 2;
   rjo = (k2 - (y0(n) + offsety)) .^ 2;
   A   = A + I0(n) * exp(bsxfun(@plus, ri,  rj)  .* dn(n));
   B   = B + I0(n) * exp(bsxfun(@plus, rio, rjo) .* dn(n));
end
A(A > 255) = 255;
B(B > 255) = 255;
toc
...

Dies benötigt nun 4.8sec, statt 5.7 für die Version mit der doppelten Schleife. Offenbar kann Matlab die Schleifen schon ziemlich gut abarbeiten! Wo ist jetzt der Flaschenhals? In EXP!
Die EXP-Funktion wird für eine Matrix ausgerechnet, aber man kann sie separieren und auf die einzelnen Vektoren anwenden. Dann muss man nur noch 200+200 EXP-Werte berechnen, und nicht 200*200!
Code:
...
tic
dn = -8 ./ d;
k1 = transpose(1:s1);
k2 = 1:s2;
for n=1:partAm % for each particle
   ri  = exp((k1 - x0(n)) .^ 2 .* dn(n));
   rj  = exp((k2 - y0(n)) .^ 2 .* dn(n));
   rio = exp((k1 - (x0(n) + offsetx)) .^ 2 .* dn(n));
   rjo = exp((k2 - (y0(n) + offsety)) .^ 2 .* dn(n));
   
   A   = A + I0(n) * bsxfun(@times, ri,  rj);
   B   = B + I0(n) * bsxfun(@times, rio, rjo);
end
A(A > 255) = 255;
B(B > 255) = 255;
toc
...

2.1 sec! (9.8 in der Original-Version, 5.7 mit effizienten Schleifen, 4.8 mit BSXFUN statt der Schleifen).
Faktor 4.5 ist zwar ganz nett, aber der Übergang von 200x200 auf 1000x1000 Matrizen wird dadurch nicht gerade lustig. Wie könnte man das Programm dann wirklich effizient machen?

Die Summen A+I0(n)*X addieren hauptsächlich Nullen zu Nullen! Dann wäre es viel besser, man würde die maximale Ausdehnung der "Punkte" zunerst ausrechnen. Wenn sie maximal 100 Pixel Ausdehnung haben, muss man die neuen Werte ja auch nur zu den entsprechenden Sub-Matrix addieren, und auch weit weniger EXP-Werte berechnen. Bei den 200x200 Matrizen bringt das wohl einen Faktor 4, bei 1000X1000 aber schon ein Faktor 100 (5*5*4)!

Viel Erfolg beim Tüffteln, Jan
Private Nachricht senden Benutzer-Profile anzeigen
 
Willa
Themenstarter

Forum-Fortgeschrittener

Forum-Fortgeschrittener



Beiträge: 80
Anmeldedatum: 23.05.08
Wohnort: Bremen
Version: Willa v1.0
     Beitrag Verfasst am: 15.11.2010, 15:47     Titel:
  Antworten mit Zitat      
Jetzt läuft der Code 388 mal schneller als zu Beginn, ich habe versucht die Kommentare von Jan zu berücksichtigen. In meiner (bzw. "der Hochschule ihm seiner") Version (7.1 R14) gibts leider noch kein bsxfun. Den größten Geschwindigkeitszuwachs bringt wie erwartet die Begrenzung bei der Errechnung der Partikelintensitäten.
Für ein Bild der Größe 1000 * 1000 px bei 100000 Partikeln braucht mein Computer nur noch 1.21 Sekunden Very Happy
Hier der Code:
Code:
clear all
clc
warning off;
sizey=1000;
sizex=1000;
A=zeros(sizey,sizex);
B=A;
partAm=100000;
Z=0.25; %sheet thickness
dt=4; %particle diameter
ddt=1; %particle diameter variation
z0=abs(randn(partAm,1)); %normal distributed sheet intensity
I0=255*exp(-(Z^2./(0.125*z0.^2))); %particle intensity
I0(I0>255)=255;
I0(I0<0)=0;
randn('state', sum(100*clock));
d=randn(partAm,1)/2; %particle diameter distribution
d=dt+d*ddt;
d(d<0)=0;
rand('state', sum(100*clock));
x0=rand(partAm,1)*sizex;
y0=rand(partAm,1)*sizey;
rd = -8.0 ./ d.^2;
%1,22s

[X,Y]=meshgrid(-500:499,-500:499);
[THETA,RHO] = cart2pol(X,Y);
RHO=RHO/50;
offsety=sin(RHO)*2;
offsetx=cos(RHO)*2;
tic

xlimit1=floor(x0-d/2); %x min particle extent image1
xlimit2=ceil(x0+d/2); %x max particle extent image1
ylimit1=floor(y0-d/2); %y min particle extent image1
ylimit2=ceil(y0+d/2); %y max particle extent image1

xlimit2(xlimit2>sizex)=sizex;
xlimit1(xlimit1<1)=1;
ylimit2(ylimit2>sizey)=sizey;
ylimit1(ylimit1<1)=1;

%calculate particle extents for image2 (shifted image)
x0integer=round(x0);
x0integer(x0integer>sizex)=sizex;
x0integer(x0integer<1)=1;
y0integer=round(y0);
y0integer(y0integer>sizey)=sizey;
y0integer(y0integer<1)=1;

xlimit3=zeros(partAm,1);
xlimit4=xlimit3;
ylimit3=xlimit3;
ylimit4=xlimit3;

for n=1:partAm
    xlimit3(n,1)=floor(x0(n)-d(n)/2-offsetx(round(y0integer(n)),round(x0integer(n)))); %x min particle extent image2
    xlimit4(n,1)=ceil(x0(n)+d(n)/2-offsetx(round(y0integer(n)),round(x0integer(n)))); %x max particle extent image2
    ylimit3(n,1)=floor(y0(n)-d(n)/2-offsety(round(y0integer(n)),round(x0integer(n)))); %y min particle extent image2
    ylimit4(n,1)=ceil(y0(n)+d(n)/2-offsety(round(y0integer(n)),round(x0integer(n)))); %y max particle extent image2
    if xlimit3(n)<1
        xlimit3(n)=1;
    end
    if xlimit4(n)>sizex
        xlimit4(n)=sizex;
    end
    if ylimit3(n)<1
        ylimit3(n)=1;
    end
    if ylimit4(n)>sizey
        ylimit4(n)=sizey;
    end
    r = rd(n);
    for j=xlimit1(n):xlimit2(n)
        rj = (j-x0(n))^2;
        for i=ylimit1(n):ylimit2(n)
            A(i,j)=A(i,j)+I0(n)*exp((rj+(i-y0(n))^2)*r);
        end
    end
    for j=xlimit3(n):xlimit4(n)
        for i=ylimit3(n):ylimit4(n)
            B(i,j)=B(i,j)+I0(n)*exp((-(j-x0(n)+offsetx(i,j))^2-(i-y0(n)+offsety(i,j))^2)*-rd(n)); %place particle with gaussian intensity profile
        end
    end
end
toc

A(A>255)=255;                  
B(B>255)=255;

figure;imshow(uint8(A))
figure;imshow(uint8(B))


Sicherlich ist das noch deutlich weiter optimierbar (z.B. mit aktuelleren Matlab Versionen...), aber der Code läuft jetzt für meine Anwendung schnell genug.

Vielen dank an Jan & viele Grüße,
William
_________________

Viele Grüße,
William
Meine Micro Air Vehicles
Private Nachricht senden Benutzer-Profile anzeigen
 
Jan S
Moderator

Moderator


Beiträge: 11.057
Anmeldedatum: 08.07.10
Wohnort: Heidelberg
Version: 2009a, 2016b
     Beitrag Verfasst am: 17.11.2010, 12:53     Titel:
  Antworten mit Zitat      
Hallo William,

Zitat:
In meiner (bzw. "der Hochschule ihm seiner") Version (7.1 R14) gibts leider noch kein bsxfun

In der FEX gibt es eine gute BSXFUN-Emulations-Funktion:
http://www.mathworks.com/matlabcent.....e/23005-bsxfun-substitute

Gruß, Jan
Private Nachricht senden Benutzer-Profile anzeigen
 
Neues Thema eröffnen Neue Antwort erstellen



Einstellungen und Berechtigungen
Beiträge der letzten Zeit anzeigen:

Du kannst Beiträge in dieses Forum schreiben.
Du kannst auf Beiträge in diesem Forum antworten.
Du kannst deine Beiträge in diesem Forum nicht bearbeiten.
Du kannst deine Beiträge in diesem Forum nicht löschen.
Du kannst an Umfragen in diesem Forum nicht mitmachen.
Du kannst Dateien in diesem Forum posten
Du kannst Dateien in diesem Forum herunterladen
.





 Impressum  | Nutzungsbedingungen  | Datenschutz | FAQ | goMatlab RSS Button RSS

Hosted by:


Copyright © 2007 - 2025 goMatlab.de | Dies ist keine offizielle Website der Firma The Mathworks

MATLAB, Simulink, Stateflow, Handle Graphics, Real-Time Workshop, SimBiology, SimHydraulics, SimEvents, and xPC TargetBox are registered trademarks and The MathWorks, the L-shaped membrane logo, and Embedded MATLAB are trademarks of The MathWorks, Inc.