< Installation | Hello World MyMPI.hpp | Kommunikation Teil 1 > |
Hello WorldWie in jeder Programmiersprache üblich, so soll auch hier das erste Beispiel die Ausgabe von Hello World sein. Für die Verdeutlichung der Parallelisierung wird zusätzlich die ID des Prozesses, die Anzahl aller Prozesse und der Namen des Knotens, auf dem der Prozess ausgeführt wird, ausgegeben. Der Quell-Code beginnt mit dem Einbinden des Interfaces <mpi.h> für die MPI-Bibliothek. Da in diesem Tutorial die C++-Bindings benutzt werden, muss vor dem MPI-Header _MPI_CPP_BINDINGS definiert werden, da sonst auf manchen Plattformen Übersetzungsfehler erfolgen. Ebenfalls sollte erst nach dem MPI-Header <iostream> eingebunden werden, da wiederum definierte Parameter mit gleichen Namen im MPI und iostream-Header vorhanden sind. Falls dies nicht gewährleistet werden kann, werden Übersetzungsfehler durch den Pre-Compiler-Befehl #error erfolgen und man muss mit undef/define-Anweisungen das Problem umgehen. Der erste MPI-Befehl in diesem und auch anderen parallelen Programmen ist der Aufruf der Initialisierungsfunktion Init(). Ob mit oder ohne Parameter hängt davon ab, ob andere Bibliotheken die Parameter benötigen. Vor Init() dürfen keine MPI-Befehle ausgeführt werden. Genauso verhält es sich mit dem Schluss. Nach Finalize(), welcher als letzter MPI-Befehl immer erfolgen muss, darf kein weiterer MPI-Aufruf ausgeführt werden. Ausnahmen sind die drei Funktionen MPI::Is_initialized(), MPI::Is_finalized() und MPI::Get_version(). Die beiden wichtigsten MPI-Befehle stehen in Zeile 9 und 10. Diese erfragen die ID und die Anzahl der Prozesse. Dabei wird das Kommunicator-Object COMM_WORLD benutzt. Dieser Kommunikator umfasst die Menge aller Prozesse und wird beim Start definiert. Von diesem können im Programm andere Kommunikatoren abgeleitet werden. In Zeile 12 bis Zeile 16 wird der Name des Knotens abgefragt, was etwas aufwändiger ist. Zunächst muss eine Variable für die Länge des Strings deklariert werden. Danach erfolgt das Anlegen eines char* Strings mit einer von MPI definierten maximalen Länge (meist 128 Zeichen). Die zwei Variablen werden dann der Funktion Get_processor_name() übergeben. Danach steht in length die Länge des Strings pName. Für die bessere Handhabbarkeit lege ich den Namen des Knotens in einen std::string name ab. Dann kann auch der angeforderte Speicher wieder frei gegeben werden. Andere, dem Standard entsprechende Möglichkeiten den Namen über MPI einfacher zu bestimmen sind mir nicht bekannt. Zum Schluss erfolgt die übliche Ausgabe an std::cout. Dabei ist die Reihenfolge nicht gleich und zufällig. 1 #define _MPI_CPP_BINDINGS 2 #include <mpi.h> 3 #include <iostream> 4 5 int main(int argc, char* argv[]) 6 { 7 MPI::Init(argc, argv); 8 9 int rank = MPI::COMM_WORLD.Get_rank(); 10 int size = MPI::COMM_WORLD.Get_size(); 11 12 int length; 13 char* pName = new char[MPI::MAX_PROCESSOR_NAME+1]; 14 MPI::Get_processor_name(pName, length); 15 std::string name(pName); 16 delete [] pName; 17 18 std::cout << "Hello World! I am " << rank << " of " << size; 19 std::cout << " running on host " << name.c_str() << std::endl; 20 21 MPI::Finalize(); 22 } Da die Parameter wie ID und Anzahl der Prozesse oft benötigt werden, habe ich im Rahmen dieses Tutorials eine Singleton-Klasse geschrieben. Diese dient als eine Art Wrapper um auf die eben angesprochenen Eigenschaften zuzugreifen und wird in größeren Beispielen des Tutorials benutzt werden. 1 /** 2 \file MyMPI.hpp 3 \brief a Singleton for MPI 4 \author Matthias Finke 5 \date 08.10.2006 6 \version 0.1 7 */ 8 9 #ifndef MYMPI_HPP_ 10 #define MYMPI_HPP_ 11 12 #define _MPI_CPP_BINDINGS 13 #include <mpi.h> 14 #include <string> 15 #include <cassert> 16 #include <ostream> 17 18 19 namespace MF 20 { 21 22 class MyMPI 23 { 24 int rank_; /**< the ID of a process*/ 25 int size_; /**< the number of all processes*/ 26 MPI::Intracomm world_; /**< the communicator COMM_WORLD*/ 27 std::string name_; /**< the name of the node the process is running on*/ 28 static const MyMPI* instance_; /**< the single instance of this class*/ 29 30 public: 31 32 /** 33 Returns the single instance of this class. If \a instance_ wasn't initialized yet, it is instantiated. 34 \return a const pointer to the single instance 35 \note MPI::Init is called without parameters 36 */ 37 static const MyMPI* instance() 38 { 39 if (! instance_) 40 { 41 MPI::Init(); 42 assert(MPI::Is_initialized() == true); 43 instance_ = new MyMPI; 44 static Destroyer d; 45 } 46 return instance_; 47 } 48 49 int rank() const {return instance_->rank_;} /**< \returns the rank of the process*/ 50 int size() const {return instance_->size_;} /**< \returns the number of all processes*/ 51 std::string name() const {return instance_->name_;} /**< \returns the name of the node*/ 52 53 /** \return the communicator MPI::COMM_WORLD*/ 54 const MPI::Intracomm& world() const {return instance_->world_;} 55 56 private: 57 58 /** The default-constructor is private and called via instance() */ 59 MyMPI() 60 : rank_(MPI::COMM_WORLD.Get_rank()) 61 , size_(MPI::COMM_WORLD.Get_size()) 62 , world_(MPI::COMM_WORLD) 63 { 64 int length; 65 char* pName = new char[MPI::MAX_PROCESSOR_NAME+1]; 66 MPI::Get_processor_name(pName, length); 67 name_ = pName; 68 delete [] pName; 69 } 70 71 /** \note the destructor calls MPI::Finalize() */ 72 ~MyMPI() 73 { 74 MPI::Finalize(); 75 assert(MPI::Is_finalized() == true); 76 } 77 78 /** the copy C'tor is forbidden */ 79 MyMPI(const MyMPI&) { } 80 81 /** 82 a private class for explicit destruction of the singleton 83 */ 84 class Destroyer 85 { 86 public: 87 /** the destructor deletes the singleton*/ 88 ~Destroyer() 89 { 90 if( MyMPI::instance_ != NULL ) 91 delete MyMPI::instance_; 92 } 93 }; 94 friend class Destroyer; 95 96 public: 97 /** 98 \param o a std::ostream 99 \param m a MyMPI object 100 \return a std::ostream 101 */ 102 friend std::ostream& operator<<(std::ostream& o, const MyMPI& m) 103 { return o << "process " << m.rank_ << " of " << m.size_ << " running on " << m.name_;} 104 105 }; 106 const MyMPI* MyMPI::instance_ = NULL; 107 108 }//end namespace 109 110 #endif |