SIMD-Erweiterung auf C ++ OpenMP in Visual Studio

Im Zeitalter allgegenwärtiger KI-Anwendungen steigt die Nachfrage des Compilers, der rechenintensiven Code für maschinelles Lernen für vorhandene Hardware beschleunigt. Ein solcher Code führt normalerweise mathematische Berechnungen wie Matrixtransformation und -manipulation durch und liegt normalerweise in Form von Schleifen vor. Die SIMD-Erweiterung von OpenMP bietet Benutzern eine mühelose Möglichkeit, Schleifen zu beschleunigen, indem die Vektoreinheit moderner Prozessoren explizit genutzt wird. Wir sind stolz darauf, C / C ++ OpenMP SIMD-Vektorisierung in Visual Studio 2019 anbieten zu können.


Die OpenMP C / C ++ - Anwendungsprogrammschnittstelle wurde ursprünglich entwickelt, um die Anwendungsleistung zu verbessern, indem Code in den 90er Jahren auf mehreren Prozessoren effektiv parallel ausgeführt werden konnte. Im Laufe der Jahre wurde der OpenMP-Standard erweitert, um zusätzliche Konzepte wie aufgabenbasierte Parallelisierung, SIMD-Vektorisierung und Prozessor-Offloading zu unterstützen. Seit 2005 unterstützt Visual Studio den OpenMP 2.0-Standard, der sich auf Multithread-Parallelisierung konzentriert. Während sich die Welt in eine KI-Ära bewegt, sehen wir eine wachsende Chance, die Codequalität zu verbessern, indem die Unterstützung des OpenMP-Standards in Visual Studio erweitert wird. Wir setzen unsere Reise in Visual Studio 2019 fort, indem wir Unterstützung für OpenMP SIMD hinzufügen.




OpenMP SIMD, das erstmals im OpenMP 4.0-Standard eingeführt wurde, zielt hauptsächlich auf die Vektorisierung von Schleifen ab. Laut unserer Forschung ist es die bislang am häufigsten verwendete OpenMP-Funktion beim maschinellen Lernen. Durch Annotieren einer Schleife mit einer OpenMP SIMD-Direktive kann der Compiler Vektorabhängigkeiten ignorieren und die Schleife so weit wie möglich vektorisieren. Der Compiler respektiert die Absicht der Benutzer, mehrere Schleifeniterationen gleichzeitig ausführen zu lassen.


#pragma omp simd for (i = 0; i < count; i++) { a[i] = b[i] + 1; } 

Wie Sie vielleicht wissen, bietet C ++ in Visual Studio bereits ähnliche Nicht-OpenMP-Schleifen-Pragmas wie #pragma vector und #pragma ivdep . Der Compiler kann jedoch mehr mit OpenMP SIMD tun. Zum Beispiel:


  1. Der Compiler darf vorhandene Vektorabhängigkeiten immer ignorieren.
  2. / fp: schnell ist innerhalb der Schleife aktiviert.
  3. Schleifen mit Funktionsaufrufen sind vektorisierbar.
  4. Äußere Schleifen sind vektorisierbar.
  5. Verschachtelte Schleifen können zu einer Schleife zusammengeführt und vektorisiert werden.
  6. Hybridbeschleunigung ist mit #pragma omp for simd erreichbar , um grobkörniges Multithreading und feinkörnige Vektorisierung zu ermöglichen.

Darüber hinaus kann die OpenMP SIMD-Direktive die folgenden Klauseln enthalten, um die Vektorisierung weiter zu verbessern:


  • simdlen ( Länge ): Geben Sie die Anzahl der Vektorspuren an
  • safelen ( Länge ): Geben Sie den Abstand der Vektorabhängigkeit an
  • linear ( Liste [ : linearer Schritt] ): Die lineare Zuordnung von der Schleifeninduktionsvariablen zum Array-Abonnement
  • ausgerichtet ( Liste [ : Ausrichtung] ): die Ausrichtung von Daten
  • privat ( Liste ): Datenprivatisierung angeben
  • lastprivate ( Liste ): Geben Sie die Datenprivatisierung mit dem Endwert der letzten Iteration an
  • Reduktion ( Reduktionskennung : Liste ): Geben Sie benutzerdefinierte Reduktionsoperationen an
  • Zusammenbruch ( n ): Verschmelzungsschleifennest

New -openmp: experimenteller Schalter


Ein mit OpenMP-SIMD annotiertes Programm kann mit einem neuen CL-Schalter -openmp: experimentell kompiliert werden. Dieser neue Switch aktiviert zusätzliche OpenMP-Funktionen, die unter -openmp nicht verfügbar sind . Während der Name dieses Switches "experimentell" ist, werden der Switch selbst und die von ihm aktivierten Funktionen vollständig unterstützt und sind produktionsbereit. Der Name gibt an, dass keine vollständige Teilmenge oder Version eines OpenMP-Standards aktiviert wird. Zukünftige Iterationen des Compilers können diesen Schalter verwenden, um zusätzliche OpenMP-Funktionen zu aktivieren, und neue OpenMP-bezogene Schalter können hinzugefügt werden. Der experimentelle Schalter -openmp: fasst den Schalter -openmp zusammen , was bedeutet, dass er mit allen OpenMP 2.0-Funktionen kompatibel ist. Beachten Sie, dass die SIMD-Direktive und ihre Klauseln nicht mit dem Schalter -openmp kompiliert werden können .


Für Schleifen, die nicht vektorisiert sind, gibt der Compiler für jede von ihnen eine Nachricht wie unten aus. Zum Beispiel


cl -O2 -openmp: experimentelle mycode.cpp


mycode.cpp (84): info C5002: Omp-simd-Schleife aus Grund '1200' nicht vektorisiert


mycode.cpp (90): info C5002: Omp-simd-Schleife aus Grund '1200' nicht vektorisiert


Für Schleifen, die vektorisiert sind, bleibt der Compiler stumm, es sei denn, ein Vektorisierungsprotokollierungsschalter ist vorhanden:


cl -O2 -openmp: experimentell -Qvec-Bericht: 2 mycode.cpp


mycode.cpp (84): info C5002: Omp-simd-Schleife aus Grund '1200' nicht vektorisiert


mycode.cpp (90): info C5002: Omp-simd-Schleife aus Grund '1200' nicht vektorisiert


mycode.cpp (96): info C5001: Omp simd loop vektorisiert


Als ersten Schritt zur Unterstützung von OpenMP SIMD haben wir das SIMD-Pragma grundsätzlich mit dem Backend-Vektorisierer unter dem neuen Switch verbunden. Wir haben uns auf die Vektorisierung der innersten Schleifen konzentriert, indem wir die Vektorisierer- und Alias-Analyse verbessert haben. Zum Zeitpunkt dieses Schreibens ist keine der SIMD-Klauseln in Visual Studio 2019 wirksam. Sie werden analysiert, aber vom Compiler mit einer Warnung ignoriert, die zur Kenntnisnahme des Benutzers ausgegeben wird. Zum Beispiel wird der Compiler ausgeben


Warnung C4849: OpenMP-Klausel 'simdlen' wird in der Anweisung 'simd' ignoriert


für den folgenden Code:


 #pragma omp simd simdlen(8) for (i = 1; i < count; i++) { a[i] = a[i-1] + 1; b[i] = *c + 1; bar(i); } 

Weitere Informationen zur Semantik der OpenMP SIMD-Direktive


Die OpenMP SIMD-Direktive bietet Benutzern die Möglichkeit, den Compiler zum Vektorisieren einer Schleife zu diktieren. Der Compiler darf die offensichtliche Rechtmäßigkeit einer solchen Vektorisierung ignorieren, indem er das Versprechen der Benutzer zur Richtigkeit akzeptiert. Es liegt in der Verantwortung der Benutzer, wenn bei der Vektorisierung unerwartetes Verhalten auftritt. Durch Annotieren einer Schleife mit der OpenMP SIMD-Direktive möchten Benutzer, dass mehrere Schleifeniterationen gleichzeitig ausgeführt werden. Dies gibt dem Compiler viel Freiheit beim Generieren von Maschinencode, der SIMD- oder Vektorressourcen auf dem Zielprozessor nutzt. Der Compiler ist zwar nicht dafür verantwortlich, die Richtigkeit und den Gewinn einer solchen benutzerdefinierten Parallelität zu untersuchen, muss jedoch das sequentielle Verhalten einer einzelnen Schleifeniteration sicherstellen.


Beispielsweise wird die folgende Schleife mit der OpenMP SIMD-Direktive kommentiert. Es gibt keine perfekte Parallelität zwischen Schleifeniterationen, da eine Rückwärtsabhängigkeit von a [i] zu a [i-1] besteht. Aufgrund der SIMD-Direktive kann der Compiler jedoch weiterhin aufeinanderfolgende Iterationen der ersten Anweisung in einen Vektorbefehl packen und parallel ausführen.


 #pragma omp simd for (i = 1; i < count; i++) { a[i] = a[i-1] + 1; b[i] = *c + 1; bar(i); } 

Daher ist die folgende transformierte Vektorform der Schleife zulässig, da der Compiler das sequentielle Verhalten jeder ursprünglichen Schleifeniteration beibehält. Mit anderen Worten, a [i] wird nach a [-1] ausgeführt, b [i] nach a [i] und der Aufruf von bar erfolgt schließlich.


 #pragma omp simd for (i = 1; i < count; i+=4) { a[i:i+3] = a[i-1:i+2] + 1; b[i:i+3] = *c + 1; bar(i); bar(i+1); bar(i+2); bar(i+3); } 

Es ist illegal, die Speicherreferenz * c aus der Schleife zu verschieben, wenn sie mit a [i] oder b [i] alias ist. Es ist auch illegal, die Anweisungen innerhalb einer ursprünglichen Iteration neu zu ordnen, wenn dadurch die sequentielle Abhängigkeit aufgehoben wird. Beispielsweise ist die folgende transformierte Schleife nicht zulässig.


 c = b; t = *c; #pragma omp simd for (i = 1; i < count; i+=4) { a[i:i+3] = a[i-1:i+2] + 1; bar(i); // illegal to reorder if bar[i] depends on b[i] b[i:i+3] = t + 1; // illegal to move *c out of the loop bar(i+1); bar(i+2); bar(i+3); } 

Zukunftspläne und Feedback


Wir empfehlen Ihnen, diese neue Funktion auszuprobieren. Wie immer freuen wir uns über Ihr Feedback. Wenn Sie eine OpenMP SIMD-Schleife sehen, von der Sie erwarten, dass sie vektorisiert wird, die jedoch nicht oder der generierte Code nicht optimal ist, teilen Sie uns dies bitte mit. Sie erreichen uns über die folgenden Kommentare, per E-Mail ( visualcpp@microsoft.com ), Twitter (@visualc) oder über die Entwickler-Community .


In Zukunft würden wir gerne hören, dass OpenMP-Funktionen in Visual Studio fehlen. Da OpenMP seit dem 2.0-Standard mehrere wichtige Entwicklungen erfahren hat, verfügt OpenMP jetzt über enorme Funktionen, die Ihnen die Erstellung von Hochleistungsprogrammen erleichtern. Beispielsweise ist ab OpenMP 3.0 eine aufgabenbasierte Parallelitätsprogrammierung verfügbar. Heterogenes Computing (CPU + Beschleuniger) wird in OpenMP 4.0 unterstützt. Erweiterte Unterstützung für SIMD-Vektorisierung und DOACROSS-Schleifenparallelisierung sind jetzt auch im neuesten OpenMP-Standard verfügbar. Die vollständigen Standardrevisionen und Funktionssätze finden Sie auf der offiziellen OpenMP-Website: https://www.openmp.org . Wir bitten Sie aufrichtig um Ihre Meinung zu den spezifischen OpenMP-Funktionen, die Sie sehen möchten. Wir sind auch daran interessiert zu erfahren, wie Sie OpenMP verwenden, um Ihren Code zu beschleunigen. Ihr Feedback ist wichtig, damit es die Richtung der OpenMP-Unterstützung in Visual Studio bestimmt.


Avatar
Hongtao Yu

Source: https://habr.com/ru/post/de446688/


All Articles