Automatische Parallelisierung und automatische Vektorisierung

Automatische Parallelisierung und automatische Vektorisierung sind so konzipiert, dass automatische Leistungszuwachs für Schleifen im Code bereitgestellt wird.

Automatische Parallelisierung

Der Compilerschalter "/Qpar " ermöglicht die automatische Parallelisierung von Schleifen in Ihrem Code. Wenn Sie dieses Flag angeben, ohne den vorhandenen Code zu ändern, wertet der Compiler den Code aus und durchsucht Schleifen, die möglicherweise von einer Parallelisierung profitieren könnten. Da möglicherweise Schleifen gefunden werden, die nicht viel Arbeit ausführen und daher nicht von einer Parallelisierung profitiert würden, und da jede unnötige Parallelisierung die Erstellung eines Threadpools, zusätzliche Synchronisierung oder andere Prozesse auslösen kann, die die Leistung eher verlangsamen anstatt zu verbessern, ist der Compiler bei der Auswahl der Schleifen, die er parallelisiert, konservativ. Betrachten Sie das folgende Beispiel, in dem die Obergrenze der Schleife zur Kompilierzeit nicht bekannt ist:

void loop_test(int u) {
   for (int i=0; i<u; ++i)
      A[i] = B[i] * C[i];
}

Da u es sich um einen kleinen Wert handelt, wird der Compiler diese Schleife nicht automatisch parallelisieren. Sie sollten sie dennoch parallelisierten, da Sie wissen, dass u immer groß ist. Um die automatische Parallelisierung zu aktivieren, geben Sie #pragma loop(hint_parallel(n)) an, wobei n die Anzahl der Threads, die parallelisiert werden sollen. Im folgenden Beispiel versucht der Compiler, die Schleife über 8 Threads zu parallelisieren.

void loop_test(int u) {
#pragma loop(hint_parallel(8))
   for (int i=0; i<u; ++i)
      A[i] = B[i] * C[i];
}

Wie bei allen Pragmadirektiven wird auch die alternative Pragmasyntax__pragma(loop(hint_parallel(n))) unterstützt.

Es gibt einige Schleifen, die der Compiler nicht parallelisieren kann, auch wenn Sie dies wünschen. Hier sehen Sie ein Beispiel:

#pragma loop(hint_parallel(8))
for (int i=0; i<upper_bound(); ++i)
    A[i] = B[i] * C[i];

Die Funktion upper_bound() kann sich jedes Mal ändern, wenn sie aufgerufen wird. Da die obere Grenze nicht bekannt ist, kann der Compiler eine Diagnosemeldung ausgeben, die erklärt, warum sie diese Schleife nicht parallelisieren kann. Im folgenden Beispiel werden eine Schleife, die parallelisiert werden kann, eine Schleife, die nicht parallelisiert werden kann, die Compilersyntax, die an der Eingabeaufforderung verwendet wird, und die Compilerausgabe für jede Befehlszeilenoption dargestellt:

int A[1000];
void test() {
#pragma loop(hint_parallel(0))
    for (int i=0; i<1000; ++i) {
        A[i] = A[i] + 1;
    }

    for (int i=1000; i<2000; ++i) {
        A[i] = A[i] + 1;
    }
}

Eine Kompilierung mit diesem Befehl:

cl d:\myproject\mylooptest.cpp /O2 /Qpar /Qpar-report:1

löst diese Ausgabe aus:

--- Analyzing function: void __cdecl test(void)
d:\myproject\mytest.cpp(4) : loop parallelized

Eine Kompilierung mit diesem Befehl:

cl d:\myproject\mylooptest.cpp /O2 /Qpar /Qpar-report:2

löst diese Ausgabe aus:

--- Analyzing function: void __cdecl test(void)
d:\myproject\mytest.cpp(4) : loop parallelized
d:\myproject\mytest.cpp(4) : loop not parallelized due to reason '1008'

Beachten Sie den Unterschied bei der Ausgabe zwischen den beiden verschiedenen Optionen für den /Qpar-Bericht (Auto-Parallelizer Reporting Level). /Qpar-report:1 gibt Parallelisierungsmeldungen nur für Schleifen aus, die erfolgreich parallelisiert werden. /Qpar-report:2 gibt Parallelisierungsmeldungen für die erfolgreiche und nicht ausgeführte Schleifenparallelisierungen aus.

Weitere Informationen zu Ursachencodes und Nachrichten finden Sie unter Vectorizer- und Parallelizer-Nachrichten.

Automatische Vektorisierung

Die automatische Vektorisierung analysiert Schleifen im Code und verwendet die Vektorregister und Anweisungen auf dem Zielcomputer, um sie, sofern möglich, auszuführen. Damit können Sie die Leistung des Codes verbessern. Der Compiler zielt auf die Anweisungen SSE2, AVX und AVX2 in Intel- oder AMD-Prozessoren oder die NEON-Anweisungen auf ARM-Prozessoren ab, entsprechend dem Schalter /arch .

Die automatische Vektorisierung kann andere Anweisungen generieren, als die vom /arch-Schalter angegebenen. Diese Anweisungen sind durch eine Laufzeitüberprüfung geschützt, um sicherzustellen, dass der Code weiterhin ordnungsgemäß ausgeführt wird. Wenn Sie z. B. /arch:SSE2 kompilieren, können SSE4.2-Anweisungen ausgegeben werden. Eine Laufzeitüberprüfung stellt sicher, dass SSE4.2 auf dem Zielprozessor verfügbar ist, und springt zu einer Nicht-SSE4.2-Version der Schleife, wenn der Prozessor diese Anweisungen nicht unterstützt.

Standardmäßig ist die automatische Vektorisierung aktiviert. Wenn Sie die Leistung des Codes unter Vektorisierung vergleichen möchten, können Sie #pragma Loop(no_vector) verwenden, um die Vektorisierung einer bestimmten Schleife zu deaktivieren.

#pragma loop(no_vector)
for (int i = 0; i < 1000; ++i)
   A[i] = B[i] + C[i];

Wie bei allen Pragmadirektiven wird auch die alternative Pragmasyntax__pragma(loop(no_vector)) unterstützt.

Wie bei der Auto-Parallelizer können Sie die Befehlszeilenoption /Qvec-report (Auto-Vectorizer Reporting Level) angeben, um entweder nur erfolgreich vektorisierte Schleifen/Qvec-report:1 ( oder erfolgreich und erfolglos vektorisierte Schleifen )/Qvec-report:2 zu melden.

Weitere Informationen zu Ursachencodes und Nachrichten finden Sie unter Vectorizer- und Parallelizer-Nachrichten.

Ein Beispiel, das zeigt, wie der Vektorizer in der Praxis funktioniert, finden Sie unter Project Austin Teil 2 von 6: Page Curling

Siehe auch

loop
Parallele Programmierung in systemeigenem Code
/Qpar (Automatische Parallelisierung)
/Qpar-report (Berichtebene der automatischen Parallelisierung)
/Qvec-report (Berichtebene der automatischen Vektorisierung)
Vectorizer- und Parallelizer-Meldungen