Стандарт OpenMP определяет начальное значение для редукционной переменной. Так нужно ли мне инициализировать переменную и как мне это сделать в следующем случае:
int sum;
//...
for(int it=0;i<maxIt;i++){
#pragma omp parallel
{
#pragma omp for nowait
for(int i=0;i<ct;i++)
arrayX[i]=arrayY[i];
sum = 0;
#pragma omp for reduction(+:sum)
for(int i=0;i<ct;i++)
sum+=arrayZ[i];
}
//Use sum
}
Обратите внимание, что я использую только 1 параллельную область, чтобы минимизировать накладные расходы и разрешить nowait в первом цикле. Использование этого как есть приведет к гонке данных (IMO), потому что потоки, поступающие из первого цикла после запуска других потоков во втором цикле, будут сбрасывать сумму.
Конечно, я могу сделать это в верхней части внешнего цикла, но в общем случае и для больших баз кода вы можете забыть, что вам нужно или было установлено это, что приводит к неожиданным результатам.
Помогает ли здесь «omp single»? Я подозреваю, что в то время как поток A выполняет одиночный, другой поток может уже войти в цикл сокращения.
«omp барьер» возможен, но я хочу избежать этого, так как он побеждает «Nowait».
И последний пример:
#pragma omp parallel
{
sum = 0;
#pragma omp for reduction(+:sum)
for(int i=0;i<ct;i++)
sum+=arrayZ[i];
//Use sum
sum = 0;
#pragma omp for reduction(+:sum)
for(int i=0;i<ct;i++)
sum+=arrayZ[i];
//Use sum
}
Как бы я (пере) инициализировать здесь?
Изменить: этот ответ неправильно так как он делает предположение, которого нет в спецификации OpenMP. Поскольку принятые ответы не могут быть удалены, я оставляю это здесь в качестве примера, что всегда следует сомневаться и проверять код и / или утверждения, найденные в Интернете.
На самом деле, код не отображает данные гонки:
#pragma omp parallel
{
...
sum = 0;
#pragma omp for reduction(+:sum)
for(int i=0;i<ct;i++)
sum+=arrayZ[i];
...
}
Здесь происходит то, что частная копия sum
создается внутри конструкции совместного использования и инициализируется 0
(значение инициализации для +
оператор). Каждая локальная копия обновляется телом цикла. Как только данный поток завершен, он ожидает неявного барьера, присутствующего в конце for
построить. Как только все потоки достигли барьера, их локальные копии sum
суммируются вместе, и результат добавляется к общему значению.
Неважно, что все потоки могут выполняться sum = 0;
в другое время, так как его значение обновляется только после достижения барьера. Подумайте о коде выше, выполняющем что-то вроде:
...
sum = 0;
// Start of the for worksharing construct
int local_sum = 0; // ^
for(int i = i_start; i < i_end; i++) // | sum not used here
local_sum += arrayZ[i]; // v
// Implicit construct barrier
#pragma omp barrier
// Reduction
#pragma omp atomic update
sum += local_sum;
#pragma omp barrier
// End of the worksharing construct
...
То же самое относится и ко второму примеру.
Спецификация OpenMP не предписывает, когда и как обновляется исходное значение, и обязывает использовать синхронизацию (OpenMP, с.205):
Чтобы избежать условий гонки, одновременные чтения или обновления исходного элемента списка должны быть синхронизированы с обновлением исходного элемента списка, которое происходит в результате
reduction
вычисление.
В обоих примерах либо barrier
после назначения sum
или single
построить (без nowait
) необходим для предотвращения гонки.