OpenMP和C ++:私有变量

时间:2015-11-19 17:09:36

标签: c++ multithreading openmp

我是OpenMP和c ++的新手,也许是因为我有一些非常基本的问题。

我正在尝试执行静态计划,所有变量都是私有的(以防万一,为了验证获得的结果与非并行变量相同)。

当我看到bodies之类的变量时,问题就出现了,我不知道它们来自何处,因为它们之前没有定义过。

是否可以将所有出现的变量(例如bodies)定义为私有?怎么可能这样做

  std::vector<phys_vector> forces(bodies.size());

  size_t i, j; double dist, f, alpha;


  #pragma omp parallel for schedule(static) private(i, j, dist, f, alpha)
  for (i=0; i<bodies.size(); ++i) {
    for (j = i+1; j<bodies.size(); ++j) {
      dist = distance(bodies[i], bodies[j]);
      if (dist > param.min_distance()) {
        f = attraction(bodies[i], bodies[j], param.gravity(), dist);
        alpha = angle(bodies[i],bodies[j]);
        phys_vector deltaf{ f * cos(alpha) , f * sin(alpha) };
        forces[i] += deltaf;
        forces[j] -= deltaf;
      }
    }
  }
  return forces;
}

PS:使用当前代码,执行结果与非并行执行不同。

2 个答案:

答案 0 :(得分:3)

应该重申,您的bodies变量并不是随机出现的;你应该找到它的确切位置和定义的位置。但是,因为您只访问bodies的元素并且从不更改它们,所以此变量应该是shared,所以不是您的问题。

您的实际问题来自forces变量。您必须确保不同的线程不会更改同一forces[j]的变量j。如果您遵循循环的逻辑,则可以确保forces[i]仅由不同的线程访问,因此它们之间不存在争用。但是forces[j]可以很容易地通过并行j循环的不同迭代来修改i display: inline-block; height: 10px; 。您需要做的是reduce on your array,请遵循StackOverflow链接中的一个答案。

答案 1 :(得分:2)

NoseKnowsAll已正确识别您的问题。

我想更多地解释为什么会出现这个问题。你可以用这样的方形循环完成这个:

#pragma omp parallel for
for(int i=0; i<n; i++) {
    if(i==j) continue;
    phys_vector sum = 0;
    for(int j=0; j<n; j++) {
        //calculate deltaf
        sum += deltaf;
    }
    forces[i] = sum;
}

使用n*(n-1)次迭代,很容易并行化。

但是自force(i,j) = -force(j,i)以来我们可以在迭代的一半n*(n-1)/2中使用三角形循环(这就是你所做的)来做到这一点:

for(int i=0; i<n; i++) {
    phys_vector sum = 0;
    for(int j=i+1; j<n; j++) {
        //calculate deltaf
        sum += deltaf;
        forces[j] -= deltaf;
    }
    forces[i] = sum;
}

问题是当你进行这种优化时,它会使外循环并行化变得更加困难。有两个问题:写入forces[j]并且迭代不再很好地分布,即第一个线程比最后一个线程运行更多次迭代。

简单的解决方案是对内环进行分析

#pragma omp parallel
for(int i=0; i<n; i++) {
    phys_vector sum = 0;
    #pragma omp for
    for(int j=i+1; j<n; j++) {
        //calculate deltaf
        sum += deltaf;
        forces[j] -= deltaf;
    }
    #pragma omp critical
    forces[i] += sum;
}

这在n*nthreads次迭代中使用了n*(n-1)/2个关键操作。因此,当n变大时,关键操作的成本会变小。您可以为每个线程使用私有forces向量并将它们合并到一个关键部分,但我不认为这是必要的,因为关键操作在外部循环而不是内部循环。

这是一个fuses the triangular loop允许每个线程在相同的迭代次数上运行的解决方案。

unsigned n = bodies.size();
unsigned r = n*(n-1)/2;
#pragma omp parallel
{
    std::vector<phys_vector> forces_local(bodies.size());
    #pragma omp for schedule(static)
    for(unsigned k=0; k<r; k++) {
        unsigned i  = (1 + sqrt(1.0+8.0*k))/2;
        unsigned j = i - k*(k-1)/2;
        //calculate deltaf
        forces_local[i] += deltaf;
        forces_local[j] -= deltaf;
    }
    #pragma omp critical
    for(unsigned i=0; i<n; i++) forces[i] += forcs_local[i];
}

我对以前融合三角形的方法感到不满意(因为它需要使用浮点和sqrt函数)所以我想出了一个基于this answer的更简单的解决方案。

这会将三角形映射到矩形,反之亦然。首先,我转换为宽度为n但带有n*(n-1)/2的矩形(与三角形相同)。然后我计算矩形的(行,列)值,然后映射到三角形(跳过对角线)我使用以下公式

//i is the row, j is the column of the rectangle
if(j<=i) {
    i = n - i - 2;
    j = n - j - 1;
}

让我们选择一个例子。考虑以下n=5三角形循环对

(0,1), (0,2), (0,3), (0,4)
       (1,2), (1,3), (1,4)
              (2,3), (2,4)
                     (3,4)

将其映射到矩形变为

(3,4), (0,1), (0,2), (0,3), (0,4)
(2,4), (2,3), (1,2), (1,3), (1,4)

具有偶数值的三角循环以相同的方式工作,尽管它可能不那么明显。例如n = 4

(0,1), (0,2), (0,3)
       (1,2), (1,3)
              (2,3)

这变成了

(2,3), (0,1), (0,2), (0,3)
(1,2), (1,3)

这不是一个矩形,但映射的作用相同。我可以将其映射为

 (0,1), (0,2), (0,3)
 (2,3), (1,2), (1,3)

这是一个矩形,但我需要两个奇数和偶数三角形公式。

以下是使用矩形到三角形映射的新代码。

unsigned n = bodies.size();
#pragma omp parallel
{
    std::vector<phys_vector> forces_local(bodies.size());
    #pragma omp for schedule(static)
    for(unsigned k=0; k<n*(n-1)/2; k++) {
        unsigned i = k/n;
        unsigned j = k%n;
        if(j<=i) {
            i = n - i - 2;
            j = n - j - 1;
        }
        //calculate deltaf
        forces_local[i] += deltaf;
        forces_local[j] -= deltaf;
    }
    #pragma omp critical
    for(unsigned i=0; i<n; i++) forces[i] += forcs_local[i];
}