更新2d数组时相邻元素的依赖关系(OpenMP)

时间:2017-11-16 21:28:02

标签: c parallel-processing dependencies openmp

我有一个二维数组,比如arr[SIZE][SIZE],它在表格中以两个for循环更新:

for(int i = 0; i < SIZE; i++)
    for(int j = 0; j < SIZE; j++)
        arr[i][j] = new_value();

我正在尝试使用 OpenMP 进行并行化。

有两种情况会发生这种情况,第一种是依赖new_value_1()arr[i+1][j]的函数arr[i][j+1](“数组的边缘”问题已经处理完毕) ,我可以愉快地使用棋盘技术并行化:

for(int l = 0; l < 2; l++)
#pragma omp parallel for private(i, j)
    for(int i = 0; i < SIZE; i++)
        for(int j = (i + l) % 2; j < SIZE; j += 2)
            arr[i][j] = new_value_1();

问题来自第二个实例new_value_2(),它依赖于:

arr[i+1][j],
arr[i-1][j],
arr[i][j+1],
arr[i][j-1]

即。各个方向的相邻元素。

这里,负相邻元素存在依赖关系,因此arr[0][2] = new_value_2()取决于已更新的arr[0][1]值,该值在第二个l循环之前不会计算。

我想知道在这种方式并行化时是否存在某些问题,或者问题是否与算法的工作方式有关?如果是前者,任何其他方法都会受到赞赏。

1 个答案:

答案 0 :(得分:2)

  

我想知道在这种方式并行化时是否存在某些问题,或者问题是否与算法的工作方式有关?

是的,你错过了什么,但不是你希望的那样。假设并行版本应该计算与串行版本相同的结果,即使对于new_value_1()情况,棋盘方法也不能解决问题。考虑一下这个数组元素的布局:

xoxoxo
oxoxox
xoxoxo
oxoxox

在两个棋盘游戏的第一个中,'x'元素根据'o'元素的原始值更新 - 到目前为止,非常好 - 但在第二遍,'o'元素根据'x'元素的值进行更新。数据依赖性被破坏,是的,但整体计算是不一样的。当然,这同样适用于new_value_2()计算。棋盘格并没有真正帮助你。

  

如果是前者,任何其他方法都会受到赞赏。

你可以在shell中进行计算。例如,考虑数组元素的这种标记:

0123
1234
2345
3456

具有相同标签的所有元素可以并行计算(对于两个new_value()函数),前提是所有具有数字较小标签的元素都是先计算的。这可能看起来像这样:

for(int l = 0; l < (2 * SIZE - 1); l++) {
    int iterations = (l < SIZE) ? (l + 1) : (2 * SIZE - (l + l));
    int i = (l < SIZE) ? l : (SIZE - 1);
    int j = (l < SIZE) ? 0 : (1 + l - SIZE);

    #pragma omp parallel for private(i, j, m)
    for(int m = 0; m < iterations; m++) {
        arr[i--][j++] = new_value_1();
    }
}

你不会从并行化中获得尽可能多的好处,但 是串行计算工作方式的固有方面,至少在new_value_2()情况下如此。

对于new_value_1()案例,你可以逐行进行一些改善:

for(int i = 0; i < SIZE; i++)
    #pragma omp parallel for private(j)
    for(int j = 0; j < SIZE; j++)
        arr[i][j] = new_value_1();

或者,仅对于new_value_1()情况,您可以通过将结果存储在单独的数组中来获得良好的加速:

#pragma omp parallel for private(i, j), collapse(2)
for(int i = 0; i < SIZE; i++)
    for(int j = 0; j < SIZE; j++)
        arr2[i][j] = new_value_1();

如果这需要你将结果复制回原始数组,那么它可能不值得,但你可以通过在两个数组之间来回切换来避免这种情况:从第一个到第二个计算,以及然后下一次,从第二次计算到第一次(如果问题 [PSPACE] 扩展允许具有这样的扩展的RAM内分配,即它再次出现[PTIME]成本,希望只支付一次)。