使用OpenMP优化外部循环并减少

时间:2017-05-19 13:44:25

标签: c++ c++11 parallel-processing openmp

我在功能方面有点挣扎。如果我尝试使用

并行化外部循环,则计算错误
#pragma omp parallel reduction(+:det). 

有人可以告诉我如何解决它以及它失败的原因吗?

// template<class T> using vector2D = std::vector<std::vector<T>>;

float Det(vector2DF &a, int n)
{
  vector2DF m(n - 1, vector1DF(n - 1, 0));

  if (n == 1) return a[0][0];
  if (n == 2) return a[0][0] * a[1][1] - a[1][0] * a[0][1];

  float det = 0;
  for (int i = 0; i < n; i++)
  {
    int l = 0;
#pragma omp parallel for private(l)
    for (int j = 1; j < n; j++)
    {
      l = 0;
      for (int k = 0; k < n; k++)
      {
        if (k == i) continue;
        m[j - 1][l] = a[j][k];
        l++;
      }
    }
    det += std::pow(-1.0, 1.0 + i + 1.0) * a[0][i] * Det(m, n - 1);
  }

  return det;
}

1 个答案:

答案 0 :(得分:2)

如果您并行化外循环,则此行上存在竞争条件:

m[j - 1][l] = a[j][k];

此外,您可能需要parallel for reduction而不只是parallel reduction

问题是,m是共享的,即使在内循环中完全覆盖它也没有必要。始终在本地声明变量,这可以避免错误共享变量的问题,例如:

float Det(vector2DF &a, int n)
{
  if (n == 1) return a[0][0];
  if (n == 2) return a[0][0] * a[1][1] - a[1][0] * a[0][1];

  float det = 0;
  #pragma omp parallel reduction(+:det)
  for (int i = 0; i < n; i++)
  {
    vector2DF m(n - 1, vector1DF(n - 1, 0));
    for (int j = 1; j < n; j++)
    {
      int l = 0;
      for (int k = 0; k < n; k++)
      {
        if (k == i) continue;
        m[j - 1][l] = a[j][k];
        l++;
      }
    }
    det += std::pow(-1.0, 1.0 + i + 1.0) * a[0][i] * Det(m, n - 1);
  }
  return det;
}

现在这是正确的,但由于m的分配成本很高,因此在每次迭代中不执行此操作都可以获得性能。这可以通过分割parallelfor指令来完成:

float Det(vector2DF &a, int n)
{
  if (n == 1) return a[0][0];
  if (n == 2) return a[0][0] * a[1][1] - a[1][0] * a[0][1];

  float det = 0;
  #pragma omp parallel reduction(+:det)
  {
    vector2DF m(n - 1, vector1DF(n - 1, 0));
    #pragma omp parallel for
    for (int i = 0; i < n; i++)
    {
      for (int j = 1; j < n; j++)
      {
        int l = 0;
        for (int k = 0; k < n; k++)
        {
          if (k == i) continue;
          m[j - 1][l] = a[j][k];
          l++;
        }
      }
      det += std::pow(-1.0, 1.0 + i + 1.0) * a[0][i] * Det(m, n - 1);
    }
  }
  return det;
}

现在您也可以将m声明为firstprivate,但这会假设复制构造函数生成完全独立的深层复制,从而使代码更难以推理。

请注意,您应始终包含预期输出,实际输出和minimal complete and verifiable example