如何优化循环:
for (l = 1; l <= loop; l++) {
for (i = 1; i < n; i++) {
x[i] = z[i] * (y[i] - x[i - 1]);
}
}
如何通过OpenMp
与
答案 0 :(得分:3)
假设您要并行化内循环
for ( i = 1; i < n; ++i ) {
x[i] = z[i] * ( y[i] - x[i - 1] );
}
我建议预先计算不依赖于前一循环的部分。这更容易并行化。
double preComps [n];
#pragma omp parallel for
for( i = 1; i < n ; ++i ) {
preComps[i] = z[i] * y[i];
}
// this loop is difficult to parallelize because of the data dependency on what was computed in the previous loop
for( i = 1; i < n ; ++i ) {
x[i] = preComps[i] - z[i] * x[i - 1];
}
答案 1 :(得分:1)
当您对外部循环控件变量l
进行无引用时,您甚至不会引用相同的赋值术语和在内循环中没有产生横向效应,运行内循环是幂等的,运行一次或多次没有任何好处,所以一个非常好的优化是完全消除它:)
,如下例所示:
00001: #include <stdio.h>
00002: #include <stdlib.h>
00003: #define n 10
00004: #define loop 30
00005: void print(int x[], int y[], int z[])
00006: {
00007: int i;
00008: printf("%12s%12s%12s%12s\n","i", "x[]", "y[]", "z[]");
00009: for(i = 0; i < n; i++)
00010: printf("%12d%12d%12d%12d\n", i, x[i], y[i], z[i]);
00011: }
00012: int main()
00013: {
00014: int x[n], y[n], z[n];
00015: int i, l;
00016: for(i = 0; i < n; i++) {
00017: x[i] = rand();
00018: y[i] = rand();
00019: z[i] = rand();
00020: }
打印开头
00021: print(x, y, z);
接下来是发布的循环:
00022: for (l = 1; l <= loop; l++) {
00023: printf("iteration %d\n", l);
00024: for (i = 1; i<n; i++) {
00025: x[i] = z[i] * (y[i] - x[i - 1]);
00026: }
并打印
00027: print(x, y, z);
00028: }
发布循环结束
00029: }
如您所见,循环传递之间的数组内容没有差异。接下来是一个程序运行来演示:
初始内容:
$ a.out
i x[] y[] z[]
0 33613 564950497 1097816498
1 1969887315 140734212 940422543
2 202055087 768218108 770072198
3 1866991770 1647128879 83392682
4 1421485336 148486083 229615973
5 127561358 735081006 33063457
6 1646757679 287085223 1793088605
7 802182690 382151770 1848710666
8 1486775472 115658218 394986197
9 661076908 1786703631 864107022
第一次迭代:
iteration 1
i x[] y[] z[]
0 33613 564950497 1097816498
1 -1607135687 140734212 940422543
2 1213242898 768218108 770072198
3 -1987622590 1647128879 83392682
4 -1113079323 148486083 229615973
5 -327431319 735081006 33063457
6 407021958 287085223 1793088605
7 1996444744 382151770 1848710666
8 500660170 115658218 394986197
9 -84727866 1786703631 864107022
iteration 2
i x[] y[] z[]
0 33613 564950497 1097816498
1 -1607135687 140734212 940422543
2 1213242898 768218108 770072198
3 -1987622590 1647128879 83392682
4 -1113079323 148486083 229615973
5 -327431319 735081006 33063457
6 407021958 287085223 1793088605
7 1996444744 382151770 1848710666
8 500660170 115658218 394986197
9 -84727866 1786703631 864107022
iteration 3
i x[] y[] z[]
0 33613 564950497 1097816498
1 -1607135687 140734212 940422543
2 1213242898 768218108 770072198
3 -1987622590 1647128879 83392682
4 -1113079323 148486083 229615973
5 -327431319 735081006 33063457
6 407021958 287085223 1793088605
7 1996444744 382151770 1848710666
8 500660170 115658218 394986197
9 -84727866 1786703631 864107022
...迭代重复直到
iteration 30
i x[] y[] z[]
0 33613 564950497 1097816498
1 -1607135687 140734212 940422543
2 1213242898 768218108 770072198
3 -1987622590 1647128879 83392682
4 -1113079323 148486083 229615973
5 -327431319 735081006 33063457
6 407021958 287085223 1793088605
7 1996444744 382151770 1848710666
8 500660170 115658218 394986197
9 -84727866 1786703631 864107022
$ _
如果你重新排序内部表达式,你也可以在内部循环中获得一些好处,如
x[0]
\----.
|
x[1] <+- y[1], z[1]
\---.
|
x[2] <+- y[2], z[2]
.
.
.
x[n-1]<+- y[n-1],z[n-1]
\--.
|
x[n] <+- y[n], z[n]
如果您将表达式重新排列为x[i] = z[i]*y[i] - z[i]*x[i-1]
,则可以z[i]*y[i]
的所有计算进行并行化,并在z[i]*x[i-1]
的值尽快计算x[i-1]
计算,在计算内循环时获得更多时间。
thrd[0] thrd[1] thrd[2] ... thrd[j] ... thrd[n]
============================================================
z[1]*x[0] z[1]*y[1] z[2]*y[2] ... z[j]*y[j] ... z[n-1]*y[n-1]
| | | | |
\----------+-------. | | |
,---' | | | |
| | | | |
V V | | |
x[1] = z[1]*y[1] - z[1]*x[0] | | |
| | | |
`--------------------. | | |
| | | |
,-----------+-----' | |
| | | |
V V | |
x[2] = z[2]*y[2] - z[2]*x[1] | |
| | |
`--------------------. | |
,-----------+-------------------' |
| | |
... ... |
V V |
x[j] = z[j]*y[j] - z[j]*x[j-1] |
... |
| |
`---------------------------. |
| |
,-------------+------------------------------'
| |
V V
x[n-1] = z[n-1]*y[n-1]-z[n-1]*x[n-2]
这可以通过两个线程池有效地计算。以前您有n-1
个产品和n-1
个减法产品,现在您有2*n
个产品和n-1
减法产品,并行计算,因此您最终无法节省成本(你得到两个线程,感谢KamiKaze向我展示了错误)
从上图可以看出,内环的计算仅取决于x[0]
,y[0...n-1]
和z[0...n-1]
,交叉值的唯一依赖性由表达式{ {1}}。如果您检查...如果我们将x[1] = f(x[0],z[1],y[1])
与x
或z
别名,那么表达式会转换为y
或x[j] = f(x[j-1],x[j], y[j])
,并且会生成值x[j] = f(x[j-1],z[j],x[j])
通常取决于 x[j]
的先前值。在这些情况下(x[j]
别名x
或y
或两者都有),算法不是幂等的,并且外部循环不能被消除。如果仅z
与y
别名,则表达式为z
(或x[j] = f(x[j-1], y[j])
),因此先前的值不存在依赖关系,并且算法是幂等的。
因此,总而言之,如果允许x[j] = f(x[j-1], z[j])
向量与任何x
或y
之间的别名,则无法消除外部循环,并且必须保留外部循环。在z
和y
别名的情况下,算法继续是幂等的,并且外部循环不是必需的。
答案 2 :(得分:0)
外环只是噪音,它一直都是这样,正如路易斯在答案中所说的那样。因此,我们不必考虑这一点(或者可以大规模地将其平行化,但只做一次就足够了)....
内循环取决于前一个循环(x[i-1]
)。
当使用Multiply-Accumulate指令时,z * y
将内循环更改为x[i] = preComps[i] - z[i] * x[i - 1];
(由dvhh提出)的预计算可能会带来好处,但这是特定于实现的,我不会不知道这个的好处。
如果0
中有z[]
,那么就有可能进行并行化。因为z[i] = 0
- &gt; x[i] = 0
为您提供了在此时分割内循环的可能性
x[i+1]
始终等于y[i+1] * z[i+1]
,这在任何时候都是已知的。为您提供另一个循环的入口点。