Unterstützung für Multiprozess- und Multithread-Beschleunigung
Diese Seite dokumentiert, in welchem Umfang qiskit-addon-sqd Multithread- und Multiprozess-Beschleunigung unterstützt und auf welche Annahmen sich ein Entwickler im Hochleistungsrechnen (HPC) verlassen kann, wenn er dieses Paket in eine beschleunigte Workload integriert.
Die Single-Thread-Annahme
Sofern nicht anders angegeben, sind die APIs in diesem Paket dafür gedacht, aus einem einzelnen Thread aufgerufen zu werden. Die von diesem Paket bereitgestellten High-Level-APIs sind nicht garantiert reentrant, und Endanwender sollten sie nicht aus einem anderen Thread als dem Hauptthread aufrufen.
Kollektive Multiprozess-Ausführung
Dieses Paket unterstützt kollektive Multiprozess-Beschleunigung im SPMD-Stil (Single Program, Multiple Data), bei dem das gesamte Programm als mehrere isolierte Prozesse gestartet wird, die mit expliziter globaler Synchronisation und Kommunikation untereinander laufen (zum Beispiel mpirun -n 128 python my_program.py). Da sich der SPMD-Stil sauber mit Job-Schedulern und anderer paralleler Software kombinieren lässt und auf große Prozesszahlen skaliert, ist er das empfohlene Modell für Hochleistungs- und Großanwendungen, und er ist das Thema dieser Seite.
Obwohl dieses Paket so konzipiert ist, dass es verschiedene Backends für kollektive Ausführung aufnehmen kann, zielt die aktuelle Implementierung auf MPI, die Standard-Message-Passing-API für HPC-Systeme. Unabhängig von der Implementierung nimmt dieses Paket an, dass jeder Prozess von einem einzelnen Thread gesteuert wird. Bei MPI entspricht das MPI_THREAD_FUNNELED oder niedriger.
Wenn eine Funktion kollektive Ausführung unterstützt, muss ihre Dokumentation das angeben. Erwähnt die Dokumentation keine kollektive Ausführung, hat die Funktion keine kollektive Semantik und muss allein vom Steuerprozess aufgerufen werden.
Die spezifische Semantik für Rückgabewerte und Synchronisation einer kollektiven Funktion ist bei der Funktion selbst dokumentiert. Im Allgemeinen sollte diese Dokumentation klarstellen:
-
Ob die Funktion von jedem Prozess unabhängig auf dessen lokalen Daten oder kollektiv von allen Prozessen aufgerufen werden soll.
-
Wie ihre Argumente über die Prozesse hinweg übereinstimmen müssen.
-
Wie ihr Rückgabewert geliefert wird – ob das Ergebnis nur auf dem Steuerprozess existiert, ob alle Prozesse denselben Wert erhalten oder ob jeder Prozess ein Handle auf einen lokalen Teil einer verteilten Datenstruktur erhält.
Derzeit unterstützt nur eine Funktion den kollektiven Aufruf von allen Prozessen aus: diagonalize_fermionic_hamiltonian(). Wird sie kollektiv aufgerufen, ist der Eigensolver-Schritt derjenige, an dem alle Prozesse teilnehmen und Arbeit beitragen, sodass eine Eigensolver-Implementierung jeden Prozess nutzen kann. Die übrigen Teile der Schleife zur Konfigurationswiederherstellung haben keine verteilte Implementierung und laufen nur auf dem Steuerprozess (Rang 0).
Jeder Unterraum einer Iteration wird dem Eigensolver in einem einzigen Aufruf übergeben, sodass eine Implementierung die Prozesse frei in Gruppen aufteilen und mehrere Unterräume parallel diagonalisieren kann. Wie ein Solver die ihm zur Verfügung stehenden Prozesse nutzt, ist eine Eigenschaft des jeweiligen Eigensolvers.
Einige bestehende Eigensolver-Implementierungen verlangen, dass das aufrufende Programm außerhalb einer MPI/SPMD-Umgebung läuft, weil sie ihre eigenen parallelen Prozesse intern starten und verwalten, zum Beispiel indem sie im Namen des Nutzers mpirun aufrufen. Dieser Modus ist für interaktives und Notebook-basiertes Arbeiten praktisch und wird weiterhin unterstützt; seine Anforderungen an das aufrufende Programm sind in der API-Referenz der Funktion diagonalize_fermionic_hamiltonian() beschrieben.
Fehlerbehandlung in einem Multiprozess-Kontext
Eine Funktion, die kollektiv von allen Prozessen aufgerufen wird, darf keine Exception auslösen und nicht nur einen einzelnen Prozess abbrechen, weil das die übrigen Prozesse in einem Deadlock oder in einem inkonsistenten Zustand zurückließe. Stattdessen folgt die Fehlerbehandlung der Fail-Stop-Semantik für den gesamten Ausführungskontext: Bei einem Fehler muss die Implementierung darauf vorbereitet sein, den gesamten Ausführungskontext kollektiv abzubrechen (zum Beispiel mit MPI_Abort).
Die API kann keine koordinierte Fehlermeldung und keine kollektive Zustellung von Exceptions über Prozesse hinweg garantieren, da MPI-Implementierungen solche Garantien nicht bieten. Eine Implementierung kann zusätzlich versuchen, einen Fehler für alle beteiligten Prozesse sichtbar zu machen – zum Beispiel indem jeder Prozess eine Exception auslöst –, doch das kann nur nach bestem Bemühen geboten werden und darf nicht als Grundlage für Korrektheit oder Wiederherstellung dienen.
Dies sind Eigenschaften, die eine kollektive Implementierung haben darf, keine Garantien für eine bestimmte Implementierung. In diesem Paket ist der einzige kollektive Schritt der Eigensolver (siehe vorherigen Abschnitt); seine Standardimplementierung ist nicht verteilt, sodass diese Überlegungen für einen benutzerdefinierten, kollektiven Eigensolver gelten, den der Nutzer bereitstellt.