OpenMPI



  • Hallo,

    okay. Also ich habe mir da was zusammengebastelt.

    Das einzige was ich jetzt nicht verstehe: Wozu ein Broadcast? Denn wenn ich mpi langsam anfange richtig zu verstehen, dann startet jeder Prozess das komplette Programm. D.h. wenn ich mittels mpirun -np 3 ./main starte, dann laufen drei Prozesse (Threads?) und dabei jedes Mal das komplette Programm. Also 'jeder Prozess' kennt A und b! Ich muss da nix broadcasten!

    Das einzige was das folgende Programm nun tut ist, dass jeder Prozess einen eigenen Eintrag des Vektors c berechnet und am Ende führe ich mittels Reduce alles zusammen.

    Nur der Sinn von broadcast erschließt sich mir nicht. Vielleicht ist es hier (entgegen des Beispiels im Buch) auch eigentlich nicht angebracht bzw. unnötig? 😕

    #include <iostream>
    #include <numeric>
    #include <vector>
    
    #include <mpi.h>
    
    int main(int argc, char* argv[])
    {
    	/* Ab = c */
    	std::vector<double> A = { 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0 };
    	std::vector<double> b = { 1.0, 2.0, 3.0 };
    	std::vector<double> c( b.size(), 0.0 );
    	std::vector<double> my_c( b.size(), 0.0 );
    
    	int size(0), rank(0);
    
    	MPI::Init(argc, argv);
    	size = MPI::COMM_WORLD.Get_size();
    	rank = MPI::COMM_WORLD.Get_rank();
    
    	for(int i = 0; i < b.size(); ++i)
    	{
    		int offset = rank * b.size();
    		my_c[rank] = std::inner_product( A.begin() + offset, A.begin() + offset + b.size(), b.begin(), 0.0);
    	}
    
    	MPI::COMM_WORLD.Reduce( my_c.data(), c.data(), c.size(), MPI::DOUBLE, MPI::SUM, 0);
    
    	if( rank == 0 )
    		for(double x : c) std::cout << x << std::endl;
    
    	MPI::Finalize();
    
    	return 0;
    }
    

    Viele Grüße,
    -- Klaus.


  • Mod

    Meinst du allgemein den Sinn von Broadcast? Das Programm ist nicht immer im Anfangszustand. Nachdem die einzelnen Prozesse unterschiedliche Dinge berechnet haben, muessen sie in der Regel auch die Ergebnisse austauschen. Ein Broadcast ist da - je nach genauer Situation - genau richtig. Eine einfache Beispielanwendung, bei der ein Broadcast genau das richtige Mittel ist, ist folgendes: Das Programm liest eine Konfigurations- und/oder Skriptdatei ein. Es ist aber doof, diese von zig Prozessen gleichzeitig lesen und verarbeiten zu lassen. Stattdessen liest nur ein Prozess (meistens waehlt man dafuer die Nummer 0) die Daten ein und schickt sie dann an alle Prozesse. Dieses Beispiel wird noch eine Nummer extremer, wenn man sich vorstellt, dass die Konfigurationsdaten moeglicherweise interaktiv vom Nutzer eingegeben werden sollen. Wenn da jeder Prozess fragen wuerde, dann muesste der Nutzer fuer jeden Prozess diese Daten eingeben (und was passiert, wenn er unterschiedlichen Prozessen dann inkonsistente Daten gibt?).



  • Guten Morgen SeppJ,

    ich denke die Bedeutung von Broadcast ist mir bewusst.

    Ich versuche meine Frage zu präzisieren (Stream of consciousness): Wenn ich an OpenMP denke, dann ist ziemlich klar, ab wann parallel gerechnet wird, nämlich wenn

    #pragma omp parallel
    { }
    

    erscheint. Möchte ich, dass z.B. jeder Thread eine lokale Kopie erhält, muss ich für einen ordentlichen Copyconstructor von T sorgen und schreiben

    #pragma omp parallel firstprivate(T)
    { }
    

    Bei MPI erscheint mir der Spieß nun umgedreht: Jeder Thread startet das Programm selbst, d.h. es gibt kein 'vor / nach der Parallelisierung' wie bei OpenMP durch die pragma omp parallel Umgebung möglich.

    Wenn ich wie in deinem Beispiel möchte, dass nur ein Thread eine Datei einliest, dann muss ich das aktiv z.B. so gestalten

    MPI::Init(argc, argv);
    rank = MPI::COMM_WORLD.Get_rank();
    
    if( rank == 0 ) /* declared Master Thread */
      // read file
    

    So gesehen gibt es auch keinen Master Thread, sondern es gibt lediglich einen Thread, den ich (manuell) auszeichne, wie eben gezeigt mit der if Abfrage.

    Ist klar was ich meine? 😕

    Gruß,
    -- Klaus.



  • Ich denke schon. Und genau hierfür brauchst du den Broadcast nicht. Du kensnt ja schon alle Startwerte, die hart im Code codiert sind (blöde Satzformulierung oO). D.h. jeder Prozess (bei MPI sind das richtige Prozesse, nicht nur Threads) kennt die zu Beginn.

    Mithilfe des Ranks (d.h. der Nummer des aktuellen Prozesses) kannst du dir dann deine Daten aus den Vektoren rausziehen, die du berechnen willst und sie zum Ende hin zusammenführen.

    Und so machst du das ja auch schon...



  • Also ich verstehe die Anwendung von Broadcast immer noch nicht so recht. Ich habe in Anlehnung an SeppJs Vorschlag ein Miniprogramm, in dem der Master einen Vektor initialisiert und dann an alle anderen Programme 'broadcasten' soll:

    Jetzt kriege ich als Fehlermeldung, dass b not declared in this scope , was auch irgendwie einleuchtet, denn der Vektor wird nur in der if-Abfrage deklariert.

    Ja aber wie soll ich dann Daten, die nur der Master initialisiert verbreiten? Das riecht ein wenig danach, dass ich allgemein den Vektor b deklarieren muss, so dass jedes Programm ihn kennt, nur der Master den Vektor mit Inhalt füllt und diesen dann 'broadcastet'.

    Also ein zweites Programm, siehe weiter unten: Da wird zu Beginn der Vektor b der Größe 3 mit Nullen angelegt, der Master ersetzt den Inhalt und verteilt dann. Wenn ich allerdings das Programm ausführe, dann zeit die range for Schleife nur ein Mal den neuen Inhalt an, nämlich beim Master.

    Also wie soll dieses Broadcasten funktionieren? Das muss doch eine Art 'globales Send and Receive' sein, nur ich sehe vom Syntax nicht, wie die Daten tatsächlich bei den anderen Programm ankommen bzw. verarbeitet werden können (wie im Falle eines einfachen std::cout , um den Inhalt anzuzeigen).

    Edit:

    Okay, das Broadcast muss einfach aus der if-Abfrage heraus! 🙂
    Warum auch immer. 😕

    #include <iostream>
    #include <numeric>
    #include <vector>
    
    #include <mpi.h>
    
    int main(int argc, char* argv[])
    {
        MPI::Init(argc, argv);
        int const size = MPI::COMM_WORLD.Get_size();
        int const rank = MPI::COMM_WORLD.Get_rank();
    
    		std::vector<double> b( 3, 0.0 );
    
    		if( rank == 0)
    		{
        	b = { 1.0, 2.0, 3.0 };
    		}
    
    		MPI::COMM_WORLD.Bcast( b.data(), b.size(), MPI::DOUBLE, 0);
    
    		for(double x : b) std::cout << x << std::endl;
    
        MPI::Finalize();
    
        return 0;
    }
    

    Gruß,
    -- Klaus.


  • Mod

    Also fuer jemanden, der sich einen Monat damit beschaeftigt hat, ist das schon ein bisschen schwach. Das ist eigentlich der Stoff fuer den ersten Nachmittag. Hilft dir folgendes Beispiel auf die Spruenge?

    #include <iostream>
    #include <vector>
    #include <mpi.h>
    
    using namespace std;
    
    int main(int argc, char* argv[])
    {
      MPI_Init(&argc, &argv);
      int rank, size;
      MPI_Comm_rank (MPI_COMM_WORLD, &rank);
      MPI_Comm_size (MPI_COMM_WORLD, &size);
    
      vector<int> data;
      if (rank == 0)  // Prozess 0 liest die Daten ein
        {
          cout << "Daten eingeben:\n";
          for (int value; cin >> value; data.push_back(value));
        }
    
      // Daten werden verteilt
      unsigned long num_values = data.size();
      MPI_Bcast(&num_values, 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD);
      if (rank != 0)
        {
          data.resize(num_values);
        }
      MPI_Bcast(data.data(), num_values, MPI_INT, 0, MPI_COMM_WORLD);
    
      for (unsigned long i = 0; i < num_values; ++i)
        cout << "Prozess " << rank << " Wert[" << i << "] = " << data[i] << '\n';
    
      MPI_Finalize(); 
    }
    


  • Mal ganz abseits von der Fragestellung.

    Ich hab bisher immer nur was mit der C-Schnittstelle gearbeitet, aber kann es sein, dass die C++ Schnittstelle nicht gerade so toll ist?! Das sieht mir echt nicht schön aus...



  • Skym0sh0 schrieb:

    Ich hab bisher immer nur was mit der C-Schnittstelle gearbeitet, aber kann es sein, dass die C++ Schnittstelle nicht gerade so toll ist?! Das sieht mir echt nicht schön aus...

    Ich als Noob habe davon natürlich keine Ahnung, finde meinen Sourcecode oben aber ganz hübsch.

    Ich wäre ja schon froh, wenn ich mal eine geschlossene Anleitung zu MPI mittels C++(11) finden würde. Es findet sich viel in C oder Fortran und dann muss ich mir die Details mittels google und stackoverflow zusammensuchen.

    Daher würde mich interessieren, ob sich die Verwendung von mpi Boost lohnt. Das wirkt dann im Zusammenhang mit C++(11) etwas einheitlicher.

    Gruß,
    -- Klaus.


  • Mod

    Skym0sh0 schrieb:

    Ich hab bisher immer nur was mit der C-Schnittstelle gearbeitet, aber kann es sein, dass die C++ Schnittstelle nicht gerade so toll ist?! Das sieht mir echt nicht schön aus...

    Klaus82 schrieb:

    Daher würde mich interessieren, ob sich die Verwendung von mpi Boost lohnt. Das wirkt dann im Zusammenhang mit C++(11) etwas einheitlicher.

    Die C++-Bindings in den ueblichen MPI-Implementierungen kannst du vergessen. Die sind ersten deprecated und zweitens sind das praktisch die C-Bindings, bloss dass man statt "MPI_" vor dem Funktionsnamen nun einen richtigen Namespace hat. Also nichts gewonnen.

    Boost MPI ist ok. Als ich es das letzte Mal benutzt habe, fehlte leider noch einiges an Funktionalitaet, aber nur so exotische Sachen, die man ohnehin nicht braucht. Dafuer ist es richtiges C++, welches die Moeglichkeiten der Sprache auch nutzt. Dadurch ist es sehr angenehm, damit zu arbeiten und man vermeidet Fehler (zumindest Fehler in der Nutzung (z.B. Finalize oder Init vergessen), nicht in der Logik. Paralleles Programmieren ist manchmal schwer 🙂 ). Leider ist der Einarbeitungsaufwand recht gross, da man gleich auch noch Boost Serialization und Boost Graph benoetigt (Serialization braucht man dringend, Graph ist eher optional). Das ist zwar letztendlich viel angenehmer, als alle Daten selber zu verpacken oder die Kommunikationsstruktur selber zu programmieren, aber ist schon eine recht grosse Huerde fuer den Einstieg, da die beiden Bibliotheken doch recht komplex sind.



  • Hm,
    ich denke ich werde nicht tiefer einsteigen als dieses Broadcast und vielleicht noch send / receive.

    Dazu scheint es anhand der Beispiele zu genügen die zugehörigen boost Header Dateien einzubinden und schließlich den Syntax zu ändern.

    Das sollte es für meine minimalen Anforderungen schon gewesen sein?

    Gruß,
    -- Klaus.


  • Mod

    Klaus82 schrieb:

    Das sollte es für meine minimalen Anforderungen schon gewesen sein?

    Wir kennen ja deine Anforderungen nicht. Die meisten MPI-Programme bestehen aber zum größten Teil aus einer Mischung aus Send, Receive und Broadcast. Aus was auch sonst? MPI dient der Kommunikation und dies sind die Kommunikationsmethoden. Der Rest von MPI dient dem Aufsetzen und Feintuning der Kommunikation. Wenn einem die Standardeinstellungen passen (90% der Fall), dann braucht man davon nur Init, Finalize, GetRank und ähnliches.

    Die wahre Schwierigkeit ist aber das parallele Programmieren an sich. MPI ist nur das Werkzeug und seine Benutzung ist der mit Abstand einfachste Teil der parallelen Programmierung.


Anmelden zum Antworten