Автоматическая параллелизация и автоматическая векторизация

Автоматический параллелизатор и автоматический векторизатор обеспечивают автоматическое повышение производительности циклов в коде.

Автоматический параллелизатор

Параметр компилятора /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 (уровень отчетности автоматического векторизатора)
Сообщения векторизатора и параллелизатора