SIMD Extension to C++ OpenMP in Visual Studio

WILD

Administrator
Staff member
ADMIN
SELLER
SUPREME
MEMBER
Joined
Jan 21, 2025
Messages
219
Reaction score
636
Deposit
0$
В эпоху повсеместного применения ИИ возникает потребность в компиляторах, ускоряющих ресурсоемкий код машинного обучения для существующего оборудования. Такой код обычно выполняет математические вычисления, такие как преобразование и манипулирование матрицами, и, как правило, имеет форму циклов. Расширение SIMD в OpenMP предоставляет пользователям простой способ ускорить циклы, явно используя векторный блок современных процессоров. Мы с гордостью начинаем предлагать векторизацию C/C++ OpenMP SIMD в Visual Studio 2019.



Интерфейс прикладного программирования OpenMP для C/C++ был первоначально разработан в 1990-х годах для повышения производительности приложений за счет эффективного параллельного выполнения кода на нескольких процессорах. За прошедшие годы стандарт OpenMP был расширен для поддержки дополнительных концепций, таких как параллелизация на основе задач, SIMD-векторизация и разгрузка процессора. С 2005 года Visual Studio поддерживает стандарт OpenMP 2.0, ориентированный на многопоточную параллелизацию. Поскольку мир вступает в эру искусственного интеллекта, мы видим растущую возможность улучшить качество кода за счет расширения поддержки стандарта OpenMP в Visual Studio. Мы продолжаем наш путь в Visual Studio 2019, добавляя поддержку OpenMP SIMD.


74-scsh4qdjqqdw-qj3ynz5ircc.jpeg



Директива OpenMP SIMD, впервые представленная в стандарте OpenMP 4.0, в основном предназначена для векторизации циклов. Согласно нашим исследованиям, на данный момент это наиболее широко используемая функция OpenMP в машинном обучении. Аннотируя цикл директивой OpenMP SIMD, компилятор может игнорировать векторные зависимости и максимально векторизовать цикл. Компилятор учитывает намерение пользователя выполнять несколько итераций цикла одновременно.


#pragma omp simd
for (i = 0; i < count; i++)
{
a = b + 1;
}




Как вы, возможно, знаете, C++ в Visual Studio уже предоставляет аналогичные директивы циклов, не поддерживающие OpenMP, такие как #pragma vector и #pragma ivdep . Однако компилятор может сделать больше с SIMD-функциями OpenMP. Например:



  1. Компилятору всегда разрешается игнорировать любые существующие векторные зависимости.
  2. Параметр /fp:fast включен внутри цикла.
  3. Циклы с вызовами функций векторизуемы.
  4. Внешние петли векторизуемы.
  5. Вложенные циклы можно объединить в один цикл и векторизовать.
  6. Гибридное ускорение достижимо с помощью директивы #pragma omp для simd , позволяющей использовать крупнозернистую многопоточность и мелкозернистую векторизацию.


Кроме того, директива OpenMP SIMD может содержать следующие пункты для дальнейшего улучшения векторизации:



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


Новый переключатель -openmp:experimental



Программа с аннотациями OpenMP-SIMD может быть скомпилирована с новым ключом CL -openmp:experimental. Этот новый ключ включает дополнительные функции OpenMP, недоступные при использовании -openmp . Хотя название этого ключа — «experimental», сам ключ и предоставляемая им функциональность полностью поддерживаются и готовы к использованию в производственной среде. Название отражает тот факт, что он не включает какое-либо полное подмножество или версию стандарта OpenMP. В будущих версиях компилятора этот ключ может использоваться для включения дополнительных функций OpenMP, и могут быть добавлены новые ключи, связанные с OpenMP. Ключ -openmp:experimental включает в себя ключ -openmp , что означает его совместимость со всеми функциями OpenMP 2.0. Обратите внимание, что директива SIMD и её пункты не могут быть скомпилированы с ключом -openmp .



Для циклов, которые не векторизованы, компилятор выдаст сообщение, подобное приведенному ниже. Например:



cl -O2 -openmp:experimental mycode.cpp



mycode.cpp(84): info C5002: Цикл Omp simd не векторизован по причине '1200'



mycode.cpp(90): info C5002: Цикл Omp simd не векторизован по причине '1200'



Для векторизованных циклов компилятор не выдает никаких сообщений, если не указан параметр логирования векторизации:



cl -O2 -openmp:experimental -Qvec-report:2 mycode.cpp



mycode.cpp(84): info C5002: Цикл Omp simd не векторизован по причине '1200'



mycode.cpp(90): info C5002: Цикл Omp simd не векторизован по причине '1200'



mycode.cpp(96): info C5001: Векторизованный цикл simd Omp



В качестве первого шага по поддержке OpenMP SIMD мы, по сути, связали директиву SIMD с векторизатором бэкэнда в рамках нового параметра. Мы сосредоточились на векторизации самых внутренних циклов, улучшив векторизатор и анализ псевдонимов. На момент написания этой статьи ни одна из директив SIMD не работает в Visual Studio 2019. Они будут проанализированы, но проигнорированы компилятором с предупреждением для пользователя. Например, компилятор выдаст ошибку.



Предупреждение C4849: В директиве 'simd' игнорируется пункт 'simdlen' OpenMP.



для следующего кода:


#pragma omp simd simdlen(8)
for (i = 1; i < count; i++)
{
a = a[i-1] + 1;
b = *c + 1;
бар(и);
}




Подробнее о семантике директивы OpenMP SIMD​



Директива OpenMP SIMD предоставляет пользователям возможность указать компилятору, какой параллелизм следует использовать. Компилятор может игнорировать кажущуюся законность такой векторизации, принимая обещание пользователя о корректности. Ответственность за неожиданное поведение при векторизации лежит на пользователе. Аннотируя цикл директивой OpenMP SIMD, пользователи предполагают одновременное выполнение нескольких итераций цикла. Это дает компилятору большую свободу в генерации машинного кода, использующего ресурсы SIMD или векторизации целевого процессора. Хотя компилятор не несет ответственности за проверку корректности и эффективности такого заданного пользователем параллелизма, он все же должен обеспечить последовательное выполнение одной итерации цикла.



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


#pragma omp simd
for (i = 1; i < count; i++)
{
a = a[i-1] + 1;
b = *c + 1;
бар(и);
}




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


#pragma omp simd
for (i = 1; i < count; i+=4)
{
a[i:i+3] = a[i-1:i+2] + 1;
b[i:i+3] = *c + 1;
бар(и);
bar(i+1);
bar(i+2);
bar(i+3);
}




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


c = b;
t = *c;
#pragma omp simd
for (i = 1; i < count; i+=4)
{
a[i:i+3] = a[i-1:i+2] + 1;
bar(i); // Недопустимо переупорядочивать, если bar зависит от b
b[i:i+3] = t + 1; // недопустимо выносить *c за пределы цикла
bar(i+1);
bar(i+2);
bar(i+3);
}
 
Top Bottom