Ваша программа имеет неопределенное поведение .См. Раздел 2.8 в спецификации OpenMP 5.0 1 :
Каждая область общего доступа должна встречаться всеми потоками в команде или вообще ни с кем
Это означает, что любой вид ветви (if
, while
и т. Д.) , чье состояние может быть различным для разных потоков вокруг #pragma omp for
(или любого другого worksharing construct ) недопустим:
#pragma omp parallel
{
if (...true for some threads, false for others...) // ILLEGAL!
{
#pragma omp for
for (...) ...
}
while (...true for some threads, false for others...) // ILLEGAL!
{
#pragma omp for
for (...) ...
}
}
В вашем случае это неопределенное поведение, вероятно, приводит к следующей последовательности событий:
- Каждый поток проверяетусловие, но оно может быть не одинаковым для всех потоков - некоторые входят в цикл
while
, некоторые - нет. - Если они входят в цикл
while
: - Они сталкиваются
#pragma omp for
. - В цикле for они обновляют
error
. - Они ждут у неявного барьера в конце
#pragma omp for
.
- Если они не входят в цикл
while
: - Они ждут у неявного барьера в конце
#pragma omp parallel
.
Когда поток OpenMP достигает барьера, он ожидает, пока все потоки в его команде не достигнут барьера. Неявный барьер #pragma omp for
не адаптируется к числу потоков, с которыми столкнулась конструкция. В вашем случае некоторые потоки никогда не достигнут барьера в конце цикла for
(потому что для нихусловие while
было ложным).Они пропустили цикл while
и теперь ждут у неявного барьера в конце #pragma omp parallel
.
В результате возникает тупик: некоторые потоки ждут в конце #pragma omp for
, другие - вконец #pragma omp parallel
, и две группы больше никогда не соберутся вместе ...
Явный барьер перед #pragma omp for
, предложенный в ответе Уолтера, исправляет это, разделяя чтение и запись общей переменнойerror
.Более конкретно:
- Каждый поток проверяет условие, и оно одинаково для всех - либо все, либо ни один не входят в тело цикла
while
. - Если они входят в
while
loop: - Все они ждут у явного барьера.
- Все они сталкиваются с
#pragma omp for
. - В цикле for они обновляют
error
. - Все они ждут у неявного барьера в конце
#pragma omp for
.(Барьер подразумевает неявное flush
, что означает, что все потоки видят окончательное значение error
.) - Вернитесь к началу.
- После
while
loop: - Все они ждут у неявного барьера в конце
#pragma omp parallel
. - Готово.
ИзКонечно, теперь все потоки выполняют цикл for
, и это не «минимизирует издержки распараллеливания», что вам и нужно.Я думаю, вам придется еще немного реструктурировать свой код, чтобы достичь этой цели.Может быть, использование #pragma omp task
вместо #pragma omp for
может быть хорошим подходом, но это зависит от деталей ваших реальных структур данных и алгоритмов.
Примечание: вы можете получитьизбавиться от тупика, добавив предложение nowait
к #pragma omp for
, но это было бы хаком, и ваша программа все равно имела бы неуказанное поведение .
1: ... или соответствующий раздел в других версиях OpenMP.