【发布时间】:2019-07-17 08:00:42
【问题描述】:
我刚刚开始学习使用 OpenMP 进行并行编程,并且嵌套循环中有一个微妙之处。我写了一个简单的矩阵乘法代码,并检查了结果是否正确。但实际上有几种方法可以并行化这个 for 循环,在底层细节方面可能会有所不同,我想问一下。
一开始,我写了下面的代码,将两个矩阵A,B相乘,并将结果赋值给C。
for(i = 0; i < N; i++)
{
for(j = 0; j < N; j++)
{
sum = 0;
#pragma omp parallel for reduction(+:sum)
for(k = 0; k < N; k++)
{
sum += A[i][k]*B[k][j];
}
C[i][j] = sum;
}
}
它有效,但需要很长时间。而且我发现由于parallel指令的位置,它会构建并行区域N2次。当我使用 linux time 命令时,我发现它的用户时间大幅增加。
下一次,我尝试了下面的代码,它也有效。
#pragma omp parallel for private(i, j, k, sum)
for(i = 0; i < N; i++)
{
for(j = 0; j < N; j++)
{
sum = 0;
for(k = 0; k < N; k++)
{
sum += A[i][k]*B[k][j];
}
C[i][j] = sum;
}
}
使用上面的代码,经过的时间从顺序执行的 72.720 秒减少到并行执行的 5.782 秒。这是合理的结果,因为我是用 16 核执行的。
但第二个代码的流程在我的脑海中并不容易绘制。我知道如果我们将所有循环变量私有化,程序会将嵌套循环视为一个大小为 N3 的大循环。可以通过执行下面的代码轻松检查。
#pragma omp parallel for private(i, j, k)
for(i = 0; i < N; i++)
{
for(j = 0; j < N; j++)
{
for(k = 0; k < N; k++)
{
printf("%d, %d, %d\n", i, j, k);
}
}
}
printf 被执行了 N3 次。
但是在我的第二个矩阵乘法代码中,在最内层循环之前和之后都有sum。很困扰我,很容易在我的脑海中展开循环。我写的第三个代码很容易在我的脑海中展开。
总而言之,我想知道在我的第二个矩阵乘法代码中,幕后究竟发生了什么,尤其是随着sum 值的变化。或者我真的很感谢你推荐一些工具来观察用 OpenMP 编写的多线程程序的流程。
【问题讨论】:
-
我有点不确定这里的问题到底是什么。在第一种情况下,最内层的循环是并行的,正如您得出的结论,这不是最好的主意。在第二种情况下,只有最外层的循环被并行化,然后每个线程分别为特定的
i执行两个内层循环。这显然表现得更好,因为进入并行区域的开销只“支付”一次。
标签: c++ multithreading openmp