Поделиться через


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

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

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

Параметр компилятора /Qpar активирует автоматическую параллелизацию циклов в вашем коде. Если установить этот флажок без изменения существующего кода, компилятор проанализирует код, чтобы выявить циклы, выполнение которых можно ускорить благодаря параллелизации. Компилятор может обнаруживать циклы, которые выполняют мало работы и не принесут заметного выигрыша от параллелизации. Каждая ненужная параллелизация может вызвать создание пула потоков, дополнительную синхронизацию или другую обработку, что может замедлить производительность, вместо ее повышения. Поэтому компилятор осторожно подходит к выбору циклов для параллелизации. Например, рассмотрим случай, в котором верхняя граница цикла неизвестна во время компиляции.

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

Так как u может быть небольшим значением, компилятор не будет автоматически параллелизировать этот цикл. Тем не менее, вы все же можете захотеть его параллелизовать, потому что знаете, что значение u всегда будет большим. Чтобы включить автоматическую параллелизацию, укажите #pragma loop(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 (уровень отчетности Auto-Parallelizer). /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)).

Как и при автоматическом параллелизаторе, можно указать параметр командной строки /Qvec-report (уровень отчетов автовекторизатора), чтобы сообщить только о векторизованных циклах (/Qvec-report:1или как успешно, так и неудачно векторизованных циклах)./Qvec-report:2

Дополнительные сведения о кодах причин и сообщениях см. в разделе Vectorizer и Parallelizer Messages.

Пример, демонстрирующий, как работает векторизатор на практике, можно найти в Project Austin: часть 2 из 6: Заворачивание страницы.

См. также

цикл
Параллельное программирование в машинном коде
/Qpar (автоматический параллелизатор)
/Qpar/report (уровень отчетности автоматического параллелизатора)
/Qvec-report (уровень отчетов автоматического векторизатора)
Сообщения векторизатора и параллелизатора