OPENMP - 使用前置条件

时间:2017-11-23 07:58:54

标签: c parallel-processing mpi openmp

我需要将Schwarz算法并行化,但我不知道如何处理前置条件以及存在嵌套循环的事实。 我必须使用OpenMP或MPI。

void ssor_forward_sweep(int n, int i1, int i2, int j1, int j2, int k1, int k2, double* restrict Ax, double w)
{
#define AX(i,j,k) (Ax[((k)*n+(j))*n+(i)])

int i, j, k;
double xx, xn, xe, xu;

for (k = k1; k < k2; ++k) {
   for (j = j1; j < j2; ++j) {
      for (i = i1; i < i2; ++i) {
         xx = AX(i,j,k);
         xn = (i > 0)   ? AX(i-1,j,k) : 0;
         xe = (j > 0)   ? AX(i,j-1,k) : 0;
         xu = (k > 0)   ? AX(i,j,k-1) : 0;
         AX(i,j,k) = (xx+xn+xe+xu)/6*w;
      }
   }
}
#undef AX
}

考虑到每个循环使用之前循环中的值,如何并行化此函数以获得最佳时间。

我已经尝试过二次并行循环或者分块(比如Stencil Jacobi 3D),但没有成功......

非常感谢!

1 个答案:

答案 0 :(得分:3)

不幸的是,循环间数据依赖性限制了您在嵌套循环中可以获得的并行度。

您可以使用具有依赖性的任务,这将是最简单的方法。 OpenMP运行时库将负责调度,您只关注算法。另一个好的方面是在任何循环结束时没有同步,但只在代码的相关部分之间。

#pragma omp parallel
#pragma omp single
for (int k = 0; k < k2; k += BLOCK_SIZE) {
  for (int j = 0; j < j2; j += BLOCK_SIZE) {
    for (int i = 0; i < i2; i += BLOCK_SIZE) {
      #pragma omp task depend (in: AX(i-1,j,k),AX(i,j-1,k),AX(i,j,k-1)) \
                       depend (out: AX(i,j,k))
      // your code here
    }
  }
}

任务有时比并行循环要贵一些(取决于工作负载和同步粒度),所以另一种选择是wavefront parallelization模式,它基本上转换迭代空间,以便内循环中的元素是独立的彼此之间(所以你可以在那里使用并行)。

wavefront

如果您想要接近,我强烈建议您将算法转换为阻塞算法:展开您的3个嵌套循环以分两个阶段进行计算:

  1. 在固定大小的块/多维数据集中进行迭代(让我们调用您的新归纳变量iijjkk)。
  2. 对于每个块,请调用循环的原始串行版本。
  3. 阻塞的目标是增加并行部分的粒度,以便并行化开销不明显。

    这是阻塞部分的一些伪代码:

    #define min(a,b) ((a)<(b)?(a):(b))
    // Inter block iterations
    for (int kk = 0; kk < k2; kk += BLOCK_SIZE) {
      for (int jj = 0; jj < j2; jj += BLOCK_SIZE) {
        for (int ii = 0; ii < i2; ii += BLOCK_SIZE) {
    
          // Intra block iterations
          for (int k = kk; k < min(k2,k+BLOCK_SIZE); k++) {
            for (int j = jj; j < min(j2,j+BLOCK_SIZE); j++) {
              for (int i = ii; i < min(i2,i+BLOCK_SIZE); i++) {
                  // Your code goes here
              }
            }
          }
    
        }
      }
    }
    

    在波前并行化的情况下,最后一步是将外部循环(块间迭代)转换为波前,以便迭代不相互依赖的元素。在3D迭代空间中,它基本上是从(0,0,0)前进到(i2,j2,k2)的对角线平面。像下面图片中用红色突出显示的那个。

    3D diagonal plane

    我将举一个2D波前的例子,因为它更容易理解。

    #define min(a,b) ((a)<(b)?(a):(b))
    #pragma omp parallel
    for (int d = 1; d < i2+j2; d++ ) {
        int i = min(d,i2) - 1;
        int j = 0;
        // Iterations in the inner loop are independent
        // Implicit thread barrier (synchronization) at the end of the loop
        #pragma omp for
        for ( ; i >= 0 && j < min(d,j2); i--, j++) {
          // your code here
        }
    }