我在C ++上使用OpenMP,我希望并行化非常简单的循环。但我不能正确地做到这一点。我一直得错了结果。
for(i=2;i<N;i++)
for(j=2;j<N;j++)
A[i,j] =A[i-2,j] +A[i,j-2];
代码:
int const N = 10;
int arr[N][N];
#pragma omp parallel for
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
arr[i][j] = 1;
#pragma omp parallel for
for (int i = 2; i < N; i++)
for (int j = 2; j < N; j++)
{
arr[i][j] = arr[i-2][j] +arr[i][j-2];
}
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
printf_s("%d ",arr[i][j]);
printf("\n");
}
你有什么建议我怎么办?谢谢!
答案 0 :(得分:3)
串行和并行运行会给出不同的。结果因为在
#pragma omp parallel for
for (int i = 2; i < N; i++)
for (int j = 2; j < N; j++)
{
arr[i][j] = arr[i-2][j] +arr[i][j-2];
}
.....
你正在更新arr [i]。所以你改变了另一个线程使用的数据。它将导致读写数据竞争!
答案 1 :(得分:3)
这个
#pragma omp parallel for
for (int i = 2; i < N; i++)
for (int j = 2; j < N; j++)
{
arr[i][j] = arr[i-2][j] +arr[i][j-2];
}
总是会成为悲伤和不可预测的输出的源泉。 OpenMP运行时将为每个线程提供i
的一系列值,并将它们保留给它。线程更新arr
的相对顺序没有确定性。例如,当线程1正在使用i = 2,3,4,5,...,100
(或其他)更新元素,而线程2正在使用i = 102,103,104,...,200
更新元素时,程序不会确定线程1是否在线程2之前或之后更新arr[i,:] = 100
想要使用arr
中的更新值。您编写了一个包含经典数据竞赛的代码。
您可以通过多种方式解决此问题:
您可以尝试确保线程以正确(即顺序)顺序更新arr
。最终结果将是一个OpenMP程序,其运行速度比顺序程序慢。不要选择这个选项。
您可以制作2份arr
副本,并始终从一个更新到另一个,然后从另一个更新为一个。像(非常伪代码)
for ...
{
old = 0
new = 1
arr[i][j][new] = arr[i-2][j][old] +arr[i][j-2][old];
old = 1
new = 0
}
当然,第二种方法是时间换空间,但这通常是一种合理的权衡。
您可能会发现向arr
添加额外的平面并不能立即加快速度,因为它会破坏拉入缓存的值的空间局部性。尝试一下,可能使[old]
成为第一个索引元素而不是最后一个。
由于更新数组中的每个元素取决于在2行/列之外的元素中找到的值,因此您可以将数组像棋盘一样有效地分割成白色和黑色元素。你可以使用2个线程,每个'颜色'一个,没有线程竞争访问相同的数据。但同样,缓存中空间局部性的中断可能会对速度产生不良影响。
如果我发现任何其他选项,我会在其中进行编辑。
答案 2 :(得分:3)
要在问题中并行化循环嵌套是棘手的,但可行。 Lamport的论文"The Parallel Execution of DO Loops"涵盖了该技术。基本上你必须将你的(i,j)坐标旋转45度到一个新的坐标系(k,l),其中k = i + j和l = i-j。
虽然要实际获得加速,但迭代可能必须分组到切片中,这使代码更加丑陋(四个嵌套循环)。
完全不同的方法是使用OpenMP任务递归地解决问题。递归是:
if( too small to be worth parallelizing ) {
do serially
} else {
// Recursively:
Do upper left quadrant
Do lower left and upper right quadrants in parallel
Do lower right quadrant
}
实际上,算术运算与内存访问的比例非常低,以至于很难从示例中获得加速。
答案 3 :(得分:0)
如果你一般都会问平行性,那么另一个可能的答案就是矢量化。没有,你可以实现一些相对较差的矢量parallelizm(类似于2x加速等) 改变数据结构和代码库。这可以使用OpenMP4.0或CilkPlus pragma simd或类似(使用safelen / vectorlength(2))
嗯,你真的有数据依赖(内部循环和外部循环),但它属于«WAR»[(写入后读取)依赖项子类别,这是阻止使用«omp parallel for««按原样»但不一定是«pragma omp simd»循环的问题。
要使其正常工作,您需要通过OpenMP4或CilkPlus(非常新的gcc或Intel编译器)支持pragma simd的x86编译器。