Automatyczna paralelizacja i wektoryzacja

Auto-Parallelizer i Auto-Vectorizer zostały zaprojektowane w celu zapewnienia automatycznych zysków wydajności dla pętli w kodzie.

Auto-Parallelizer

Przełącznik kompilatora /Qpar umożliwia automatyczną równoległość pętli w kodzie. Po określeniu tej flagi bez zmiany istniejącego kodu kompilator ocenia kod w celu znalezienia pętli, które mogą korzystać z równoległego przetwarzania. Ponieważ może znajdować pętle, które nie wykonują dużej ilości pracy i dlatego nie skorzystają z równoległości, a ponieważ każda niepotrzebna równoległość może wywołać tarcie puli wątków, dodatkową synchronizację lub inne przetwarzanie, które zwykle spowalnia wydajność, a nie poprawia, kompilator jest konserwatywny podczas wybierania pętli, które są równoległe. Rozważmy na przykład następujący przykład, w którym górna granica pętli nie jest znana w czasie kompilacji:

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

Ponieważ u może to być mała wartość, kompilator nie będzie automatycznie równoległy tej pętli. Można jednak nadal chcieć go zrównać, ponieważ wiesz, że u zawsze będzie duży. Aby włączyć automatyczną równoległość, określ pętlę #pragma(hint_parallel(n)), gdzie n jest liczbą wątków do równoległości. W poniższym przykładzie kompilator podejmie próbę zrównania pętli między 8 wątkami.

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

Podobnie jak we wszystkich dyrektywach pragma, obsługiwana jest również alternatywna składnia __pragma(loop(hint_parallel(n))) pragma.

Istnieją pętle, których kompilator nie może zrównać, nawet jeśli chcesz. Oto przykład:

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

Funkcja upper_bound() może się zmieniać za każdym razem, gdy jest wywoływana. Ponieważ nie można znać górnej granicy, kompilator może emitować komunikat diagnostyczny wyjaśniający, dlaczego nie może zrównoleglizować tej pętli. W poniższym przykładzie pokazano pętlę, która może być równoległa, pętla, której nie można zrównać, składnia kompilatora do użycia w wierszu polecenia oraz dane wyjściowe kompilatora dla każdej opcji wiersza polecenia:

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;
    }
}

Kompilowanie przy użyciu tego polecenia:

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

zwraca następujące dane wyjściowe:

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

Kompilowanie przy użyciu tego polecenia:

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

zwraca następujące dane wyjściowe:

--- 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'

Zwróć uwagę na różnicę w danych wyjściowych między dwoma różnymi opcjami /Qpar-report (Poziom raportowania automatycznego równoległego modułu). /Qpar-report:1 generuje komunikaty równoległe tylko dla pętli, które zostały pomyślnie zrównane. /Qpar-report:2 generuje komunikaty równoległe zarówno dla pomyślnych, jak i nieudanych równoległych pętli.

Aby uzyskać więcej informacji na temat kodów przyczyn i komunikatów, zobacz Vectorizer and Parallelizer Messages (Komunikaty wektorowe i równoległe).

Autowektory

Automatycznie wektoryzator analizuje pętle w kodzie i używa rejestrów wektorów i instrukcji na komputerze docelowym do ich wykonania, jeśli to możliwe. Może to poprawić wydajność kodu. Kompilator jest przeznaczony dla instrukcji SSE2, AVX i AVX2 w procesorach Intel lub AMD lub neon instrukcji na procesorach ARM, zgodnie z przełącznikiem /arch .

Autowektory mogą generować inne instrukcje niż określone przez /arch przełącznik. Te instrukcje są chronione przez sprawdzanie środowiska uruchomieniowego, aby upewnić się, że kod nadal działa poprawnie. Na przykład podczas kompilowania /arch:SSE2instrukcje SSE4.2 mogą być emitowane. Sprawdzanie środowiska uruchomieniowego sprawdza, czy procesor SSE4.2 jest dostępny na procesorze docelowym i przechodzi do wersji innej niż SSE4.2 pętli, jeśli procesor nie obsługuje tych instrukcji.

Domyślnie jest włączony autowektorizer. Jeśli chcesz porównać wydajność kodu w wektoryzacji, możesz użyć pętli #pragma (no_vector), aby wyłączyć wektoryzację dowolnej pętli.

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

Podobnie jak we wszystkich dyrektywach pragma, obsługiwana jest również alternatywna składnia __pragma(loop(no_vector)) pragma.

Podobnie jak w przypadku auto-parallelizera, można określić /Qvec-report (Auto-Vectorizer Reporting Level) opcję wiersza polecenia, aby zgłosić tylko wektoryzowane pętle —/Qvec-report:1 albo zarówno pomyślnie, jak i bezskutecznie wektoryzowane pętle —/Qvec-report:2).

Aby uzyskać więcej informacji na temat kodów przyczyn i komunikatów, zobacz Vectorizer and Parallelizer Messages (Komunikaty wektorowe i równoległe).

Przykład pokazujący, jak działa wektoryzator w praktyce, zobacz Project Austin Part 2 of 6: Page Curling

Zobacz też

pętla
Programowanie równoległe w kodzie natywnym
/Qpar (Automatyczny paralelizator)
/Qpar-raport (Poziom raportowania automatycznej paralelizacji)
/Qvec-report (Poziom raportowania automatycznej wektoryzacji)
Komunikaty wektoryzatora i paralelizatora