Недавно я начал работать с OpenMP, чтобы провести небольшое «исследование» для проекта в университете. У меня есть прямоугольная и равномерно распределенная сетка, на которой я решаю уравнение в частных производных с итерационной схемой. Таким образом, у меня в основном есть два цикла for (по одному в направлении x и y сетки каждый), обернутых циклом while для итераций.
Теперь я хочу исследовать различные схемы распараллеливания для этого. Первый (очевидный) подход заключался в том, чтобы сделать пространственное распараллеливание на циклах for. Тоже нормально работает.
Подход, с которым у меня возникли проблемы, - идея более сложная. Каждый поток вычисляет все точки сетки. Первый поток начинает решение уравнения в первой строке сетки (y = 0). По завершении поток переходит к следующему ряду (y = 1) и так далее. При этом поток №2 может начинаться уже при y = 0, потому что вся необходимая информация уже доступна. Мне просто нужно выполнить своего рода ручную синхронизацию между потоками, чтобы они не могли обогнать друг друга.
Поэтому я использовал массив с именем check
. Он содержит идентификатор потока, которому в настоящее время разрешено работать с каждой строкой сетки. Когда следующая строка не «готова» (значение в check[j]
неверно), поток переходит в пустой цикл while, пока он не станет готовым.
С MWE все станет яснее:
#include <stdio.h>
#include <math.h>
#include <omp.h>
int main()
{
// initialize variables
int iter = 0; // iteration step counter
int check[100] = { 0 }; // initialize all rows for thread #0
#pragma omp parallel num_threads(2)
{
int ID, num_threads, nextID;
double u[100 * 300] = { 0 };
// get parallelization info
ID = omp_get_thread_num();
num_threads = omp_get_num_threads();
// determine next valid id
if (ID == num_threads - 1) nextID = 0;
else nextID = ID + 1;
// iteration loop until abort criteria (HERE: SIMPLIFIED) are valid
while (iter<1000)
{
// rows (j=0 and j=99 are boundary conditions and don't have to be calculated)
for (int j = 1; j < (100 - 1); j++)
{
// manual sychronization: wait until previous thread completed enough rows
while (check[j + 1] != ID)
{
//printf("Thread #%d is waiting!\n", ID);
}
// gridpoints in row j
for (int i = 1; i < (300 - 1); i++)
{
// solve PDE on gridpoint
// replaced by random operation to consume time
double ignore = pow(8.39804,10.02938) - pow(12.72036,5.00983);
}
// update of check array in atomic to avoid race condition
#pragma omp atomic write
{
check[j] = nextID;
}
}// for j
#pragma omp atomic write
check[100 - 1] = nextID;
#pragma omp atomic
iter++;
#pragma omp single
{
printf("Iteration step: %d\n\n", iter);
}
}//while
}// omp parallel
}//main
Дело в том, что этот MWE действительно работает на моей машине. Но если я скопирую это в свой проект, этого не произойдет. Кроме того, результат всегда разный: он останавливается либо после первой итерации, либо после третьей.
Еще одна странность: когда я удаляю косую черту в комментарии во внутреннем цикле while, он работает! Вывод содержит некоторые
"Thread #1 is waiting!"
но это разумно. Мне кажется, что я каким-то образом создал состояние гонки, но я не знаю где.
Есть ли у кого-нибудь идеи, в чем может быть проблема? Или подсказка, как реализовать такую синхронизацию?
iter
делится? Я не уверен, что так должно быть, но если так, его нужно обновить вatomic
, не так ли? - person Gilles   schedule 29.12.2016iter
. Итак, вы правы, я вставил это вatomic
, но это не повлияло на результат. - person topper91   schedule 29.12.2016