Message Passing Interface

Einführung

Das „Message Passing Interface“ (MPI) ist eine API zur Parallelisierung von Programmen auf mehreren Knoten und existiert seit 1994 (1). MPI kann auch zur Parallelisierung auf SMP-Maschinen eingesetzt werden und gilt dort als sehr effizient (nahezu 100 % Skalierung bei parallelisierbarem Code im Vergleich zu den üblicherweise mit Threads erreichten ca. 80 % aufgrund suboptimaler Speicherverwaltung auf NUMA-Maschinen). Vor MPI verwendete nahezu jeder Supercomputerhersteller seine eigene Programmiersprache; MPI vereinfachte die Softwareportierung erheblich.

Es gibt viele MPI-Implementierungen, wie zum Beispiel Open MPI (der Standard-MPI-Compiler in Fedora und der in RHEL verwendete MPI-Compiler), MPICH (in Fedora und RHEL) und MVAPICH1 und MVAPICH2 (in RHEL, aber noch nicht in Fedora).

Da manche MPI-Bibliotheken auf bestimmter Hardware besser funktionieren als auf anderer und manche Software am besten mit bestimmten MPI-Bibliotheken harmoniert, muss die Auswahl der verwendeten Bibliothek benutzerbezogen und sitzungsspezifisch erfolgen. Anwender im Bereich High Performance Computing benötigen möglicherweise effizientere Compiler als den in Fedora standardmäßig verwendeten (gcc). Daher muss es möglich sein, mehrere Versionen des MPI-Compilers gleichzeitig mit jeweils einem anderen Compiler kompilieren zu können. Dies muss beim Schreiben von Spec-Dateien berücksichtigt werden.

Paketierung von MPI-Compilern

Die Dateien der MPI-Compiler MÜSSEN in den folgenden Verzeichnissen installiert werden:

Dateitype Platzierung

Binärdateien

%{_libdir}/%{name}/bin

Bibliotheken

%{_libdir}/%{name}/lib

[[PackagingDrafts/Fortran

Fortran-Module]]

%{_fmoddir}/%{name}

[[Packaging/Python

Python-Module]]

%{python2_sitearch}/%{name} %{python3_sitearch}/%{name}

Konfigurationsdateien

%{_sysconfdir}/%{name}-%{_arch}

Da sich Include-Dateien und Handbuchseiten bei verschiedenen MPI-Implementierungen zwangsläufig überschneiden, MÜSSEN sie ebenfalls außerhalb der normalen Verzeichnisse abgelegt werden. Es ist möglich, dass einige Handbuchseiten oder Include-Dateien (entweder die des MPI-Compilers selbst oder die einer im Compilerverzeichnis installierten MPI-Software) architekturspezifisch sind (z.B. unterscheidet sich eine Definition auf einer 32-Bit-Architektur von der auf einer 64-Bit-Architektur). Folgende Verzeichnisse MÜSSEN verwendet werden:

Dateityp Ort

Handbuchseiten

%{_mandir}/%{name}-%{_arch}

Include -Dateien

%{_includedir}/%{name}-%{_arch}

Architekturunabhängige Teile (mit Ausnahme von Headern, die in -devel gehören) MÜSSEN in einem -common-Teilpaket platziert werden, das als BuildArch: noarch markiert ist.

Die Laufzeitumgebung der MPI-Compiler (mpirun, die Bibliotheken, die Handbücher usw.) MUSS in %{name} paketiert werden, und die Entwicklungs-Header und -Bibliotheken in %{name}-devel.

Da der Compiler außerhalb von PATH installiert ist, müssen die entsprechenden Variablen geladen werden, bevor der Compiler verwendet oder MPI-Programme ausgeführt werden können. Dies geschieht mithilfe von Umgebungsmodulen.

Die Moduldatei MUSS unter %{_sysconfdir}/modulefiles/mpi installiert werden. Dies ermöglicht es einem Benutzer, der nur eine MPI-Implementierung installiert hat, das Modul wie folgt zu laden:

module load mpi

Die Moduldatei MUSS folgende Zeile enthalten:

conflict mpi

… um das gleichzeitige Laden mehrerer MPI-Module zu verhindern.

Die Moduldatei MUSS $MPI_BIN dem PATH-Pfad des Benutzers und $MPI_LIB dem LD_LIBRARY_PATH-Pfad voranstellen. Die Moduldatei MUSS außerdem einige Hilfsvariablen setzen (hauptsächlich zur Verwendung in Spec-Dateien):

Variable Wert Erklärung

MPI_BIN

%{_libdir}/%{name}/bin

Gegen den MPI-Stack kompilierte Binärdateien

MPI_SYSCONFIG

%{_sysconfdir}/%{name}-%{_arch}

Stack-spezifische MPI-Konfigurationsdateien

MPI_FORTRAN_MOD_DIR

%{_fmoddir}/%{name}

Stack-spezifisches MPI-Fortran-Modulverzeichnis

MPI_INCLUDE

%{_includedir}/%{name}-%{_arch}

Stack-spezifische MPI-Header

MPI_LIB

%{_libdir}/%{name}/lib

Gegen den MPI-Stack kompilierte Bibliotheken

MPI_MAN

%{_mandir}/%{name}-%{_arch}

Stack-spezifische MPI-Handbuchseiten

MPI_PYTHON2_SITEARCH

%{python2_sitearch}/%{name}

Stack-spezifische Python2-MPI-Module

MPI_PYTHON3_SITEARCH

%{python3_sitearch}/%{name}

Stack-spezifische Python3-MPI-Module

MPI_COMPILER

%{name}-%{_arch}

Name des Compiler-Pakets, z.B. für die Nutzung in Spec-Dateien

MPI_SUFFIX

_%{name}

Für Programme, die gegen den MPI-Stack kompiliert werden, zu verwendendes Suffix

Da diese Verzeichnisse von Software, die den MPI-Stack verwendet, genutzt werden können, MUSS das MPI-Laufzeitpaket Eigentümer aller dieser Verzeichnisse sein.

MUSS: Standardmäßig werden KEINE Dateien in /etc/ld.so.conf.d abgelegt. Wenn der Paketierer Unterstützung für Alternativen bereitstellen möchte, MUSS diese in einem Teilpaket zusammen mit der Datei ld.so.conf.d abgelegt werden, damit die Unterstützung für alternative Pakete nicht zusätzlich installiert werden muss, falls sie nicht gewünscht ist.

WICHTIG: Wenn der Betreuer möchte, dass das Umgebungsmodul automatisch über ein Skript in /etc/profile.d oder über einen anderen Mechanismus geladen wird, MUSS dies in einem Teilpaket erfolgen.

MUSS: Das MPI-Compilerpaket MUSS ein RPM-Makro bereitstellen, das das Laden und Entladen der Unterstützung in Spec-Dateien vereinfacht, z. B. durch Platzieren des Folgenden in /etc/rpm/macros.openmpi

%_openmpi_load \
 . /etc/profile.d/modules.sh; \
 module load mpi/openmpi-%{_arch}; \
 export CFLAGS="$CFLAGS %{optflags}";
%_openmpi_unload \
 . /etc/profile.d/modules.sh; \
 module unload mpi/openmpi-%{_arch};

Das Laden und Entladen des Compilers in Spec-Dateien ist so einfach wie %{_openmpi_load} und %{_openmpi_unload}.

Die automatische Festlegung des Modulladepfads in Python-Interpretern erfolgt über eine .pth-Datei, die in einem der Verzeichnisse abgelegt wird, in denen normalerweise nach Modulen gesucht wird (%{python2_sitearch}, %{python3_sitearch}). Diese .pth-Dateien hängen das mit der Umgebungsvariablen $MPI_PYTHON2_SITEARCH oder $MPI_PYTHON3_SITEARCH (abhängig von der Interpreter-Version) angegebene Verzeichnis an sys.path an. Sind diese Variablen nicht gesetzt, geschieht nichts. Moduldateien dürfen PYTHONPATH nicht direkt setzen, da dies nicht gleichzeitig für beide Python-Versionen möglich ist.

Wenn das Umgebungsmodul Compiler-Flags wie CFLAGS setzt (und damit die in %configure exportierten Flags überschreibt), MUSS das RPM-Makro dafür sorgen, dass sie wieder die Fedora-Optimierungsflags %{optflags} verwenden (wie im obigen Beispiel, in dem das Modul openmpi-%{_arch} CFLAGS setzt).

Paketierung von MPI-Software

Software, die MPI unterstützt, MUSS auch im seriellen Modus [d.h. ohne MPI] paketiert werden, wenn dies vom Upstream-Projekt unterstützt wird. (zum Beispiel: foo).

Wenn möglich, MUSS der Paketierer Versionen für jeden MPI-Compiler in Fedora paketieren (z.B. wenn etwas nur mit mpich und mvapich2 gebaut werden kann, dann müssen keine mvapich1- und openmpi-Pakete erstellt werden).

MPI-implementierungsspezifische Dateien MÜSSEN in den Verzeichnissen installiert werden, die vom verwendeten MPI-Compiler verwendet werden ($MPI_BIN, $MPI_LIB usw.).

An die Binärdateien MÜSSEN Suffixe mit $MPI_SUFFIX angehängt werden (z. B. _openmpi für Open MPI, _mpich für MPICH und _mvapich2 für MVAPICH2). Dies hat zwei Gründe: Die serielle Version des Programms kann weiterhin ausgeführt werden, wenn ein MPI-Modul geladen ist, und der Benutzer weiß stets, welche Version er verwendet. Die Verwendung von Shell-Skripten wird dadurch nicht beeinträchtigt:

# Welche MPI-Implementierung verwenden wir?

#module load mpi/mvapich2-i386
#module load mpi/openmpi-i386
module load mpi/mpich-i386

# Präprozessor  ausführen
foo -preprocess < foo.in
# Berechnung ausführen
mpirun -np 4 foo${MPI_SUFFIX}
# Nachbearbeitung ausführen
mpirun -np 4 bar${MPI_SUFFIX} -process
# Ergebnisse zusammenfassen
bar -collect

Die MPI-fähigen Komponenten MÜSSEN in einem Teilpaket mit dem Suffix des verwendeten MPI-Compilers abgelegt werden (z. B. foo-openmpi für Open MPI [den traditionellen MPI-Compiler in Fedora] oder foo-mpich für MPICH). Um die Verzeichnisberechtigungen zu verwalten und die korrekte MPI-Laufzeitumgebung zu gewährleisten, MÜSSEN die MPI-Teilpakete das entsprechende Laufzeitpaket des MPI-Compilers einbinden.

Jeder MPI-Bauvorgang von gemeinsam genutzten Bibliotheken SOLLTE ein separates Teilpaket namens -libs für die Bibliotheken erzeugen (z.B. foo-mpich-libs). Wie bei MPI-Compilern DARF keine Bibliothekskonfiguration (in /etc/ld.so.conf.d) vorgenommen werden.

Falls die Header unabhängig von Kompilierungsmethode und Architektur identisch sind (z. B. 32-Bit-Seriell, 64-Bit Open MPI, MPICH), MÜSSEN sie in ein separates Teilpaket -headers (z.B. 'foo-headers') ausgelagert werden. Fortran-Module sind architekturspezifisch und werden daher im (MPI-implementierungsspezifischen) Paket -devel abgelegt (foo-devel für die serielle Version und foo-openmpi-devel für die Open-MPI-Version).

Jeder MPI-Bauvorgang muss ein separates -devel-Teilpaket (z. B. foo-mpich-devel) erzeugen, das die Entwicklungsbibliotheken und Requires: %{name}-headers enthält, sofern ein solches Paket existiert. Ziel ist es, die Installation und Entwicklung mit z. B. 'foo-mpich-devel' zu ermöglichen, ohne beispielsweise openmpi oder die serielle Version des Pakets installieren zu müssen.

Dateien müssen so weit wie möglich zwischen Paketen gemeinsam genutzt werden. Compilerunabhängige Teile, wie z. B. Datendateien in %{_datadir}/%{name} und Man-Dateien, MÜSSEN in ein -common-Teilpaket ausgelagertt werden, das von allen Binärpaketen (dem seriellen Paket und allen MPI-Paketen) benötigt wird.

Spec-Beispieldatei

# Define a macro for calling ../configure instead of ./configure
%global dconfigure %(printf %%s '%configure' | sed 's!\./configure!../configure!g')

Name: foo
Requires: %{name}-common = %{version}-%{release}

%package common

%package openmpi
BuildRequires: openmpi-devel
# Require explicitly for dir ownership and to guarantee the pickup of the right runtime
Requires: openmpi
Requires: %{name}-common = %{version}-%{release}

%package mpich
BuildRequires: mpich-devel
# Require explicitly for dir ownership and to guarantee the pickup of the right runtime
Requires: mpich
Requires: %{name}-common = %{version}-%{release}

%build
# Have to do off-root builds to be able to build many versions at once

# To avoid replicated code define a build macro
%define dobuild() \
mkdir $MPI_COMPILER; \
cd $MPI_COMPILER; \
%dconfigure --program-suffix=$MPI_SUFFIX ;\
make %{?_smp_mflags} ; \
cd ..

# Build serial version, dummy arguments
MPI_COMPILER=serial MPI_SUFFIX= %dobuild

# Build parallel versions: set compiler variables to MPI wrappers
export CC=mpicc
export CXX=mpicxx
export FC=mpif90
export F77=mpif77

# Build OpenMPI version
%{_openmpi_load}
%dobuild
%{_openmpi_unload}

# Build mpich version
%{_mpich_load}
%dobuild
%{_mpich_unload}

%install
# Install serial version
make -C serial install DESTDIR=%{buildroot} INSTALL="install -p" CPPROG="cp -p"

# Install OpenMPI version
%{_openmpi_load}
make -C $MPI_COMPILER install DESTDIR=%{buildroot} INSTALL="install -p" CPPROG="cp -p"
%{_openmpi_unload}

# Install MPICH version
%{_mpich_load}
make -C $MPI_COMPILER install DESTDIR=%{buildroot} INSTALL="install -p" CPPROG="cp -p"
%{_mpich_unload}


%files # All the serial (normal) binaries

%files common # All files shared between the serial and different MPI versions

%files openmpi # All openmpi linked files

%files mpich # All mpich linked files