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

Parallel Computing Toolbox -- Brauche Ansatz für Fehlersuch

 

Nicolas
Forum-Anfänger

Forum-Anfänger


Beiträge: 11
Anmeldedatum: 12.09.12
Wohnort: ---
Version: ---
     Beitrag Verfasst am: 19.03.2014, 21:35     Titel: Parallel Computing Toolbox -- Brauche Ansatz für Fehlersuch
  Antworten mit Zitat      
Hallo allesamt!

Ich möchte
Code:
verwenden um die mehrfache Ausführung einer Funktion zu parallelisieren. Hierbei tritt jedoch ein Fehler auf dessen Ursache sich mir nicht erschließt.
Ich erhoffe mir von euch einen Hinweis für die weitere Recherche, oder aber eine Erklärung, weshalb es zu diesem Fehler kommt.

Dies ist ein Minimalbeispiel meiner Funktion:
Code:
function ElementsOut = ComputeParallel(FunctionHandle, ElementsIn, NOut)
   
    % Diese Funktion ist eine Alternative zu cellfun, die zusätzlich die
    % Funktionen der Parallel Computing Toolbox nutzt.
    % ---------------------------------------------------------------------
    %
    % FunctionHandle ist eine beliebige Funktion, die NIn Parameter
    % verlangt.
    %
    % ElementsIn ist ein NElements x NIn großes cell array, welches alle
    % Eingabeparameter für die Berechnungen der Funktion FunctionHandle
    % umfasst.
    %
    % NOut gibt an, wie viele Ausgabeparameter FunctionHandle generiert,
    % bzw. wieviele Ausgabeparameter gespeichert werden sollen.
   
    % Initialisiere Ausgabewerte:
    ElementsOut = cell(size(ElementsIn, 1), NOut);
   
    % Berechne Ausgabewerte:
    parfor IndexElements = 1 : size(ElementsIn, 1)
        [ElementsOut{IndexElements, :}] = FunctionHandle(ElementsIn{IndexElements, :});
    end
   
end


Bei einem Aufruf dieser Funktion, z.B. auf diese Weise:
Code:
A = ComputeParallel(@abs, {-1; -2; -3; -4; -5}, 1);


... wird die folgende Fehlermeldung generiert:
Code:
Error using ComputeParallel>(parfor body) (line 22)
The left hand side has ElementsOut{:} inside brackets, which requires that ElementsOut be defined, so that the number of expected results can be computed.

Error in ComputeParallel (line 21)
    parfor IndexElements = 1 : size(ElementsIn, 1)

Caused by:
    The left hand side has ElementsOut{:} inside brackets, which requires that ElementsOut be defined, so that the number of expected results can be
    computed.


Genau der selbe Aufruf funktioniert jedoch anstandslos, wenn ich die parfor-Schleife durch eine for-Schleife ersetze.

Nun frage ich mich unter anderem, wieso in der Fehlermeldung steht, dass ElementsOut zunächst definiert werden muss, bevor ihm in der Zeile
Code:
[ElementsOut{IndexElements, :}] = FunctionHandle(ElementsIn{IndexElements, :});

ein Wert zugewiesen werden kann, da ich ElementsOut ja initialisiert habe.

Ich hoffe auf eure Hilfestellung!

Mit freundlichen Grüßen
Nicolas
Private Nachricht senden Benutzer-Profile anzeigen


Harald
Forum-Meister

Forum-Meister


Beiträge: 24.495
Anmeldedatum: 26.03.09
Wohnort: Nähe München
Version: ab 2017b
     Beitrag Verfasst am: 21.03.2014, 11:42     Titel:
  Antworten mit Zitat      
Hallo,

probiers mal so:
Code:
function ElementsOut = ComputeParallel(FunctionHandle, ElementsIn, NOut)
   
    % Diese Funktion ist eine Alternative zu cellfun, die zusätzlich die
    % Funktionen der Parallel Computing Toolbox nutzt.
    % ---------------------------------------------------------------------
    %
    % FunctionHandle ist eine beliebige Funktion, die NIn Parameter
    % verlangt.
    %
    % ElementsIn ist ein NElements x NIn großes cell array, welches alle
    % Eingabeparameter für die Berechnungen der Funktion FunctionHandle
    % umfasst.
    %
    % NOut gibt an, wie viele Ausgabeparameter FunctionHandle generiert,
    % bzw. wieviele Ausgabeparameter gespeichert werden sollen.
   

    ElementsOut = cell(size(ElementsIn, 1), NOut);
   
    % Berechne Ausgabewerte:
    parfor IndexElements = 1 : size(ElementsIn, 1)
        ElementsOut(IndexElements, :) = ComputeParallelLocal(FunctionHandle, ElementsIn{IndexElements, :}, NOut);
    end
   


function LocalElementsOut = ComputeParallelLocal(FunctionHandle, LocalElementsIn, NOut)

  LocalElementsOut = cell(1, NOut);
 [LocalElementsOut{:}] = FunctionHandle(LocalElementsIn);


Bitte überprüfen, ob das auch in komplexeren Fällen das gewünschte macht. Ich habe es jetzt nur für dein Beispiel überprüft.

Grüße,
Harald
Private Nachricht senden Benutzer-Profile anzeigen
 
Nicolas
Themenstarter

Forum-Anfänger

Forum-Anfänger


Beiträge: 11
Anmeldedatum: 12.09.12
Wohnort: ---
Version: ---
     Beitrag Verfasst am: 21.03.2014, 18:49     Titel:
  Antworten mit Zitat      
Hallo Harald, vielen Dank für deine Antwort.

Die Funktion ist jetzt lauffähig, ich habe sie allerdings noch dahingehend abgeändert, dass sie auch function handle, die mehrere Eingabeparameter verlangen, verarbeiten kann.

Code:
function ElementsOut = ComputeParallel(FunctionHandle, ElementsIn, NOut)

    ElementsOut = cell(size(ElementsIn, 1), NOut);
   
    parfor IndexElements = 1 : size(ElementsIn, 1)
        ElementsOut(IndexElements, :) = ComputeParallelLocal(FunctionHandle, ElementsIn(IndexElements, :), NOut);
    end
   
end

function LocalElementsOut = ComputeParallelLocal(FunctionHandle, LocalElementsIn, NOut)
   
    LocalElementsOut = cell(1, NOut);
    [LocalElementsOut{:}] = FunctionHandle(LocalElementsIn{:});
   
end


Alternativ bin ich auch auf diese Lösung gekommen, die keiner weiteren lokalen Funktion bedarf, dafür aber jede Rückgabe in einer eigenen cell kapselt und eine Warnung bezüglich möglicherweise großem "communication overhead" ausgibt:
Code:
function ElementsOut = ComputeParallel(FunctionHandle, ElementsIn, NOut)
   
    ElementsOut = cell(size(ElementsIn, 1), 1);
   
    parfor IndexElements = 1 : size(ElementsIn, 1)
        ElementsOut{IndexElements} = cell(1, NOut);
        ElementsOut{IndexElements}{:} = FunctionHandle(ElementsIn{IndexElements, :});
    end
   
end


Ich habe mich für die Lösung mit deinem Ansatz entschieden, allerdings tritt nun bei beiden Varianten gleichermaßen folgendes Phänomen auf:
Außerhalb der eigentlichen Berechnung des Schleifeninhalts vergeht ca. noch einmal das 2-fache der eigentlichen Ausführungszeit, scheinbar um die parallele Berechnung zu initialisieren, oder ähnliches ...

In meinem Beispiel handelt es sich um 32 Schleifendurchläufe, ich habe 2 Worker aktiv.
Nun habe ich einmal a) alle Zeiten gemessen, die für die Ausführung jeder Schleifeniteration benötigt werden, und diese aufsummiert und b) die Zeit für den Durchlauf des gesamten Schleifenkörpers.

Im Vergleich zwischen seriellem (for) und parallelem Durchlauf (parfor) zeigt sich das beschriebene Problem:

Finished all serial calculations.
sum of all execution runtimes: 2.5465s
whole loop runtime: 2.5471s

Finished all parallelized calculations.
sum of all execution runtimes: 3.6171s
whole loop runtime: 11.0728s

Nun habe ich vermutet, dass vielleicht das Kopieren der zu verarbeitenden Daten (vom Host auf die Worker?) ein Problem darstellen könnte, allerdings scheint dem nicht so zu sein, da eine einfachere Funktion, wie @abs mit den selben Daten wesentlich weniger absoluten Laufzeitunterschied zwischen paralleler und serieller Verarbeitung zeigt, jedoch proportional ist zu der eigentlichen Rechenzeit innerhalb der Schleife:

Finished all serial calculations.
sum of all execution runtimes: 0.11155s
whole loop runtime: 0.1125s

Finished all parallelized calculations.
sum of all execution runtimes: 0.14114s
whole loop runtime: 0.83217s

Hast du oder jemand anderes eine Vermutung wie die Rechenzeitverlängerung zustande kommt?

Ich arbeite mit MATLAB R2013a unter Windows 7 Professional.

Beste Grüße,
Nicolas
Private Nachricht senden Benutzer-Profile anzeigen
 
Harald
Forum-Meister

Forum-Meister


Beiträge: 24.495
Anmeldedatum: 26.03.09
Wohnort: Nähe München
Version: ab 2017b
     Beitrag Verfasst am: 21.03.2014, 19:53     Titel:
  Antworten mit Zitat      
Hallo,

sind die Worker lokal oder auf einem Cluster?
Wie wurde die Zeitmessung vorgenommen?

Das komplette Beispiel wäre hilfreich.

Grundsätzlich ist Parallelisierung mit parfor nur zu empfehlen, wenn die Rechenzeit ausreichend groß ist, um den Overhead (mehr als) aufzuwiegen.

Grüße,
Harald
Private Nachricht senden Benutzer-Profile anzeigen
 
Nicolas
Themenstarter

Forum-Anfänger

Forum-Anfänger


Beiträge: 11
Anmeldedatum: 12.09.12
Wohnort: ---
Version: ---
     Beitrag Verfasst am: 22.03.2014, 09:31     Titel:
  Antworten mit Zitat      
Die Worker sind lokal.

Leider ist das komplette zuvor beschriebene Beispiel nur sehr schwer in eine Form zu bringen, die leicht bereitzustellen wäre.

Aber hier ist ein Ausschnitt, der u.a. die Zeitmessung klärt:

Code:

function [Repo, Result] = addField(Repo, FieldToAdd, Input, OrderDimInput, PosCongInput, FieldInput, ComputeParallel)

    % [...] Aufbereitung der Daten, sodass sie vorliegen,
    % wie im Minimalbeispiel

            % Calculate ElementsOut (execute Input):
            % -------------------------------------------------------------
            % Calculate number of resulting elements (NElementsOut):
            NElementsOut = prod(SizeElementsOut);
            % Initialize cell array for resulting elements (ElementsOut):
            ElementsOut = cell(NElementsOut, NFieldToAdd);
            % Calculate ElementsOut elementwise:
            Time = zeros(1, NElementsOut);
            if ComputeParallel
                ticID = tic;
                parfor IndexOut = 1 : NElementsOut
                    tic
                    ElementsOut(IndexOut, :) = executeInput(Input, ElementsIn(IndexOut, :), NFieldToAdd);
                    Time(IndexOut) = toc;
                end
                disp(['Finished all parallelized calculations.', char(10), ...
                      'sum of all execution runtimes: ', num2str(sum(Time)), 's', char(10), ...
                      'whole loop runtime: ', num2str(toc(ticID)), 's']);
            else
                ticID = tic;
                for IndexOut = 1 : NElementsOut
                    tic
                    [ElementsOut{IndexOut, :}] = Input(ElementsIn{IndexOut, :});
                    Time(IndexOut) = toc;
                end
                disp(['Finished all serial calculations.', char(10), ...
                      'sum of all execution runtimes: ', num2str(sum(Time)), 's', char(10), ...
                      'whole loop runtime: ', num2str(toc(ticID)), 's']);
            end

    % [...] Weiterverarbereitung der Daten, sodass sie dem Format des
    % Repositories (Repo) entsprechen.

end

function ElementsOut = executeInput(Input, ElementsIn, NElementsOut)
   
    ElementsOut = cell(1, NElementsOut);
    [ElementsOut{:}] = Input(ElementsIn{:});
   
end
 


Der Aufruf dieser Funktion erfolgte dann so:

Code:

% Verwendet für die Ergebnisse der 1. Zeitmessung:
Input = @(Phase, Mask) unwrapDataSet(Phase, Mask, Pos2Indices([0, 0, 0], 'Phys', Repo{1}.Header.Orientation, 2), [-pi, pi], 3);
% Verwendet für die Ergebnisse der 2. Zeitmessung:
% Input = @(Phase, Mask) abs(Phase) + abs(Mask);

disp(char(10));

Repo{1} = addField(...
    Repo{1}, ...
    'PhaseUnwrapped1', ...
    Input, ...
    {'Line', 'Column', 'Slice'}, ...
    'PhaseWrapped', ...
    {'PhaseWrapped', 'Mask'}, ...
    false);

disp(' ');

Repo{1} = addField(...
    Repo{1}, ...
    'PhaseUnwrapped2', ...
    Input, ...
    {'Line', 'Column', 'Slice'}, ...
    'PhaseWrapped', ...
    {'PhaseWrapped', 'Mask'}, ...
    true);
 


Hierzu ist zu sagen, dass 'Repo{1}' eine größere Struktur von ca. 450 MB ist.
Das Feld namens Input entspricht der auszuführenden Funktion.
Beide von mir verwendeten Funktionen, für die ich die Zeiten in meiner vorigen Antwort gelistet habe, sind im oberen Codebeispiel enthalten (die zweite auskommentiert).

Mir ist nun eingefallen, dass in meiner Funktion:
Code:

Input = @(Phase, Mask) unwrapDataSet(Phase, Mask, Pos2Indices([0, 0, 0], 'Phys', Repo{1}.Header.Orientation, 2), [-pi, pi], 3);
 

auf einen Teilbereich des Repositories 'Repo{1}.Header.Orientation' zugegriffen wird (nur wenige Byte groß) und dies vielleicht auf Grund der Größe vom gesamten 'Repo{1}' problematisch sein könnte.

Ich habe also 'Input' folgendermaßen abgeändert:
Code:

Orientation = Repo{1}.Header.Orientation;
Input = @(Phase, Mask) unwrapDataSet(Phase, Mask, Pos2Indices([0, 0, 0], 'Phys', Orientation, 2), [-pi, pi], 3);
 


und nun das folgende, wesentlich bessere Ergebnis in der Zeitmessung erhalten:

Code:

Finished all serial calculations.
sum of all execution runtimes: 2.5883s
whole loop runtime: 2.589s
 
Finished all parallelized calculations.
sum of all execution runtimes: 3.4535s
whole loop runtime: 2.4932s
 


Grüße,
Nicolas
Private Nachricht senden Benutzer-Profile anzeigen
 
Harald
Forum-Meister

Forum-Meister


Beiträge: 24.495
Anmeldedatum: 26.03.09
Wohnort: Nähe München
Version: ab 2017b
     Beitrag Verfasst am: 22.03.2014, 10:17     Titel:
  Antworten mit Zitat      
Hallo,

wie ergibt sich elementsIn aus den Eingabeargumenten?

Wenn du eine recht kurze Ausführungszeit mit großen Datenmengen hast, dann ist das nicht ideal für parfor. Man sollte darauf achten, nur die Daten an die Worker zu schicken, die sie auch wirklich benötigen.

Grüße,
Harald
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 - 2024 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.