Я не вижу очевидных проблем, за исключением, возможно, следующего кода:
#pragma omp parallel default(shared) private(i, j, diff, priv_difmax)
{
priv_difmax = 0.0;
#pragma omp for schedule(static)
for (i=1; i <= m; i++) {
for (j=1; j <= n; j++) {
diff = fabs(tnew[i][j]-t[i][j]);
if (diff > priv_difmax) {
priv_difmax = diff;
}
//** copy new to old temperatures**
t[i][j] = tnew[i][j];
}
#pragma omp critical
if (priv_difmax > difmax){
difmax = priv_difmax;
}
}
}
Редукционная часть, копирующая priv_difmax
в difmax
, должна быть перемещена из цикла, чтобы потоки проходили черезсекция critical
только один раз, а не на каждой итерации внешнего цикла.
#pragma omp parallel default(shared) private(i, j, diff, priv_difmax)
{
priv_difmax = 0.0;
#pragma omp for schedule(static) nowait //no need to wait after the loop
for (i=1; i <= m; i++) {
for (j=1; j <= n; j++) {
diff = fabs(tnew[i][j]-t[i][j]);
if (diff > priv_difmax) {
priv_difmax = diff;
}
//** copy new to old temperatures**
t[i][j] = tnew[i][j];
}
}
// Finish the loop first, then update difmax
#pragma omp critical
if (priv_difmax > difmax){
difmax = priv_difmax;
}
} //Implicit barrier
Теперь распараллеливание сопряжено с накладными расходами, и ускорение можно ожидать только для больших значений m и n.Проблема, которую вы рассматриваете, возможно, слишком мала.Один из способов уменьшить издержки - объединить две конструкции parallel
, чтобы пул потоков не создавался дважды.Или, что еще лучше, поместите цикл while внутри конструкции parallel
, чтобы нам приходилось синхронизировать существующие потоки на каждой итерации, а не создавать и уничтожать их:
difmax=1000000.0;
#pragma omp parallel default(shared) private(i, j, diff, priv_difmax)
while (difmax > tol) {
// have one thread reset difmax and increment iter
#pragma omp single nowait
iter++,difmax=0.0;
// loop to update tnew - distributed among threads
#pragma omp parallel for schedule(static) \
default(shared) private(i,j)
for (i=1; i <= m; i++) {
for (j=1; j <= n; j++) {
tnew[i][j] = (t[i-1][j]+t[i+1][j]+t[i][j-1]+t[i][j+1])/4.0;
}
} //implicit barrier here
// each thread resets its private difmax
priv_difmax=0.0;
// loop to compute difmax - distributed among threads
#pragma omp for schedule(static) nowait
for (i=1; i <= m; i++) {
for (j=1; j <= n; j++) {
diff = fabs(tnew[i][j]-t[i][j]);
if (diff > priv_difmax) {
priv_difmax = diff;
}
//** copy new to old temperatures**
t[i][j] = tnew[i][j];
}
}
// each thread now updates difmax if needed, one at a time
#pragma omp critical
if (priv_difmax > difmax){
difmax = priv_difmax;
}
// put a barrier here to make sure that diffmax have been updated
// before any thread tests the condition for next iteration of the
// while-loop condition
#pragma omp barrier
}
Лучший способсравнить, как код выполняется последовательно и параллельно, значит скомпилировать его с поддержкой OpenMP и без нее (например, с помощью gcc, компилировать с компилятором -fopenmp и без него и флагом компоновщика).Это поможет определить, действительно ли проблема связана с распараллеливанием или другими изменениями между исходным последовательным кодом и версией, готовой к параллельной обработке.
Идея состоит в том, чтобы узнать, где теряется время при переходе от оригинальный серийный код до параллельный код (скомпилирован без поддержки параллельной работы) до параллельный код (скомпилирован с OpenMP)
Необходимо использовать заголовок предварительной обработкипотому что компилятор не будет распознавать такие функции, как omp_get_thread_num()
без поддержки OpenMP.omp_get_wtime()
также не следует использовать;поскольку все ваши временные измерения выполняются из параллельных регионов, нет необходимости использовать эту конкретную функцию, и вызов time()
будет точным (для этого необходимо #include <time.h>
).
// This part is necessary for the code to run whether it is compiled or not with OpenMP
#ifdef _OPENMP
#include <omp.h>
#else
# ifndef _ESCAPE_OMPENMP
#define omp_get_num_threads() 1
#define omp_get_thread_num() 0
#define omp_get_max_threads() 0
#define _ESCAPE_OMPENMP
#endif
#endif