Schöne Lösung gesucht: Sowas wie cout für MPI-Programme
-
Hallo,
Ich schreibe momentan ein paralleles Programm auf Basis von boost::mpi. Nun wird ab und zu was ausgegeben, wobei die Ausgabe jeweils immer nur einmal erfolgen soll und nicht auf alles PEs. Bisher habe ich sowas in der Art gemacht:
#include <iostream> #include <boost/mpi.hpp> namespace mpi = boost::mpi; int main(int narg, char* arg[]) { mpi::environment env(narg, arg); mpi::communicator world; unsigned rank = world.rank(); if(rank == 0) std::cout << "running on " << world.size() << " CPUs" << std::endl; }Da ich aber ziemlich viele Ausgaben im Laufe des Programmes einbauen werde finde ich die Lösung mit dem if(rank == 0) nicht so schön. Besser wäre es sowas wie cout zu haben was nur für rank == 0 auf die Standardausgabe schickt und in allen anderen Fällen einfach nichts tut.
Weiß jemand, ob es sowas schon fertig gibt bzw. hat jemand ne Idee wie ich das möglichst ohne großen Aufwand bewerkstelligen kann. Ich steh da momentan ein wenig auf dem Schlauch was eine elegante Lösung betrifft und eigentlich ist es ein unwichtiges Detail aber die dauernden if's nerven mich
.Viele Grüße,
Walli
-
Hi,
würde das gelten ?#include <iostream> #include <boost/mpi.hpp> namespace mpi = boost::mpi; ostream& operator<<(ostream &os, const mpi::communicator& w) { if( w.rank() == 0 ) std::cout << "running on " << world.size() << " CPUs" << std::endl; return os; } int main(int narg, char* arg[]) { mpi::environment env(narg, arg); mpi::communicator world; std::cout << world << std::endl; }Könnte verwirrend wirken, aber wenn world "leer" ist, soll ja auch nicht ausgegeben werden

-
Hmm... bei den Überlegungen zu dem Problemchen hab ich mich ein wenig verrannt - das Ergebnis war die idee eines Streams mit ein-aus-Schalter:
class DoNothingStreamBuf : public std::streambuf { /* ??? */ //müsste so implementiert werden, dass jede Ausgabe ohne Wirkung ist... }; class SwitchableOstream : public std::ostream { std::streambuf* saveBuf; public: explicit SwitchableOstream(std::streambuf* sb) : std::ostream(sb), saveBuf(NULL) {} bool isOn() { return saveBuf == NULL; } void switchOff() { if (isOn()) { saveBuf = rdbuf(new DoNothingStreamBuf); //internen Streambuf auf donothing setzen und vorher assoziierten streambuf in saveBuf speichern } } void switchOn() { if (not isOn()) { delete rdbuf(saveBuf); //in saveBuf gespeicherten Streambuf setzen und vorher assoziierten donothingstreambuf zerstören saveBuf = NULL; } } }; //Benutzung: int main(int narg, char* arg[]) { mpi::environment env(narg, arg); mpi::communicator world; unsigned rank = world.rank(); SwitchableOstream scout(std::cout.rdbuf()); //schreibt auf den gleichen strambuf wie cout - wenn er schreibt... if(rank != 0) scout.switchOff(); scout << "running on " << world.size() << " CPUs" << std::endl; }Ich hab mich "nur" noch nicht damit beschäftigt wie man streambuf's ableitet, also welche Methoden man implementieren muss (std::streambuf ist abstract)...
Kurze Erläuterung: wie jeden anderen ostream initialisiert man den SwitchableOstream mit einem streambuf, und zwar mit dem, an den die Ausgabe im Zustand "an" gehen soll. Wird der Stream ausgeschaltet, dann wird dem geerbten ostream der buffer entzogen und durch einen DoNothingStreamBuf ersetzt, damit folgende Ausgaben keinen Effekt haben. Der ursprünglich überreichte Streambuffer wird derweil zwischengespeichert in saveBuf.
/edith:
class DoNothingStreamBuf : public std::streambuf { const static std::size_t buflen = 1024; char dummybuf[buflen]; public: DoNothingStreambuf() : pptr(dummybuf+buflen/2) { std::fill(dummybuf, dummybuf+buflen/2, '\0'); setg(dummybuf, dummybuf, dummybuf+buflen/2); //erste Haelfte: Spielplatz fuer Lesezugriffe, liefern immer '\0' setp(dummybuf+buflen/2, dummybuf+buflen); //zweite Haelfte: Spielplatz fuer Schreibzugriffe } protected: virtual int underflow() { gptr = dummybuf; return '\0'; } virtual int overflow(int) { pptr = dummybuf + buflen/2; return (EOF == 1) ? 2 : 1; //irgendwas, nur nicht EOF } };Dieser streambuf macht wirklich garnichts, außer die lese/schreibepointer des streambuf auf einem dummybuffer zu plazieren wo sie lustig rumschreiben/lesen können - wird das Ende erreicht, dann wird der entsprechende Pointer wieder an den Anfang des Buffers gesetzt.
-
@pumuckl,
Hast du dich nicht ein wenig sehr verrannt? Das folgende funktioniert bei mir unter G++ und MSVC.std::ostream out(std::cout.rdbuf()); if(world.rank() == 0) { out.rdbuf(0); } // ...Man setzt einfach den Buffer auf 0, dann hat man einen leeren Stream. Aber ob das sinnvoll ist? Man führt dann unnötige Operationen auf einem leeren Stream aus.
Grüssli
-
Danke schonmal für eure Überlegungen. Zum letzten Post: Es ist nicht schlimm wenn unnötige Operationen auf einen Stream ausgeführt werden. Da geht es mir eher um Eleganz als um Effizienz weil die Ausgaben unkritisch sind. Zwischen zwei Ausgaben wird zuviel gerechnet werden als dass sie irgendwie ins Gewicht fallen.
KasF: Ne, ich musste erst genauer hinsehen um zu erkennen was Du meinst. Das ist nicht was ich suche. Es soll für beliebige Ausgaben gehen und nicht bloß die, wo man sagt auf wieviel CPUs man arbeitet.
pumuckl: Hmm, ich habe es mir noch nicht komplett angesehen, aber im Prinzip das was ich suche: Nur bei der Menge an zusätzlichem Code kann ich auch direkt überall das if davorschreiben
.Dravere: Die Lösung gefällt mir eigentlich. Hat jemand eine Ahnung ob es kritisch wäre das direkt mit cout zu machen? Also etwa: "if(rank != 0) std::cout.rdbuf(0);". Mir ist bewusst, dass das vielleicht nicht die feine englische Art ist, aber die Zwischenausgaben werden nur auf CPU 0 gemacht. Eventuell ist es schlecht, wenn man mit einem Fehler abbrechen und eine Nachricht ausgeben will, weil Fehler können durchaus lokal auf nur einer CPU passieren. Auf der anderen Seite gehört das aber auch eher nach cerr, oder?