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


Расширение SIMD

Visual C++ в настоящее время поддерживает стандарт OpenMP 2.0, однако Visual Studio 2019 также предлагает функциональные возможности SIMD.

Примечание.

Чтобы использовать SIMD, скомпилируйте с помощью коммутатора -openmp:experimental , который включает дополнительные функции OpenMP, недоступные при использовании коммутатора -openmp .

Вложенные -openmp:experimental элементы коммутатора -openmp, что означает, что все функции OpenMP 2.0 включены в его использование.

Дополнительные сведения см. в разделе "Расширение SIMD для C++ OpenMP" в Visual Studio.

OpenMP SIMD в Visual C++

OpenMP SIMD, представленный в стандарте OpenMP 4.0, предназначен для создания векторных циклов. Используя simd директиву перед циклом, компилятор может игнорировать векторные зависимости, сделать цикл понятным для вектора как можно больше, и уважать намерение пользователей выполнять несколько итераций цикла одновременно.

    #pragma omp simd
    for (i = 0; i < count; i++)
    {
        a[i] = a[i-1] + 1;
        b[i] = *c + 1;
        bar(i);
    }

Visual C++ предоставляет аналогичные прагмы цикла, отличные от OpenMP, как #pragma vector и #pragma ivdepпри использовании OpenMP SIMD, компилятор может сделать больше, например:

  • Всегда разрешено игнорировать векторные зависимости.
  • /fp:fast включен в цикле.
  • Внешние циклы и циклы с вызовами функций являются векторными.
  • Вложенные циклы можно объединить в один цикл и сделать векторным.
  • Гибридное ускорение с #pragma omp for simd включением грубого многопотокового и тонкого векторов.

Для циклов, понятных для векторов, компилятор остается безмолвным, если вы не используете переключатель журнала поддержки векторов:

    cl -O2 -openmp:experimental -Qvec-report:2 mycode.cpp
    mycode.cpp(84) : info C5002: Omp simd loop not vectorized due to reason '1200'
    mycode.cpp(90) : info C5002: Omp simd loop not vectorized due to reason '1200'
    mycode.cpp(96) : info C5001: Omp simd loop vectorized

Для невекторных циклов компилятор выдает каждое сообщение:

    cl -O2 -openmp:experimental mycode.cpp
    mycode.cpp(84) : info C5002: Omp simd loop not vectorized due to reason '1200'
    mycode.cpp(90) : info C5002: Omp simd loop not vectorized due to reason '1200'

Предложения

Директива OpenMP SIMD также может принимать следующие предложения для повышения поддержки векторов:

Директива Описание
simdlen(length) Укажите количество векторных полос.
safelen(length) Укажите расстояние зависимости вектора.
linear(list[ : linear-step]( Линейное сопоставление от индукционной переменной цикла к подписке массива.
aligned(list[ : alignment]) Выравнивание данных.
private(list) Укажите приватизацию данных.
lastprivate(list) Укажите приватизацию данных с окончательным значением из последней итерации.
reduction(reduction-identifier:list) Укажите настраиваемые операции сокращения.
collapse(n) Объединение гнезда цикла.

Примечание.

Неэффективные предложения SIMD анализируются и игнорируются компилятором с предупреждением.

Например, при использовании следующего кода возникает предупреждение:

   #pragma omp simd simdlen(8)
   for (i = 0; i < count; i++)
   {
       a[i] = a[i-1] + 1;
       b[i] = *c + 1;
       bar(i);
   }
   warning C4849: OpenMP 'simdlen' clause ignored in 'simd' directive

Пример

Директива OpenMP SIMD предоставляет пользователям способ диктовать компилятору векторные циклы. Заметив цикл с помощью директивы OpenMP SIMD, пользователи намерены одновременно выполнять несколько итераций цикла.

Например, следующий цикл аннотирован с помощью директивы OpenMP SIMD. Нет идеального параллелизма между итерациями цикла, так как существует обратная зависимость от a[i] до [i-1], но из-за директивы SIMD компилятор по-прежнему разрешен упаковывать последовательные итерации первой инструкции в одну векторную инструкцию и выполнять их параллельно.

    #pragma omp simd
    for (i = 0; i < count; i++)
    {
        a[i] = a[i-1] + 1;
        b[i] = *c + 1;
        bar(i);
    }

Поэтому следующая преобразованная форма вектора цикла является законной , так как компилятор сохраняет последовательное поведение каждой исходной итерации цикла. Иными словами, a[i] выполняется после a[-1]b[i]a[i], после и вызов, который будет происходить bar последним.

    for (i = 0; i < count; i+=4)
    {
        a[i:i+3] = a[i-1:i+2] + 1;
        b[i:i+3] = *c + 1;
        bar(i);
        bar(i+1);
        bar(i+2);
        bar(i+3);
    }

Это не является законным для перемещения ссылки на *c память из цикла, если он может быть псевдонимом или a[i]b[i]. Это также не является законным для переупорядочения инструкций внутри одной исходной итерации, если она нарушает последовательную зависимость. Например, следующий преобразованный цикл не является законным:

    c = b;
    t = *c;
    for (i = 0; i < count; i+=4)
    {
        a[i:i+3] = a[i-1:i+2] + 1;
        bar(i);            // illegal to reorder if bar[i] depends on b[i]
        b[i:i+3] = t + 1;  // illegal to move *c out of the loop
        bar(i+1);
        bar(i+2);
        bar(i+3);
    }

См. также

/openmp (включение поддержки OpenMP 2.0)