Welche Funktionen benötigen Benutzer von einer MPI C ++ - Schnittstelle?

28

In der Version 3.0 des MPI-Standards wurde die C ++ - Schnittstelle formal gelöscht (sie war zuvor veraltet). Während Implementierungen dies möglicherweise weiterhin unterstützen, ist für die in MPI-3 neueren Funktionen keine C ++ - Schnittstelle im MPI-Standard definiert. Weitere Informationen finden Sie unter http://blogs.cisco.com/performance/the-mpi-c-bindings-what-happened-and-why/ .

Die Motivation zum Entfernen der C ++ - Schnittstelle aus MPI war, dass sie keinen signifikanten Wert gegenüber der C-Schnittstelle hatte. Abgesehen von "s / _ / :: / g" gab es nur sehr wenige Unterschiede, und viele Funktionen, an die C ++ - Benutzer gewöhnt sind, wurden nicht verwendet (z. B. automatische Typbestimmung über Vorlagen).

Als jemand, der am MPI-Forum teilnimmt und mit einer Reihe von C ++ - Projekten arbeitet, die eine eigene C ++ - Schnittstelle für die MPI-C-Funktionen implementiert haben, möchte ich wissen, was die wünschenswerten Merkmale einer C ++ - Schnittstelle für MPI sind. Während ich mich zu nichts verpflichte, wäre ich an der Implementierung einer eigenständigen MPI C ++ - Schnittstelle interessiert, die die Anforderungen vieler Benutzer erfüllt.

Und ja, ich kenne Boost :: MPI ( http://www.boost.org/doc/libs/1_54_0/doc/html/mpi.html ), aber es werden nur MPI-1-Funktionen unterstützt und das Serialisierungsmodell wäre extrem schwer für RMA zu unterstützen.

Eine C ++ - Schnittstelle zu MPI, die ich mag, ist die von Elemental ( https://github.com/poulson/Elemental/blob/master/src/core/imports/mpi.cpp ), also können die Leute vielleicht ein paar Pro und Contra-Funktionen bereitstellen Ansatz. Insbesondere denke ich, dass MpiMap ein wesentliches Problem löst.

Jeff
quelle
Ich denke nicht, dass dies der richtige Ort für eine solche Frage ist.
Jack Poulson
Können Sie dafür einige Gründe nennen? Viele der MPI-Fragen auf dieser Website lassen darauf schließen, dass die Leute hier bereit sind, diese Frage zu beantworten. 0,2 Upvotes pro Minute lassen vermuten, dass andere Personen Ihrer Einschätzung nicht zustimmen. In jedem Fall wäre es hilfreicher, einen alternativen Ort vorzuschlagen, um dies zu posten, wenn Sie den aktuellen Veranstaltungsort nicht mögen.
Jeff
Die Frage ist wertvoll, und ich denke, sie könnte einige wertvolle Antworten auf breitere Mailinglisten für Computerwissenschaften erhalten, wenn sie dort verfügbar ist. (Möglicherweise NA-Digest, SIAM-CSE oder sogar ein öffentlicher Post auf G +?) Diese Frage passt möglicherweise nicht zu einer Stack Exchange-Site, da sie subjektiv ist (siehe scicomp.stackexchange.com/help/dont-ask ). . Solange die Antworten konkret sind und sich auf bestimmte Anwendungsfälle konzentrieren (ohne signifikante Wiederholungen oder Überschneidungen), halte ich es für sinnvoll, sie offen zu halten.
Geoff Oxberry
@ Jeff: Die Frage kommt mir wie eine Umfrage vor. Ich bestreite nicht, dass es wertvoll ist, aber ich sehe keine akzeptierte Antwort. Wäre eine solche Frage für das MPI-Forum ungewöhnlich?
Jack Poulson
@ JackPoulson Ich möchte nicht wissen, was die Implementierer für die richtige Antwort halten. Ich möchte wissen, was Computerwissenschaftler brauchen. Insofern hat die Frage objektive Antworten. Es gibt keine richtige Antwort, aber das bedeutet nicht, dass es sich um eine subjektive Situation handelt.
Jeff

Antworten:

17

Lassen Sie mich zunächst beantworten, warum ich denke, dass C ++ - Schnittstellen zu MPI im Allgemeinen nicht übermäßig erfolgreich waren, nachdem ich lange über das Problem nachgedacht hatte, als ich versuchte, zu entscheiden, ob wir nur die Standard-C-Bindungen von MPI verwenden oder etwas auf höherer Ebene aufbauen sollten :

Wenn Sie sich MPI-Codes aus der Praxis ansehen (z. B. PETSc oder in meinem Fall II), stellt sich heraus, dass die Anzahl der MPI-Anrufe möglicherweise überraschend gering ist. In den 500k-Zeilen von deal.II gibt es beispielsweise nur ~ 100 MPI-Anrufe. Dies hat zur Folge, dass die Schmerzen bei der Verwendung von Interfaces auf niedrigerer Ebene wie der MPI C-Bindung nicht zu groß sind. Umgekehrt würde man durch die Verwendung übergeordneter Schnittstellen nicht allzu viel gewinnen.

Meine zweite Beobachtung ist, dass auf vielen Systemen mehrere MPI-Bibliotheken installiert sind (verschiedene MPI-Implementierungen oder verschiedene Versionen). Dies ist eine erhebliche Schwierigkeit, wenn Sie beispielsweise boost :: mpi verwenden möchten, das nicht nur aus Headerdateien besteht: Entweder muss dieses Paket auch mehrfach installiert werden, oder es muss als Teil des Pakets erstellt werden Projekt, das boost :: mpi verwendet (aber das ist wieder ein Problem für sich, da boost ein eigenes Build-System verwendet, das sich von allem anderen unterscheidet).

Ich denke, all dies hat sich gegen die aktuelle Anzahl von C ++ - Schnittstellen zu MPI verschworen: Die alten MPI C ++ - Bindungen hatten keinen Vorteil, und externe Pakete hatten Probleme mit der realen Welt.

Dies alles sagte, hier ist, was ich denke, die Killer-Funktionen, die ich von einer übergeordneten Oberfläche haben möchte:

  • Es sollte generisch sein. Den Datentyp einer Variablen angeben zu müssen, ist definitiv nicht C ++ - artig. Das führt natürlich auch zu Fehlern. Die MpiMap-Klasse von Elemental wäre bereits ein guter erster Schritt (obwohl ich nicht herausfinden kann, warum die MpiMap::typeVariable keine statische Konstante ist, sodass auf sie zugegriffen werden kann, ohne ein Objekt zu erstellen).

  • Es sollte Möglichkeiten zum Streamen beliebiger Datentypen geben.

  • Operationen, die ein MPI_OpArgument erfordern (z. B. Reduktionen), sollten sich gut in die std::functionSchnittstelle von C ++ integrieren lassen, damit es einfach ist, einen Funktionszeiger (oder ein Lambda!) Zu übergeben, anstatt etwas umständlich registrieren zu müssen.

boost :: mpi erfüllt all diese Anforderungen. Ich denke, wenn es eine reine Header-Bibliothek wäre, wäre sie in der Praxis viel beliebter. Es würde auch helfen, wenn es Post-MPI 1.0-Funktionen unterstützen würde, aber seien wir ehrlich: Dies deckt das meiste ab, was wir die meiste Zeit benötigen.

Wolfgang Bangerth
quelle
Eines der Probleme bei der Serialisierung in Boost :: MPI ist, dass es nicht einseitig funktioniert (auch bekannt als RMA). Denken Sie, dass es möglich sein wird, MPI-Datentypen für die C ++ - Objekte zu erstellen, an denen Benutzer interessiert sind? Natürlich sollte theoretisch alles unterstützt werden, aber ich bevorzuge es, mit einem pragmatischeren Ziel zu beginnen.
Jeff
Ich denke, es ist ein Fehler zu glauben, dass serialisierte Datentypen jemals mit einseitiger Kommunikation funktionieren können. Dies setzt voraus, dass bei der Serialisierung nur Daten in einen String gepackt und auf der anderen Seite erneut entpackt werden. Serialisierungsfunktionen können jedoch viel mehr (z. B. Verfolgen von Zeigern auf andere Objekte, Zählen von serialisierten Bytes usw.) als erwartet, wenn Sie auf dem Zielhost nichts ausführen können. Ich bin der Ansicht, dass C ++ - Serialisierung und einseitige Kommunikation nichts Neues sind. Ich denke, niemand sollte damit rechnen, dass es funktioniert. Es würde also wenig fehlen.
Wolfgang Bangerth
Hallo Wolfgang, du hast Recht, dass MpiMap eleganter wäre, wenn es eine statische konstante Mitgliedsvariable verwenden würde. Ich habe die Implementierung neu organisiert: github.com/poulson/Elemental/commit/…
Jack Poulson
Ja, viel schöner :-)
Wolfgang Bangerth
+1 über nicht viele mpi ruft @WolfgangBangerth auf. Grundsätzlich soll MPI die Berechnungen beschleunigen, Sie möchten MPI-Aufrufe minimieren, weil sie Sie kosten!
Charles
6

Um den Ball ins Rollen zu bringen, sind hier zwei meiner Bedürfnisse:

  • Die Schnittstelle sollte redundante oder unnötige Argumente beseitigen können, z. B. MPI_IN_PLACE.
  • Die Schnittstelle sollte die integrierten Datentypen von Elementals MpiMap automatisch erkennen.
  • Wenn möglich, sollten benutzerdefinierte Datentypen für Klassen erstellt werden.
Jeff
quelle
5

Meine Liste in keiner bestimmten Reihenfolge. Die Schnittstelle sollte:

  • nur Header sein, ohne Abhängigkeiten aber <mpi.h>, und die Standardbibliothek,
  • generisch und erweiterbar sein,
  • werden nicht blockiert nur (wenn Sie blockieren möchten, dann explizit blockieren, nicht standardmäßig),
  • eine fortlaufende Verkettung von nicht blockierenden Vorgängen ermöglichen,
  • Unterstützung der erweiterbaren und effizienten Serialisierung (wie Boost.Fusion, so dass es mit RMA funktioniert),
  • keine Abstraktionsstrafe haben (dh mindestens so schnell sein wie die C-Schnittstelle),
  • sicher sein (der Destruktor einer nicht bereiten Zukunft heißt? -> std :: terminate!),
  • haben einen starken DEBUGModus mit Tonnen von Behauptungen,
  • extrem typsicher (keine ints / void * mehr für alles, zum Teufel möchte ich, dass Tags Typen sind!),
  • es sollte mit Lambdas funktionieren (zB alle reduzieren + Lambda),
  • Ausnahmen konsequent als Fehlermeldungs- und Fehlerbehandlungsmechanismus verwenden (keine Fehlercodes mehr! Keine Funktionsausgabeargumente mehr!),
  • MPI-IO sollte eine nicht blockierende E / A-Schnittstelle im Stil von Boost.AFIO bieten.
  • Befolgen Sie einfach die bewährten Methoden zum Entwerfen von C ++ - Benutzeroberflächen (definieren Sie reguläre Typen, Nicht-Mitglieder-Funktionen, spielen Sie gut mit der Bewegungssemantik, unterstützen Sie Bereichsoperationen usw.).

Extras:

  • Gestatten Sie mir, den Executor der MPI-Umgebung auszuwählen, dh den verwendeten Thread-Pool. Im Moment können Sie Anwendungen mit einer Mischung aus OpenMP, MPI, CUDA und TBB gleichzeitig ausführen, wobei jede Laufzeit davon ausgeht, dass sie die Umgebung besitzt, und das Betriebssystem jedes Mal nach Threads fragt, wenn sie dies wünschen es. Ernst?

  • Verwenden Sie die Namenskonvention STL (und Boost). Warum? Jeder C ++ - Programmierer weiß es.

Ich möchte folgenden Code schreiben:

auto buffer = some_t{no_ranks};
auto future = gather(comm, root(comm), my_offsets, buffer)
              .then([&](){
                /* when the gather is finished, this lambda will 
                   execute at the root node, and perform an expensive operation
                   there asynchronously (compute data required for load 
                   redistribution) whose result is broadcasted to the rest 
                   of the communicator */
                return broadcast(comm, root(comm), buffer);
              }).then([&]() {
                /* when broadcast is finished, this lambda executes 
                   on all processes in the communicator, performing an expensive
                   operation asynchronously (redistribute the load, 
                   maybe using non-blocking point-to-point communication) */
                 return do_something_with(buffer);
              }).then([&](auto result) {
                 /* finally perform a reduction on the result to check
                    everything went fine */
                 return all_reduce(comm, root(comm), result, 
                                  [](auto acc, auto v) { return acc && v; }); 
              }).then([&](auto result) {
                  /* check the result at every process */
                  if (result) { return; /* we are done */ }
                  else {
                    root_only([](){ write_some_error_log(); });
                    throw some_exception;
                  }
              });

/* Here nothing has happened yet! */

/* ... lots and lots of unrelated code that can execute concurrently 
   and overlaps with communication ... */

/* When we now call future.get() we will block 
   on the whole chain (which might have finished by then!).
*/

future.get();

Überlegen Sie, wie Sie all diese Operationen mit MPI_Cs verketten können request. Sie müssten in mehreren (oder jedem einzelnen) Zwischenschritt durch eine ganze Reihe von nicht zusammenhängendem Code testen, um zu sehen, ob Sie Ihre Kette weiterentwickeln können, ohne sie zu blockieren .

gnzlbg
quelle
Dies ist eine intensive Liste von Anforderungen. Einige von ihnen sind für alle praktischen Zwecke unmöglich (z. B. die Unterstützung von Lambda-Ermäßigungen). Insgesamt denke ich jedoch, dass die MPI-Community danach streben sollte, dies zu unterstützen.
Jeff
In Bezug auf Threads und Laufzeitumgebung verwendet MPI intern entweder keine Threads oder Betriebssystem-Threads (POSIX, Windows oder Solaris, je nach Betriebssystem). Ich bin mir nicht ganz sicher, ob ich die Anforderung hier verstehe.
Jeff
Das Problem ist, dass MPI beliebige Threads vom Betriebssystem anfordern kann. Meine Anwendung verfügt über einen Thread-Pool und ich möchte, dass MPI diese Threads aus meinem Thread-Pool anfordert. Dies ist derzeit nicht möglich (und in der Regel kein Problem), es sei denn, Sie haben eine Anwendung, die OpenMP, TBB und MPI verwendet. Jede Anwendung verfügt über eigene Thread-Pools mit jeweils vier Kernen.
gnzlbg
1
MPI kann aber im Allgemeinen keine OS-Threads intern verwenden. In jedem mir vertrauten Fall (MPICH (2), MVAPICH2, OpenMPI, CrayMPI) bewirkt nur eine vom Benutzer bereitgestellte Laufzeitoption, dass dies nicht der Fall ist. Blue Gene / Q MPI ist eine Ausnahme, jedoch in einer Form, die für diese Diskussion nicht relevant ist.
Jeff
Vielen Dank @ Jeff! Könnten Sie näher erläutern, wie MPI mehrere nicht blockierende Anrufe unter Verwendung eines einzelnen Threads verarbeitet? Verwendet es Coroutinen / grüne Fäden / Fasern?
gnzlbg
2

Ich persönlich habe nichts dagegen, lange Funktionen im C-Stil aufzurufen, genau aus dem Grund, den Wolfgang erwähnt hat. Es gibt wirklich wenige Stellen, an denen Sie sie aufrufen müssen, und selbst dann werden sie fast immer von einem Code höherer Ebene umschlossen.

Die einzigen Dinge, die mich bei MPI im C-Stil wirklich stören, sind benutzerdefinierte Datentypen und in geringerem Maße benutzerdefinierte Operationen (weil ich sie seltener verwende). In Bezug auf benutzerdefinierte Datentypen würde ich sagen, dass eine gute C ++ - Schnittstelle eine generische und effiziente Möglichkeit zur Behandlung dieser Aufgabe unterstützen sollte, wahrscheinlich durch Serialisierung. Dies ist natürlich der eingeschlagene Weg, boost.mpider, wenn Sie vorsichtig sind , viel Zeit spart.

In boost.mpiBezug auf zusätzliche Abhängigkeiten (insbesondere, wenn es boost.serializationsich nicht um reine Header- Abhängigkeiten handelt) bin ich kürzlich auf eine reine C ++ - Serialisierungsbibliothek mit dem Namen cereal gestoßen, die vielversprechend erscheint. Voraussetzung ist ein C ++ 11-kompatibler Compiler. Es könnte sich lohnen, sie zu untersuchen und als Grundlage für etwas Ähnliches zu verwenden boost.mpi.

GradGuy
quelle
Beachten Sie, dass ich nicht unbedingt nach etwas suchte, das in den MPI-Standard aufgenommen werden kann. Ich stimme voll und ganz zu, dass MPI fast immer "von höherem Code umschlossen" werden sollte. Was ich mich also frage, wie sieht dieser höherwertige Code aus? Elemental hat eine nette Herangehensweise, aber ist es das Beste für alle Fälle? Wenn man eine C ++ - Schnittstelle zu MPI haben wollte, die eine sehr große Anzahl von Menschen glücklich machen wollte, wie würde sie aussehen?
Jeff
@ Jeff. Für mich: 1. Ich möchte mit Leichtigkeit benutzerdefinierte Datentypen senden können. 2. Ich möchte in der Lage sein, mit Leichtigkeit eine Reduktion mit benutzerdefinierten Operationen durchzuführen. Außerdem denke ich, dass 1 wichtiger / nützlicher ist als 2
GradGuy 13.11.13
Ich verstehe nicht, wie C ++ etwas magisches anstellt (2). Was stellst du dir hier vor?
Jeff
@ Jeff Ich habe mir etwas in etwa überlegt, wie thrustes bei Reduktionen funktioniert
GradGuy
-1

Das Github-Projekt easyLambda bietet mit C ++ 14 eine übergeordnete Schnittstelle zu MPI.

Meines Erachtens hat das Projekt ähnliche Ziele und soll einen Eindruck davon vermitteln, was mit modernem C ++ in diesem Bereich möglich und möglich ist. Anleitung zu anderen Bemühungen sowie zu easyLambda selbst.

Die ersten Benchmarks für Leistung und Codezeilen haben vielversprechende Ergebnisse gezeigt.

Bildbeschreibung hier eingeben

Im Folgenden finden Sie eine kurze Beschreibung der Funktionen und der darin enthaltenen Benutzeroberfläche.

Die Schnittstelle basiert auf Datenflussprogrammierung und Funktionslistenoperationen, die eine inhärente Parallelität bereitstellen. Die Parallelität wird als Eigenschaft einer Aufgabe ausgedrückt. Die Prozesszuordnung und Datenverteilung für die Aufgabe kann mit der Eigenschaft .prll () angefordert werden. Es gibt zahlreiche Beispiele auf der Webseite und im Code-Repository , die die LAMMPS-Molekulardynamik-Nachbearbeitung, die explizite Lösung endlicher Differenzen zur Wärmegleichung, die logistische Regression usw. umfassen. Als Beispiel stirbt das im Artikel HPC diskutierte Problem der Wärmediffusion ... kann in ~ 20 Codezeilen ausgedrückt werden.

Ich hoffe, es ist in Ordnung, Links anzugeben, anstatt hier weitere Details und Beispielcodes hinzuzufügen.

Disclamer: Ich bin der Autor der Bibliothek. Ich glaube, es schadet mir nicht, ein konstruktives Feedback zur aktuellen Oberfläche von easyLambda zu erhalten, das für easyLambda und jedes andere Projekt, das ähnliche Ziele verfolgt, von Vorteil sein könnte.

Utkarsh Bhardwaj
quelle
Die Frage lautet " Wir suchen nach langen Antworten, die eine Erklärung und einen Kontext bieten. Geben Sie nicht nur eine einzeilige Antwort, sondern erläutern Sie, warum Ihre Antwort richtig ist, idealerweise mit Zitaten. Antworten, die keine Erklärungen enthalten, werden möglicherweise entfernt . “. Warum ist Ihre Antwort Ihrer Meinung nach vollständig genug, um dieser Beschreibung zu entsprechen?
nicoguaro
Ich weise auf ein Projekt hin, das eine Schnittstelle bietet, die der des OP ähnelt. Ich kann in der Antwort selbst Einzelheiten zu Motivation und Merkmalen des Projekts angeben und das OP und andere lesen und vorschlagen lassen, was sie darüber denken. Ich habe mich jedoch entschieden, nur Links zu geben. Ich verstehe was sie meinen. Lassen Sie mich einen Text mit Hinweisen auf die Antwort hinzufügen.
Utkarsh Bhardwaj
@UtkarshBhardwaj: Einige Kommentare: (1) Vielen Dank, dass Sie eine Bibliothek geschrieben haben, die C ++ mit MPI verbindet. Es ist ein bedeutendes Unterfangen und sieht so aus, als hätten Sie viel Arbeit investiert. (2) Beim schnellen Durchlesen der Dokumente (wieder ein guter Job) stechen die langen Ketten der verwendeten Methodenbefehle hervor, die stilistisch ... schmerzhaft zu debuggen scheinen, obwohl Sie sie gut formatiert haben. (3) Die Abstraktionen setzen ein funktionales Paradigma voraus, das für kartenreduzierte Aufgaben nützlich erscheint, aber als jemand, der in MPI programmiert, möchte ich meine Algorithmen nicht überarbeiten und mich zu sehr von den mir bekannten Schnittstellen entfernen.
Geoff Oxberry
(4) Ich sehe in diesem Beispiel keine Kommunikatoren. Dies lässt mich vermuten, dass Sie MPI_COMM_WORLD für alles verwenden, und schränkt das Potenzial Ihrer Bibliothek ein. (5) Die Abstraktionen scheinen auf den MPI-Abstraktionen aufzubauen und könnten ein anderes Backend haben. (6) Basierend auf (3) - (5) halte ich diese Bibliothek nicht für eine MPI-Schnittstelle, und ich glaube, dass dieser Kommentar aus diesem Grund nicht zum Thema gehört.
Geoff Oxberry
@Geoff danke für das wertvolle Feedback, sehr zu schätzen. Antworten Sie auf bestimmte Punkte: 2) Die Verkettung wird manchmal als ExpressionBuilder bezeichnet . Es ist eine übliche Art, Komposition in funktionalem Stil auszudrücken. Es ist nicht notwendig, in diesem Stil in ezl zu schreiben. Es ist möglich, zuerst einzelne Einheiten zu schreiben und diese dann zusammenzustellen. Überprüfen Sie das [Beispiel] ( goo.gl/YzaL0k ). 3) Ich weiß, dass es schwierig ist, vom Kontrollfluss zum Datenfluss überzugehen. Jeder hat dort Vor- und Nachteile. Ich bin jedoch der Meinung, dass letzteres genauer untersucht werden muss, um seine wahre Wirksamkeit zu erkennen.
Utkarsh Bhardwaj