你的代码问题在于内部循环的迭代次数取决于外部循环。根据OpenMP规范中关于绑定和
collapse
子句描述的部分:
如果执行任何相关循环会更改用于计算任何迭代计数的任何值,则行为未指定。
例如,当使用一个正方形循环时,可以使用collapse。
#pragma omp parallel for private(j) collapse(2)
for (i = 0; i < 4; i++)
for (j = 0; j < 100; j++)
实际上,这是一个很好的使用collapse的例子。外层循环只有四次迭代。如果你有超过四个线程,那么有些线程将会被浪费。但是当你折叠线程时,线程将分布在400次迭代中,这可能比线程数要大得多。使用collapse的另一个原因是负载不均衡。如果你只用了四次迭代,第四次迭代花费的时间最长,其他线程就得等待。但是如果你使用了400次迭代,负载很可能会更加均衡。
你可以像下面这样手动合并循环:
#pragma omp parallel for
for(int n=0; n<4*100; n++) {
int i = n/100; int j=n%100;
这里提供了一个手动融合三重循环的示例。
最后,这里提供了一个示例,展示如何融合一个无法使用"collapse"指令的三角形循环。
这里提供了一个解决方案,将一个矩形循环映射到OPs问题中的三角形循环。这可以用于融合OPs的三角形循环。
for(int k=0; k<n*(n+1)/2; k++) {
int i = k/(n+1), j = k%(n+1);
if(j>i) i = n - i -1, j = n - j;
printf("(%d,%d)\n", i,j);
}
这适用于任何n的值。
对于此问题的OP,映射如下:
(0,0),
(1,0), (1,1),
(2,0), (2,1), (2,2),
(3,0), (3,1), (3,2), (3,3),
到
(0,0), (3,3), (3,2), (3,1), (3,0),
(1,0), (1,1), (2,2), (2,1), (2,0),
对于奇数n值,该图不完全是一个矩形,但公式仍然有效。
例如,n = 3被映射为
(0,0),
(1,0), (1,1),
(2,0), (2,1), (2,2),
(0,0), (2,2), (2,1), (2,0),
(1,0), (1,1),
这里是测试此功能的代码
#include <stdio.h>
int main(void) {
int n = 4;
for(int i=0; i<n; i++) {
for(int j=0; j<=i; j++) {
printf("(%d,%d)\n", i,j);
}
}
puts("");
for(int k=0; k<n*(n+1)/2; k++) {
int i = k/(n+1), j = k%(n+1);
if(j>i) i = n - i - 1, j = n - j;
printf("(%d,%d)\n", i,j);
}
}
n
,另一个是m
,那么每个线程得到的迭代次数为n/nthreads
。而在折叠之后,迭代次数为n*m
。这可以帮助解决当n
相对于nthreads
不是很大,但n*m
是很大的情况。 - Z boson