Автоматическая параллелизация и автоматическая векторизация
Автоматический параллелизатор и автоматический векторизатор обеспечивают автоматическое повышение производительности циклов в коде.
Автоматический параллелизатор
Параметр компилятора /Qpar включает автоматическую параллелизацию циклов в коде. Если установить этот флажок без изменения существующего кода, компилятор проанализирует код, чтобы выявить циклы, выполнение которых можно ускорить благодаря параллелизации. Компилятор может обнаружить циклы, которые не представляют большую нагрузку и по которым не получается выигрыш от параллелизации. Для организации параллелизации может потребоваться создание пула потоков, дополнительная синхронизация или другая обработка, которая вместо повышения производительности может привести к ее снижению. Поэтому компилятор строго оценивает возможность выигрыша при выборе циклов для параллелизации. Например, рассмотрим случай, в котором верхняя граница цикла неизвестна во время компиляции.
void loop_test(int u) {
for (int i=0; i<u; ++i)
A[i] = B[i] * C[i];
}
Так как u
может быть небольшим значением, компилятор не будет автоматически параллелизировать этот цикл. Допустим, вам известно, что значение u
всегда будет большим и параллелизацию этого цикла желательно выполнить. Чтобы включить автоматическую параллелизацию, укажите цикл #pragma(hint_parallel(n)), где n
количество потоков для параллелизации между собой. В приведенном ниже примере компилятор попытается выполнить параллелизацию цикла по 8 потокам.
void loop_test(int u) {
#pragma loop(hint_parallel(8))
for (int i=0; i<u; ++i)
A[i] = B[i] * C[i];
}
Как и во всех директивах pragma, также поддерживается альтернативный синтаксис __pragma(loop(hint_parallel(n)))
pragma.
Существует несколько циклов, которые компилятор не может параллелизировать, даже если требуется. Приведем пример:
#pragma loop(hint_parallel(8))
for (int i=0; i<upper_bound(); ++i)
A[i] = B[i] * C[i];
Функция upper_bound()
может меняться при каждом вызове. Так как верхняя граница не может быть известна, компилятор может выпустить диагностическое сообщение, объясняющее, почему он не может параллелизировать этот цикл. В приведенном ниже примере демонстрируется цикл, в котором может выполняться параллелизация, цикл, в котором не может выполняться параллелизация, синтаксис компилятора для командной строки и выходные данные компилятора для каждого параметра командной строки:
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;
}
}
При компиляции с помощью команды:
cl d:\myproject\mylooptest.cpp /O2 /Qpar /Qpar-report:1
получаются следующие выходные данные:
--- Analyzing function: void __cdecl test(void)
d:\myproject\mytest.cpp(4) : loop parallelized
При компиляции с помощью команды:
cl d:\myproject\mylooptest.cpp /O2 /Qpar /Qpar-report:2
получаются следующие выходные данные:
--- 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'
Обратите внимание на разницу в выходных данных между двумя разными параметрами /Qpar-report (уровень отчетов автопарализатора). /Qpar-report:1
выводит сообщения параллелизатора только для тех циклов, параллелизация которых успешно выполнена. /Qpar-report:2
выводит сообщения параллелизатора как для выполненных, так и для невыполненных параллелизаций цикла.
Дополнительные сведения о кодах причин и сообщениях см. в разделе Vectorizer и Parallelizer Messages.
Автоматический векторизатор
Автоматический векторизатор анализирует циклы в коде и использует векторные регистры и инструкции на целевом компьютере для их выполнения, если это возможно. Это может повысить производительность кода. Компилятор предназначен для инструкций SSE2, AVX и AVX2 в процессорах Intel или AMD, а также инструкции NEON для процессоров ARM в соответствии с параметром /arch .
Автоматический векторизатор может создавать инструкции, отличные от указанных в переключателе /arch
. Эти инструкции защищены проверкой среды выполнения, гарантирующей, что код все еще работает правильно. Например, при компиляции /arch:SSE2
могут быть выданы инструкции SSE4.2. При проверке среды выполнения проверяется, что инструкция SSE4.2 доступна на целевом процессоре, и осуществляется переход к версии цикла, отличной от SSE4.2, если процессор не поддерживает эти инструкции.
По умолчанию автоматический векторизатор включен. Если вы хотите сравнить производительность кода под векторизацией, можно использовать цикл #pragma (no_vector) для отключения векторизации любого заданного цикла.
#pragma loop(no_vector)
for (int i = 0; i < 1000; ++i)
A[i] = B[i] + C[i];
Как и во всех директивах pragma, также поддерживается альтернативный синтаксис __pragma(loop(no_vector))
pragma.
Как и при автоматическом параллелизаторе, можно указать параметр командной строки /Qvec-report (уровень отчетов автовекторизатора), чтобы сообщить только о векторизованных циклах (/Qvec-report:1
или как успешно, так и неудачно векторизованных циклах)./Qvec-report:2
Дополнительные сведения о кодах причин и сообщениях см. в разделе Vectorizer и Parallelizer Messages.
Пример, показывающий, как работает векторизатор на практике, см. в разделе Project Остин часть 2 из 6: Page Curling
См. также
loop
Параллельное программирование в машинном коде
/Qpar (автоматический параллелизатор)
/Qpar/report (уровень отчетности автоматического параллелизатора)
/Qvec/report (уровень отчетности автоматического векторизатора)
Сообщения векторизатора и параллелизатора