我有两个循环,我正在并行化
#pragma omp parallel for
for (i = 0; i < ni; i++)
for (j = 0; j < nj; j++) {
C[i][j] = 0;
for (k = 0; k < nk; ++k)
C[i][j] += A[i][k] * B[k][j];
}
#pragma omp parallel for
for (i = 0; i < ni; i++)
for (j = 0; j < nl; j++) {
E[i][j] = 0;
for (k = 0; k < nj; ++k)
E[i][j] += C[i][k] * D[k][j];
}
奇怪的是,即使使用大量线程,顺序执行也比上面的并行版本快得多。难道我做错了什么?请注意,所有数组都是全局的。这有什么不同吗?
答案 0 :(得分:4)
并行外循环的迭代共享其内循环的索引变量(j
和k
)。这肯定会使你的代码比你预期的要慢一些,即你的循环不是"embarrassingly"(或“令人愉快的”)并行和并行循环迭代需要以某种方式从共享内存中访问这些变量。
更糟糕的是,因此,您的代码包含race conditions。结果,它将表现出不确定性。换句话说:你的并行矩阵乘法的实现现在是不正确的! (继续检查你的计算结果。;))
您要做的是确保外部循环的所有迭代都有自己的索引变量j
和k
的私有副本。您可以通过在并行循环的范围内声明这些变量来实现此目的:
int i;
#pragma omp parallel for
for (i = 0; i < ni; i++) {
int j1, k1; /* explicit local copies */
for (j1 = 0; j1 < nj; j1++) {
C[i][j1] = 0;
for (k1 = 0; k1 < nk; ++k1)
C[i][j1] += A[i][k1] * B[k1][j1];
}
}
#pragma omp parallel for
for (i = 0; i < ni; i++) {
int j2, k2; /* explicit local copies */
for (j2 = 0; j2 < nl; j2++) {
E[i][j2] = 0;
for (k2 = 0; k2 < nj; ++k2)
E[i][j2] += C[i][k2] * D[k2][j2];
}
}
或在循环pragma中将它们声明为private
:
int i, j, k;
#pragma omp parallel for private(j, k)
for (i = 0; i < ni; i++)
for (j = 0; j < nj; j++) {
C[i][j] = 0;
for (k = 0; k < nk; ++k)
C[i][j] += A[i][k] * B[k][j];
}
#pragma omp parallel for private(j, k)
for (i = 0; i < ni; i++)
for (j = 0; j < nl; j++) {
E[i][j] = 0;
for (k = 0; k < nj; ++k)
E[i][j] += C[i][k] * D[k][j];
}
这些更改是否会使您的并行实现比顺序实现更快?很难说。这取决于您的问题规模。并行化(特别是通过OpenMP的并行化)带来了一些开销。只有当你产生足够的并行工作时,在并行线程上分配工作的收益才会超过所产生的开销成本。
要了解您的代码和软件/硬件平台的工作量是多少,我建议您通过运行不同矩阵大小的代码进行实验。然后,如果您还希望“太小”矩阵大小作为计算的输入,您可能希望使条件并行处理(例如,通过使用if
- 子句装饰循环编译指示):
#pragma omp parallel for private (j, k) if(ni * nj * nk > THRESHOLD)
for (i = 0; i < ni; i++) {
...
}
#pragma omp parallel for private (j, k) if(ni * nl * nj > THRESHOLD)
for (i = 0; i < ni; i++) {
...
}