请考虑以下代码:
#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
的方式似乎会导致问题。
怎么能这样做?
答案 0 :(得分:3)
这似乎不应该太难,但我不确定从哪里开始。
如果它看起来不应该那么难,那么你还没有正确地理解这项任务。并非所有高迭代计数循环都是并行计算的理想选择。
j = __builtin_ctzl(counter)正在给出标尺序列。使用#pragma omp parallel是很诱人的,但计算j的方式似乎会导致问题。
计算j
的方式不是那么多,而是如何使用它以及它在串行计算过程中所呈现的问题所采用的值。特别注意
delta[j]
delta[j]
j
在整体计算过程中多次重复使用相同的值因此,使用delta
和j
会在循环的不同迭代之间创建许多依赖关系。除非为使用相同j
值的迭代保留相对顺序,否则迭代不能重新排序或并行运行 - 否则,计算结果可能会改变。
并非全部。内循环的每次迭代都使用和修改s
以及数组v
的所有元素。这些是并行化的更严重的问题,因为它们使外部循环的每次迭代都依赖于所有之前的迭代。
怎么能这样做?
在此计算可以使用并行性之前,您需要处理依赖性问题。如果问题只出现在delta
和j
那么您可能会重新安排计算以处理涉及每个j
值的所有计算。如果只是s
,那么您很可能将计算分成s == 1
和s == -1
块,然后并行化。但是,我遇到了麻烦,看看你如何同时处理这两个问题,或者你如何处理涉及v
的依赖关系。
也许您对计算的本质有一些了解,这将使您能够以更适合并行计算的形式重新实现它。然而,它现在的形式本质上是连续的。
答案 1 :(得分:1)
当在循环内计算的变量只是计数器和外部不变的变量时,并行化是一个好主意。
当在循环内计算的变量依赖于前一次迭代中的相同变量时,并行化是一个坏主意。
检查串行循环是否适合并行化的简单练习是尝试颠倒循环的顺序。如果计数器按升序排列,请按降序排列。如果循环产生相同的结果,并行化非常适合您的问题。
在您的问题中,许多变量都依赖于之前迭代的值:v
,delta
,s
和p
。
由于p
的中间值无关紧要,因此不存在问题。 omp parallel reduction
可以解决此问题。
s
可以重新定义,以便它不依赖于以前的值。 if (counter % 2 == 0){s = 1;}else{s = -1;}
但是,我没有看到处理delta
和v
的好方法。这需要比我更熟悉这个问题的具体细节。如果您能找到一种以非递归方式定义这些数量的方法,那么OpenMP将变得非常有用。祝你好运!