Я уже использую компилятор PGI, поддерживающий OpenAcc, для запуска кода на GPU уже около 3 лет, но я пока не могу понять разницу между терминами «ядра» и «параллельные». Я прочитал в Руководстве по началу работы с OpenAcc:
Параллельная конструкция
Определяет область программы, которая должна быть скомпилирована для параллельного выполнения на устройстве-ускорителе.
Ядра Конструкт
Определяет область программы, которая должна быть скомпилирована в последовательность ядер для выполнения на устройстве-ускорителе.
Я не понимаю, в чем разница между терминами «параллельное выполнение на устройстве-ускорителе» и «скомпилировано в последовательность ядер для выполнения на устройстве-ускорителе». Если устройство-ускоритель является графическим процессором, то весь код компилируется в ядра CUDA определенного размера (я пытаюсь иметь в виду сетку и блоки CUDA), и эти ядра CUDA выполняются на графическом процессоре в потоках CUDA, не так ли? Что такое «последовательность» ядер? Директива "parallel" составляет 1 ядро, а "ядра" могут составлять последовательность ядер из одного и того же фрагмента кода?
Также я везде использую только директиву "параллельный" цикл. Например, чтобы распараллелить цикл for для выполнения на GPU, я пишу
#pragma acc parallel loop gang vector copy(...) present(...)
for(int i=0; i<N; ++i)
{
...
}
Это правильно? Когда следует использовать «ядра»? Или это синоним слова «параллель» и теперь устарел и не используется?
2 ответа
Лучший способ понять разницу - это то, что с «параллелью» вы, программист, определяете, какие циклы распараллеливать и как. По сути, вы говорите компилятору распараллеливать определенные циклы. С помощью «ядер» вы определяете область кода, которая может быть распараллелена, но задача компилятора - определить, какие циклы распараллеливать и как.
Для «параллельного» весь код в регионе выгружается как одно ядро CUDA. Если у вас есть несколько внешних циклов в «параллельной» области, они все равно будут выгружены в одном ядре CUDA. Поскольку компилятор может обнаружить распараллеливание с «ядрами», несколько циклов в этой области могут быть разделены на последовательность отдельных запусков ядра CUDA.
Полную информацию можно найти по адресу: https://www.pgroup.com/lit /articles/insider/v4n2a1.htm
Обратите внимание, что для доступа к статье требуется наличие учетной записи веб-пользователя PGI.
Уже опубликован мой ответ здесь, но здесь это снова.
Параллельная конструкция
Определяет область программы, которая должна быть скомпилирована для параллельного выполнения на устройстве-ускорителе.
Директива о параллельном цикле - это утверждение программиста о том, что параллельное выполнение затронутого цикла безопасно и желательно. Это полагается на то, что программист правильно идентифицирует параллелизм в коде и удаляет в коде все, что может быть небезопасно для распараллеливания. Если программист неверно утверждает, что цикл может быть распараллелен, то результирующее приложение может дать неверные результаты.
Параллельная конструкция позволяет более детально контролировать, как компилятор будет пытаться структурировать работу на ускорителе. Таким образом, он не сильно зависит от способности компилятора автоматически распараллеливать код.
Когда параллельный цикл используется в двух последующих циклах, которые обращаются к одним и тем же данным, компилятор может копировать или не копировать данные назад и вперед между хостом и устройством между двумя циклами.
Более опытные параллельные программисты, которые, возможно, уже определили параллельные циклы в своем коде, вероятно, найдут подход с параллельными циклами более желательным.
Например, см.
#pragma acc parallel
{
#pragma acc loop
for (i=0; i<n; i++)
a[i] = 3.0f*(float)(i+1);
#pragma acc loop
for (i=0; i<n; i++)
b[i] = 2.0f*a[i];
}
генерировать одно ядро
Barrier Между двумя циклами нет барьера: второй цикл может начаться до окончания первого цикла. (Это отличается от OpenMP).
Конструкция ядра
Определяет область программы, которая должна быть скомпилирована в последовательность ядер для выполнения на устройстве-ускорителе.
Важно отметить, что компилятор ядра будет анализировать код и распараллеливать только тогда, когда он уверен, что это безопасно. В некоторых случаях компилятор может не иметь достаточно информации во время компиляции, чтобы определить, безопасен ли цикл распараллеливания, в этом случае он не будет распараллеливать цикл, даже если программист может ясно видеть, что цикл безопасно параллелен.
Конструкция ядра дает компилятору максимум возможностей для распараллеливания и оптимизации кода так, как он считает нужным для целевого ускорителя, но также в наибольшей степени зависит от способности компилятора автоматически распараллеливать код.
Еще одно заметное преимущество, которое обеспечивает конструкция ядра, состоит в том, что если несколько циклов обращаются к одним и тем же данным, они будут скопированы в ускоритель только один раз, что может привести к меньшему перемещению данных.
Программисты с меньшим опытом параллельного программирования или чей код содержит большое количество циклов, которые необходимо проанализировать, могут найти подход к ядрам намного проще, так как это увеличивает нагрузку на компилятор.
Например, см.
#pragma acc kernels
{
for (i=0; i<n; i++)
a[i] = 3.0f*(float)(i+1);
for (i=0; i<n; i++)
b[i] = 2.0f*a[i];
}
генерировать два ядра
Between Между двумя циклами существует неявный барьер: второй цикл начнется после окончания первого цикла.
Похожие вопросы
Связанные вопросы
Новые вопросы
c++
C++ — это язык программирования общего назначения. Изначально он разрабатывался как расширение C и имел аналогичный синтаксис, но теперь это совершенно другой язык. Используйте этот тег для вопросов о коде, который будет скомпилирован с помощью компилятора C++. Используйте тег версии для вопросов, связанных с конкретной стандартной версией [C++11], [C++14], [C++17], [C++20] или [C++23]. и т.д.