[LINUX] Koordination jedes Prozesses im MPI und Pufferung der Standardausgabe

Einführung

Hintergrund

Schauen Sie sich Mr. Robotas diesen Tweet bezüglich MPI an und wie funktioniert jeder Prozess zusammen (hauptsächlich hinsichtlich der Zusammenstellung der Standardausgabe)? Ich war neugierig auf die Pufferkontrolle und den Punkt, also habe ich eine grobe Umfrage durchgeführt.

Hinweis

Dieses Mal habe ich nicht im Detail über die Umgebung geschrieben. Nehmen Sie also bitte den Rücken und verwenden Sie ihn selbst, ohne ihn zu nehmen. Bitte beachten Sie außerdem, dass die verwendeten Begriffe dem Text ganz beigefügt sind.

Außerdem habe ich die Linux-Umgebung ausprobiert und es gibt drei Arten von MPI: Intel, SGI-MPT und OpenMPI. Auch Fortran wird nicht erwähnt. Für C / C ++.

Was ist Standardausgabepufferung?

Was ist Pufferung?

Zunächst zum Thema "Pufferung".

Bei der Ausgabe von Daten aus der Standardausgabe erfolgt dies über stdout für C und std :: cout für C ++. Selbst wenn Sie printf oder std :: ostream :: operator << aufrufen, wird die Ausgabe sofort wiedergegeben. Es wird nicht immer gemacht. Dies liegt daran, dass das Aufrufen der OS-APIs (Schreiben und Senden), die tatsächlich für die Ausgabe in kleinen Teilen zuständig sind, im Allgemeinen hinsichtlich der Leistung nachteilig ist und in der Standardbibliothek zu einem gewissen Grad in einem Puffer gespeichert und zusammengefasst wird. Das liegt daran, dass ich versuche, alles auf einmal zu spucken. Dieses Verhalten wird als ** Pufferung ** bezeichnet.

Pufferung in der Standardbibliothek

Es gibt drei Arten der Pufferung:

Für C stdio erfolgt die Steuerung mit setbuf / setvbuf. Die Standardsteuerung hängt von der Datei / dem Gerät ab, an die / das der Standardausgang angeschlossen, für TTY / PTY leitungsgepuffert und ansonsten vollständig gepuffert ist. Der Spülvorgang wird mit der Spülfunktion ausgeführt.

Bei C ++ iostream wird gesteuert, ob das Flag std :: ios_base :: unitbuf auf std :: cout (std :: cout.setf oder unsetf) gesetzt werden soll. Wenn es gesetzt ist, ist es ungepuffert, wenn es nicht gesetzt ist, ist es vollständig gepuffert und es ist keine Zeile gepuffert. Der Standardwert ist voll gepuffert. Die Spüloperation wird mit dem E / A-Manipulator std :: flush oder std :: endl ausgeführt.

Wie Sie sehen können, sind die Steuerelemente für C und C ++ getrennt. Bei gemischtem C / C ++ - Code ist es jedoch normalerweise ein Problem, wenn die Ausgabereihenfolge gestört ist, sodass beide standardmäßig synchronisiert sind. Ich werde. Mit anderen Worten, selbst wenn es auf der C ++ - Seite vollständig gepuffert ist, wird es auf die C-Seite gezogen, wenn dies auf der C-Seite nicht der Fall ist. Dieses Verhalten kann jedoch von std :: ios_base :: sync_with_stdio (false) überschrieben werden.

Wie MPI funktioniert

Mechanismusübersicht

Grob gesagt ist MPI, dass, wenn Sie die Knoten angeben, die mobilisiert werden können, und die Anzahl der auszuführenden Prozesse, mehrere Knoten dasselbe (oder sogar heterogene) Programm starten und kooperative Berechnungen durchführen können. Es ist eine Bibliothek und eine Gruppe von Werkzeugen.

Daher gibt es verschiedene Hochgeschwindigkeitsnetzwerke wie InfiniBand als Kooperation zwischen den gestarteten Programmen, aber diesmal denke ich über den "Standard-Ausgabefluss" nach. Daher reicht es aus, die drei in der folgenden Abbildung gezeigten Faktoren zu berücksichtigen.

image.png

** Dies ist ein beliebiger Begriff **,

Es wird klassifiziert als. In der obigen Abbildung sind die Vorderseite und die anderen so gezeichnet, als würden sie auf verschiedenen Knoten ausgeführt, sie können jedoch gleich sein.

Unterschiede nach MPI

Intel MPI Erstens ist Intel MPI. Es sieht aus wie in der folgenden Abbildung.

image.png

Die Rollen und Antworten sind wie folgt.

Nach dem Start stellt der Manager eine TCP / IP-Verbindung zur Front her, aggregiert die vom Worker weitergeleitete Ausgabe und sendet sie an die Front.

SGI MPT Als nächstes kommt SGI MPT.

image.png

Die Rollen und Antworten sind wie folgt.

Die Aufteilung der Rollen ähnelt dem zuvor erwähnten Intel MPI. Für den Start des Managers von vorne ist jedoch ein Daemon namens arrayd (auch wenn es sich um einen lokalen Knoten handelt) erforderlich, der mit dem SGI MPT geliefert wird.

OpenMPI Schließlich OpenMPI.

image.png

Die Rollen und Antworten sind wie folgt.

Der große Unterschied zu den beiden oben genannten MPIs besteht darin, dass die Kommunikation zwischen Managern und Mitarbeitern zu PTY wird, wie dies beim Umgang mit lokalen Knoten der Fall ist.

Standardausgangspufferung in MPI

Neuorganisation des Ausgabepfads

Wenn Sie mit einem MPI-Programm ausgeben, werden wir untersuchen, was passiert, wenn es schließlich aggregiert und nach vorne ausgegeben wird.

Wie oben beschrieben, arbeiten drei Arten von Programmen, Front Manager und Worker, bei der Ausführung von MPI zusammen. Und ** Worker Output wird über den Manager nach vorne aggregiert **. Daher ist es auch erforderlich, die Pufferung für jede Route zu organisieren. Das ist,

Es gibt drei Orte.

Unterschiede in der Pufferung für jeden MPI

Intel MPI Im Fall von Intel MPI wird die Ausgabe jedes Arbeiters sogar in der Mitte der Zeile gemischt. Es sieht also so aus, als ob die Pufferung deaktiviert ist.

Dies liegt daran, dass die MPI-Bibliothek während MPI_Init intern setbuf / setvbuf aufruft, um den Worker in einen ungepufferten Zustand zu versetzen **. Mit anderen Worten, der Worker-> Manager-Teil ist zum Puffern deaktiviert, und das Manager-> Front-, Front-> Endausgabeziel wird verschüttet, da es ohne besondere Steuerung ist. Es sieht also so aus, als ob die Pufferung insgesamt deaktiviert ist.

Daher können Sie nach MPI_Init die Pufferung aktivieren, indem Sie setbuf / setvbuf aufrufen und die Pufferung neu konfigurieren. Darüber hinaus scheint das Flag von std :: cout weder in MPI_Init noch in MPI: Init geändert zu werden. Wenn es sich also um eine reine C ++ - Anwendung handelt, wird die Pufferung durch Deaktivieren der C, C ++ - Synchronisation aktiviert. ..

-ordered-output Use this option to avoid intermingling of data output from the MPI processes. This option affects both the standard output and the standard error streams. NOTE When using this option, end the last output line of each process with the end-of-line '\n' character. Otherwise the application may stop responding.

SGI MPT Im Fall von SGI MPT ist die Ausgabe zeilenweise organisiert, was einem zeilengepufferten Verhalten entspricht.

Der Mechanismus dahinter ist etwas kompliziert.

Mit anderen Worten, es wird durch die Bemühungen der Rezeption gepuffert. Umgekehrt kann es sein, dass Sie nicht möchten, dass die MPI-Anwendung (Worker) den Puffer ohne Erlaubnis steuert.

OpenMPI OpenMPI verhält sich wie SGI MPT wie zeilengepuffert.

Dieser Mechanismus ist sehr einfach, da die Ausgabe zwischen Worker und Manager PTY ist und das Standardsteuerelement dafür standardmäßig zeilengepuffert ist. Andere Manager-> Front, Front-> Endgültiges Ausgabeziel scheint nichts Besonderes zu steuern. Mit anderen Worten, OpenMPI selbst befasst sich nicht speziell mit der Puffersteuerung und überlässt sie der Standardbibliothek.

Zusammenfassung

Ich habe also den Unterschied in der Kontrolle in jedem MPI gesehen.

Obwohl es bei jedem MPI Unterschiede gibt, ist es meiner Meinung nach besser, setbuf / setvbuf unmittelbar nach MPI_Init aufzurufen, wenn Sie sicherstellen möchten, dass die Pufferung effektiv ist.

Referenz

Im Folgenden werden die Quelle und das Betriebsprotokoll als Referenz für das Verhalten mit Intel MPI als Referenz aufgeführt.

Betriebsprotokoll


$ cat /etc/centos-release
CentOS Linux release 7.4.1708 (Core) 
$ mpirun --version
Intel(R) MPI Library for Linux* OS, Version 2018 Update 1 Build 20171011 (id: 17941)
Copyright (C) 2003-2017, Intel Corporation. All rights reserved.
$ icpc --version
icpc (ICC) 18.0.1 20171018
Copyright (C) 1985-2017 Intel Corporation.  All rights reserved.

$ mpiicpc -std=gnu++11 -o test test.cpp
$ mpirun -np 2 ./test
abababababababababbababababababababababa

abababababababababababababababababababab
a
bbabaababababababababababababababababab
a
bababbaababababbaababababababababababab

babaabababababababababababababababababab

$ mpirun -np 2 ./test --nosync
aaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbb
$ mpirun -np 2 ./test --setvbuf
aaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbb
$ mpirun -np 2 ./test --nosync --unitbuf
abababbaabababababababababababababababab
a
bababababababababababababababababababab

babababababababababababababababababababa

ababababbabababababababababababababababa

abababababababababababababababababababab

$ mpiicpc -std=gnu++11 -o test2 test2.cpp
$ mpirun -np 2 ./test2
abababababbaababababababbaababababababab

babaabababbaabababababababababababababab

babababababababababababababababababababa

ababababbaabababababbabaabababababababab
a
bababababababababababababababababababab

$ mpirun -np 2 ./test2 -f
aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbb
bbbbbbbbbbbbbbbbbbbb
$ mpirun -np 2 ./test2 -l
aaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbb
aaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbb
$ 

test.cpp


#include <mpi.h>
#include <iostream>
#include <thread>
#include <string>
#include <cstdio>

static char stdoutbuf[8192];

int main(int argc, char **argv) {
  MPI::Init(argc,argv);
  MPI::COMM_WORLD.Set_errhandler(MPI::ERRORS_THROW_EXCEPTIONS);
  int rank = MPI::COMM_WORLD.Get_rank();

  for ( int i=1; i<argc; i++ ) {
    std::string opt(argv[i]);
    if ( opt == "--nosync" ) {
      // detach C++-iostream from C-stdio
      std::ios_base::sync_with_stdio(false);
    }
    else if ( opt == "--setvbuf" ) {
      // re-setvbuf for C-stdio
      std::setvbuf(stdout,stdoutbuf,_IOFBF,sizeof(stdoutbuf));
    }
    else if ( opt == "--unitbuf" ) {
      // disable buffering on C++-iostream
      std::cout.setf(std::ios_base::unitbuf);
    }
    else if ( rank == 0 ) {
      std::cerr << "invalid option: " << opt << std::endl;
      std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
  }

  char c='a'+rank;
  for ( int i=0; i<5; i++ ) {
    MPI::COMM_WORLD.Barrier();
    for ( int j=0; j<20; j++ ) {
      std::cout << c;
      std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
    std::cout << std::endl;
  }
  MPI::Finalize();
}

test2.cpp


#include <mpi.h>
#include <iostream>
#include <thread>
#include <string>
#include <cstdio>

static char stdoutbuf[8192];

int main(int argc, char **argv) {
  MPI::Init(argc,argv);
  MPI::COMM_WORLD.Set_errhandler(MPI::ERRORS_THROW_EXCEPTIONS);
  int rank = MPI::COMM_WORLD.Get_rank();

  if ( argc > 1 ) {
    std::string opt(argv[1]);
    if ( opt == "-f" ) {
      // full buffered
      std::setvbuf(stdout,stdoutbuf,_IOFBF,sizeof(stdoutbuf));
    }
    else if ( opt == "-l" ) {
      // line buffered
      std::setvbuf(stdout,stdoutbuf,_IOLBF,sizeof(stdoutbuf));
    }
  }

  char c='a'+rank;
  for ( int i=0; i<5; i++ ) {
    MPI::COMM_WORLD.Barrier();
    for ( int j=0; j<20; j++ ) {
      std::cout << c;
      std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
    std::cout << '\n';
  }
  std::cout << std::flush;
  MPI::Finalize();
}

Recommended Posts

Koordination jedes Prozesses im MPI und Pufferung der Standardausgabe
Berechnung der Standardabweichung und des Korrelationskoeffizienten in Python
Empfängt und gibt die Standardausgabe von Python 2- und Python 3> C-Implementierungen aus
Überprüfen Sie die Verarbeitungszeit und die Anzahl der Aufrufe für jeden Prozess mit Python (cProfile).
Erläuterung der CSV und Implementierungsbeispiel in jeder Programmiersprache
Lesen Sie das Bild des Puzzlespiels und geben Sie die Reihenfolge der einzelnen Blöcke aus
Lesen Sie die Standardausgabe eines Unterprozesses zeilenweise in Python
Eingabe / Ausgabe von Werten aus der Standardeingabe in der Wettbewerbsprogrammierung usw.
Machen Sie die Standardausgabe in Python nicht blockierend
Exportieren und Ausgeben von Dateien in Python
Versuchen Sie auch bei der Konvertierung von CSV in Leerzeichenbegrenzer ernsthaft, Eingabe / Ausgabe und Regeln zu trennen
Implementieren Sie einen Teil des Prozesses in C ++
Hinweise zur Standardeingabe / -ausgabe von Go
UnicodeEncodeError hat Probleme mit der Standardausgabe von Python3
Screenshots des Webfischens mit Selen und Chrom.
Trennung von Design und Daten in matplotlib
Unterschied in der Ausgabe der Fensterfunktion mit gerader Länge
Zusammenfassung der Module und Klassen in Python-TensorFlow2-
Status jedes Python-Verarbeitungssystems im Jahr 2020
Projekt Euler # 1 "Vielfaches von 3 und 5" in Python
So zählen Sie die Anzahl der Elemente in Django und geben sie in die Vorlage aus