OpenMP: предложения в настоящее время и сокращения на одной и той же прагме - PullRequest
10 голосов
/ 11 июня 2011

Я изучаю OpenMP и наткнулся на следующий пример:

#pragma omp parallel shared(n,a,b,c,d,sum) private(i)
{
    #pragma omp for nowait
    for (i=0; i<n; i++)
        a[i] += b[i];

    #pragma omp for nowait
    for (i=0; i<n; i++)
        c[i] += d[i];
    #pragma omp barrier

    #pragma omp for nowait reduction(+:sum)
    for (i=0; i<n; i++)
        sum += a[i] + c[i];
} /*-- End of parallel region --*/

В последнем цикле for есть предложение nowait и сокращениеЭто правильно?Не нужно ли синхронизировать условие сокращения?

Ответы [ 3 ]

18 голосов
/ 11 июня 2011

nowait s во втором и последнем цикле несколько избыточны.В спецификации OpenMP упоминается nowait перед концом региона, поэтому, возможно, это может остаться.

Но nowait перед вторым циклом и явный барьер после него отменяют друг друга.

Наконец, о предложениях shared и private.В вашем коде shared не имеет никакого эффекта, и private просто не должен использоваться вообще: если вам нужна переменная с приватным потоком, просто объявите ее внутри параллельной области.В частности, вы должны объявить переменные цикла внутри цикла, а не раньше.

Чтобы сделать shared полезным, вам нужно сообщить OpenMP, что по умолчанию он не должен ничего совместно использовать.Вы должны сделать это, чтобы избежать ошибок из-за случайно используемых переменных.Это делается путем указания default(none).Это оставляет нас с:

#pragma omp parallel default(none) shared(n, a, b, c, d, sum)
{
    #pragma omp for nowait
    for (int i = 0; i < n; ++i)
        a[i] += b[i];

    #pragma omp for
    for (int i = 0; i < n; ++i)
        c[i] += d[i];

    #pragma omp for nowait reduction(+:sum)
    for (int i = 0; i < n; ++i)
        sum += a[i] + c[i];
} // End of parallel region
9 голосов
/ 12 июня 2011

В некоторых отношениях это похоже на домашнее задание, которое я ненавижу делать для людей. С другой стороны, приведенные выше ответы не совсем точны, и я считаю, что их следует исправить.

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

Что касается вопроса о барьерах, то в первом цикле может быть предложение nowait, поскольку во втором цикле значение, вычисленное (a), не используется. Второй цикл может иметь предложение nowait, только если вычисленное значение (c) не используется до вычисления значений (т.е. нет зависимости). В исходном коде примера во втором цикле есть nowait, но явный барьер перед третьим циклом. Это нормально, поскольку ваш профессор пытался показать использование явного барьера - хотя отключение nowait во втором цикле сделает избыточным явный барьер (так как в конце цикла есть неявный барьер).

С другой стороны, nowait во втором цикле и явный барьер могут вообще не понадобиться. До спецификации OpenMP V3.0 многие полагали, что что-то было правдой, что не было разъяснено в спецификации. Со спецификацией OpenMP V3.0 в раздел 2.5.1 Loop Construct было добавлено следующее, таблица 2-1, предложение расписания вид значения, статическое (расписание):

Соответствующая реализация статического расписания должна обеспечивать назначение номеров логических итераций потокам будет использоваться в два цикла области, если выполняются следующие условия: 1) обе области цикла имеют одинаковое количество итераций цикла, 2) обе области цикла имеют одинаковое значение указан chunk_size, или в обеих областях цикла не указано значение chunk_size, и 3) Обе области петли связаны с одной и той же параллельной областью. Зависимость данных между одинаковые логические итерации в двух таких циклах гарантированно будут выполнены разрешить безопасное использование предложения nowait (см. раздел A.9 на стр. 170 для примеры).

Теперь в вашем примере расписание не было показано ни в одном из циклов, так что это может или не может выполняться. Причина в том, что расписание по умолчанию определяется реализацией, и хотя большинство реализаций в настоящее время определяют расписание по умолчанию как статическое, это не гарантируется. Если ваш профессор установил статический тип расписания без chunk-size во всех трех циклах, то nowait можно было бы использовать в первом и втором циклах, и никакой барьер (ни явный, ни явный) не понадобился бы между второй и третьей петлями вообще.

Теперь перейдем к третьему циклу и вашему вопросу о текущем состоянии и сокращении. Как указал Мичи, спецификация OpenMP позволяет указывать и то, и другое (сокращение и nowait). Однако неверно, что для завершения сокращения синхронизации не требуется. В этом примере неявный барьер (в конце третьего цикла) может быть удален с помощью nowait. Это связано с тем, что сокращение (сумма) не используется до того, как будет обнаружен неявный барьер параллельной области.

Если вы посмотрите на спецификацию OpenMP V3.0, раздел 2.9.3.6 сокращения, вы найдете следующее:

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

Это означает, что если вы хотите использовать переменную суммы в параллельной области после третьего цикла, то вам потребуется барьер (неявный или явный), прежде чем использовать его. Поскольку пример стоит сейчас, это правильно.

2 голосов
/ 11 июня 2011

Спецификация OpenMP говорит:

Синтаксис конструкции цикла следующий:

#pragma omp for [clause[[,] clause] ... ] new-line
    for-loops

, где предложение является одним из следующих:

 ...
 reduction(operator: list)
 ...
 nowait

Таким образом, может быть больше предложений, таким образом, может быть и сокращение, и оператор nowait.

Нет необходимости явной синхронизации в предложении reduction - добавлениепеременная sum синхронизируется из-за того, что reduction(+: sum) и предыдущие барьерные силы a и b имеют окончательные значения во времени цикла reduction.nowait означает, что если поток завершает работу в цикле, ему не нужно ждать, пока все другие потоки завершат тот же цикл.

...