OpenMP并行代码较慢

时间:2014-12-01 19:29:25

标签: c parallel-processing openmp

我有两个循环,我正在并行化

#pragma omp parallel for
  for (i = 0; i < ni; i++)
    for (j = 0; j < nj; j++) {
      C[i][j] = 0;
      for (k = 0; k < nk; ++k)
        C[i][j] += A[i][k] * B[k][j];
    }
#pragma omp parallel for
  for (i = 0; i < ni; i++)
    for (j = 0; j < nl; j++) {
      E[i][j] = 0;
      for (k = 0; k < nj; ++k)
        E[i][j] += C[i][k] * D[k][j];
    }

奇怪的是,即使使用大量线程,顺序执行也比上面的并行版本快得多。难道我做错了什么?请注意,所有数组都是全局的。这有什么不同吗?

1 个答案:

答案 0 :(得分:4)

并行外循环的迭代共享其内循环的索引变量(jk)。这肯定会使你的代码比你预期的要慢一些,即你的循环不是"embarrassingly"(或“令人愉快的”)并行和并行循环迭代需要以某种方式从共享内存中访问这些变量。

更糟糕的是,因此,您的代码包含race conditions。结果,它将表现出不确定性。换句话说:你的并行矩阵乘法的实现现在是不正确的! (继续检查你的计算结果。;))

您要做的是确保外部循环的所有迭代都有自己的索引变量jk的私有副本。您可以通过在并行循环的范围内声明这些变量来实现此目的:

int i;

#pragma omp parallel for
  for (i = 0; i < ni; i++) {
    int j1, k1;  /* explicit local copies */
    for (j1 = 0; j1 < nj; j1++) {
      C[i][j1] = 0;
      for (k1 = 0; k1 < nk; ++k1)
        C[i][j1] += A[i][k1] * B[k1][j1];
    }
  }        
#pragma omp parallel for
  for (i = 0; i < ni; i++) {
    int j2, k2;  /* explicit local copies */
    for (j2 = 0; j2 < nl; j2++) {
      E[i][j2] = 0;
      for (k2 = 0; k2 < nj; ++k2)
        E[i][j2] += C[i][k2] * D[k2][j2];
    }
  }

或在循环pragma中将它们声明为private

int i, j, k;

#pragma omp parallel for private(j, k)
  for (i = 0; i < ni; i++)
    for (j = 0; j < nj; j++) {
      C[i][j] = 0;
      for (k = 0; k < nk; ++k)
        C[i][j] += A[i][k] * B[k][j];
    }
#pragma omp parallel for private(j, k)
  for (i = 0; i < ni; i++)
    for (j = 0; j < nl; j++) {
      E[i][j] = 0;
      for (k = 0; k < nj; ++k)
        E[i][j] += C[i][k] * D[k][j];
    }

这些更改是否会使您的并行实现比顺序实现更快?很难说。这取决于您的问题规模。并行化(特别是通过OpenMP的并行化)带来了一些开销。只有当你产生足够的并行工作时,在并行线程上分配工作的收益才会超过所产生的开销成本。

要了解您的代码和软件/硬件平台的工作量是多少,我建议您通过运行不同矩阵大小的代码进行实验。然后,如果您还希望“太小”矩阵大小作为计算的输入,您可能希望使条件并行处理(例如,通过使用if - 子句装饰循环编译指示):

#pragma omp parallel for private (j, k) if(ni * nj * nk > THRESHOLD)
  for (i = 0; i < ni; i++) {
     ...
  }
#pragma omp parallel for private (j, k) if(ni * nl * nj > THRESHOLD)
  for (i = 0; i < ni; i++) {
    ...
  }