如何使用openmp并行化循环?

时间:2017-02-01 13:54:18

标签: c openmp

请考虑以下代码:

#include <stdlib.h>
#include <math.h>
float f(float M[32][32]) { 
  int counter, s = 1, i, j, n = 32;
  float *delta = malloc(n * sizeof *delta);
  float *v = malloc(n * sizeof *v);
  float prod = 1, p = 0;
  for (i = 0; i < n; i++) {
      v[i] = 0;
      delta[i] = 1;
  }
  for(counter=1; counter < pow(2,n-1); counter++) { 
      prod = 1.;
      j = __builtin_ctzl(counter);
      for (i = 0; i < n; i++) {
        v[i] -= 2.*delta[j]*M[j][i];
        prod *= v[i];
      }
      delta[j] = -delta[j];
      s = -s;            
      p += s*prod;    
    }
  return p;
}

我想使用openmp并行化涉及counter的外部循环。

这似乎不应该太难,但我不确定从哪里开始。 j = __builtin_ctzl(counter)正在给ruler sequence。使用#pragma omp parallel for很有吸引力,但计算j的方式似乎会导致问题。

怎么能这样做?

2 个答案:

答案 0 :(得分:3)

  

这似乎不应该太难,但我不确定从哪里开始。

如果它看起来不应该那么难,那么你还没有正确地理解这项任务。并非所有高迭代计数循环都是并行计算的理想选择。

  

j = __builtin_ctzl(counter)正在给出标尺序列。使用#pragma omp parallel是很诱人的,但计算j的方式似乎会导致问题。

计算j的方式不是那么多,而是如何使用它以及它在串行计算过程中所呈现的问题所采用的值。特别注意

  • 每次循环迭代使用delta[j]
  • 的值
  • 每次循环迭代也修改 delta[j]
  • 的值
  • j在整体计算过程中多次重复使用相同的值

因此,使用deltaj会在循环的不同迭代之间创建许多依赖关系。除非为使用相同j值的迭代保留相对顺序,否则迭代不能重新排序或并行运行 - 否则,计算结果可能会改变。

并非全部。内循环的每次迭代都使用和修改s以及数组v的所有元素。这些是并行化的更严重的问题,因为它们使外部循环的每次迭代都依赖于所有之前的迭代。

  

怎么能这样做?

在此计算可以使用并行性之前,您需要处理依赖性问题。如果问题只出现在deltaj那么您可能会重新安排计算以处理涉及每个j值的所有计算。如果只是s,那么您很可能将计算分成s == 1s == -1块,然后并行化。但是,我遇到了麻烦,看看你如何同时处理这两个问题,或者你如何处理涉及v的依赖关系。

也许您对计算的本质有一些了解,这将使您能够以更适合并行计算的形式重新实现它。然而,它现在的形式本质上是连续的。

答案 1 :(得分:1)

当在循环内计算的变量只是计数器和外部不变的变量时,并行化是一个好主意。

当在循环内计算的变量依赖于前一次迭代中的相同变量时,并行化是一个坏主意。

检查串行循环是否适合并行化的简单练习是尝试颠倒循环的顺序。如果计数器按升序排列,请按降序排列。如果循环产生相同的结果,并行化非常适合您的问题。

在您的问题中,许多变量都依赖于之前迭代的值:vdeltasp

由于p的中间值无关紧要,因此不存在问题。 omp parallel reduction可以解决此问题。

s可以重新定义,以便它不依赖于以前的值。 if (counter % 2 == 0){s = 1;}else{s = -1;}

但是,我没有看到处理deltav的好方法。这需要比我更熟悉这个问题的具体细节。如果您能找到一种以非递归方式定义这些数量的方法,那么OpenMP将变得非常有用。祝你好运!