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:SSE2
instrukcje 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