Я изучаю много статей и руководство по OpenACC, но до сих пор не понимаю основного различия этих двух конструкций.
Разница между построением ядер и параллельным построением
Ответы (3)
Директива kernels
- это более общий случай, о котором вы, вероятно, могли бы подумать, если ранее писали ядра GPU (например, CUDA). kernels
просто указывает компилятору работать с фрагментом кода и производить произвольное количество «ядер» произвольной «размерности», которые должны выполняться последовательно, чтобы распараллелить / выгрузить конкретную часть кода на ускоритель. Конструкция parallel
позволяет более детально управлять тем, как компилятор будет пытаться структурировать работу на ускорителе, например, путем указания конкретных размеров распараллеливания. Например, количество рабочих и групп обычно будет постоянным как часть директивы parallel
(поскольку обычно подразумевается только одно базовое «ядро»), но, возможно, не в директиве kernels
(поскольку это может быть преобразовано в несколько базовых «ядер» ).
Хороший ответ на этот конкретный вопрос содержится в этой статье о PGI.
Цитата из резюме статьи: «Каждое ядро OpenACC и параллельные конструкции пытаются решить одну и ту же проблему, идентифицируя параллелизм цикла и отображая его на машинный параллелизм. в соответствии с требованиями целевого ускорителя. Параллельная конструкция является более явной и требует от программиста более тщательного анализа, чтобы определить, когда она является законной и уместной ".
parallel
реализовано намного лучше. Насколько я могу судить, reduction
не поддерживается kernel
в GCC.
- person Z boson; 12.03.2018
Директивы OpenACC и ядра графического процессора - это всего лишь два способа представления одного и того же - раздела кода, который может выполняться параллельно.
OpenACC может быть лучшим вариантом при модернизации существующего приложения для использования преимуществ графического процессора и / или когда желательно позволить компилятору обрабатывать больше деталей, связанных с такими проблемами, как управление памятью. Это может ускорить написание приложения с потенциальными потерями в производительности.
Ядра могут быть лучшими при написании приложения для графического процессора с нуля и / или когда требуется более детальный контроль. Это может увеличить время написания приложения, но может повысить производительность.
Я думаю, что у людей, плохо знакомых с графическими процессорами, может возникнуть соблазн перейти на OpenACC, потому что он выглядит более знакомым. Но я думаю, что на самом деле лучше пойти другим путем и начать с написания ядер, а затем, возможно, перейти на OpenACC, чтобы сэкономить время в некоторых проектах. Причина в том, что OpenACC - это дырявая абстракция. Таким образом, хотя OpenACC может выглядеть так, как будто детали графического процессора абстрагированы, они все еще существуют. Таким образом, использование OpenACC для написания кода графического процессора без понимания того, что происходит в фоновом режиме, скорее всего, разочарует, с появлением странных сообщений об ошибках при попытке компиляции и приведет к тому, что приложение будет иметь низкую производительность.
kernels
- это директива, определенная стандартом OpenACC. Он также включает директиву parallels
.
- person Mark Ebersole; 14.01.2013
Параллельная конструкция
Определяет область программы, которая должна быть скомпилирована для параллельного выполнения на устройстве-ускорителе.
Директива параллельного цикла - это утверждение программиста о том, что распараллеливание затронутого цикла безопасно и желательно. Это требует от программиста правильного определения параллелизма в коде и удаления из кода всего, что может быть небезопасно для распараллеливания. Если программист неверно утверждает, что цикл может быть распараллелен, результирующее приложение может дать неверные результаты.
Параллельная конструкция позволяет более детально контролировать то, как компилятор будет пытаться структурировать работу на ускорителе. Таким образом, он не сильно зависит от способности компилятора автоматически распараллеливать код.
Когда параллельный цикл используется в двух последующих циклах, которые обращаются к одним и тем же данным, компилятор может или не может копировать данные туда и обратно между хостом и устройством между двумя циклами.
Более опытные параллельные программисты, которые, возможно, уже определили параллельные циклы в своем коде, вероятно, сочтут подход параллельного цикла более желательным.
например, ссылка
#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];
}
Сгенерировать одно ядро
Между двумя петлями нет барьера: второй цикл может начаться до того, как закончится первый. (Это отличается от 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];
}
Создайте два ядра
Между двумя циклами существует неявный барьер: второй цикл начнется после завершения первого цикла.