结构中的 OMP 数据依赖数组
OMP data dependency array in a struct
我是 OpenMP 并行编程的新手,我只是在学习任务和数据依赖性的工作原理。
我开发了一个简单的矩阵乘法(使用块)程序,我在其中定义了一个结构,如下所示:
struct matrix {
int ncols;
int nrows;
double* mat;
};
现在,对于每个矩阵,我都执行 malloc 以获得线性向量和线性化矩阵。
我要编写的并行化代码是这样的:
#pragma omp parallel
#pragma omp single
for(i=0; i<m1->nrows; i+=BS){
for(j=0; j<m2->ncols; j+=BS){
for(k=0; k<m3->ncols; k+=BS){
#pragma omp task depend(in: m1->mat[i:BS*BS], m2->mat[k:BS*BS]) depend(inout: m3->mat[i:BS*BS])
for (ii = i; ii < i+BS; ii++) {
for (jj = j; jj < j+BS; jj++) {
for (kk = k; kk < k+BS; kk++) {
m3->mat[ii * m3->ncols + jj] += m1->mat[ii*m1->ncols+kk] * m2->mat[kk*m2->ncols+jj];
}
}
}
}
}
}
问题是编译器报告了一些错误,但我确信可以使用数组设置依赖关系...
mat_mul_blocks.c:67:42: error: expected ‘]’ before ‘:’ token
67 | #pragma omp task depend(in: m1->mat[i:BS*BS], m2->mat[k:BS*BS]) depend(inout: m3->mat[i:BS*BS])
| ^
| ]
mat_mul_blocks.c:67:60: error: expected ‘]’ before ‘:’ token
67 | #pragma omp task depend(in: m1->mat[i:BS*BS], m2->mat[k:BS*BS]) depend(inout: m3->mat[i:BS*BS])
| ^
| ]
mat_mul_blocks.c:67:92: error: expected ‘]’ before ‘:’ token
67 | in: m1->mat[i:BS*BS], m2->mat[k:BS*BS]) depend(inout: m3->mat[i:BS*BS])
基于 OpenMP 5.1 规范的第 2.19.11 节和第 2.1 节:
The syntax of the depend clause is as follows:
depend([depend-modifier,] dependence-type: locator-list)
[...]
A locator-list consists of a comma-separated collection of one or more locator list items
[...]
The list items that appear in the depend clause may include array sections or the omp_all_memory
reserved locator.
因此,简而言之:这完全符合编译器不实现数组部分的要求parsing/support。这实际上是 GCC 的情况,而 Clang 正确地解析了它们。
一些编译器和运行时不care-about/support依赖子句中的数组部分。 AFAIK,所有主流的 OpenMP 实现(包括 GCC 的 GOMP 和 Clang/ICC 的 IOMP)到目前为止都只是在运行时忽略它们......合理的是依赖分析显然在运行时执行起来太昂贵了(一些研究项目试图实现这一点,但性能结果并不理想)。由于使用的 OpenMP 运行时与编译器紧密绑定,并且由于前一点,某些编译器可能根本不支持 depend 子句中的数组部分(这意味着在您的情况下会导致解析错误)。
也就是说,根据第 2.1.5 节,您使用的数组部分语法看起来符合 OpenMP 标准,但请注意 locators/array-sections 不能重叠.在您的情况下,它们似乎与 OpenMP 标准重叠并导致支持数组部分的 OpenMP 运行时出现未定义的行为。
我建议你不要在 depend 子句中使用数组部分。相反,您可以 将指针与在指令外预定义的依赖定位器一起使用 以避免编译器解析问题:
const double* dep1 = &m3->mat[i * m3->ncols + j];
const double* dep2 = &m1->mat[i * m1->ncols + k];
const double* dep3 = &m2->mat[k * m1->ncols + j];
#pragma omp task depend(in: *dep1, *dep2) depend(inout: *dep3)
这段代码应该适用于大多数编译器,包括 GCC、Clang 和 ICC(到目前为止,MSVC 仅支持 OpenMP 2.0)。请注意,自 C++17 起,您可以使用属性 [[maybe_unused]]
来避免编译器在 OpenMP 未被目标编译器 enabled/supported(或错误检测为未使用)时为未使用的变量生成无用的警告。
我是 OpenMP 并行编程的新手,我只是在学习任务和数据依赖性的工作原理。 我开发了一个简单的矩阵乘法(使用块)程序,我在其中定义了一个结构,如下所示:
struct matrix {
int ncols;
int nrows;
double* mat;
};
现在,对于每个矩阵,我都执行 malloc 以获得线性向量和线性化矩阵。 我要编写的并行化代码是这样的:
#pragma omp parallel
#pragma omp single
for(i=0; i<m1->nrows; i+=BS){
for(j=0; j<m2->ncols; j+=BS){
for(k=0; k<m3->ncols; k+=BS){
#pragma omp task depend(in: m1->mat[i:BS*BS], m2->mat[k:BS*BS]) depend(inout: m3->mat[i:BS*BS])
for (ii = i; ii < i+BS; ii++) {
for (jj = j; jj < j+BS; jj++) {
for (kk = k; kk < k+BS; kk++) {
m3->mat[ii * m3->ncols + jj] += m1->mat[ii*m1->ncols+kk] * m2->mat[kk*m2->ncols+jj];
}
}
}
}
}
}
问题是编译器报告了一些错误,但我确信可以使用数组设置依赖关系...
mat_mul_blocks.c:67:42: error: expected ‘]’ before ‘:’ token
67 | #pragma omp task depend(in: m1->mat[i:BS*BS], m2->mat[k:BS*BS]) depend(inout: m3->mat[i:BS*BS])
| ^
| ]
mat_mul_blocks.c:67:60: error: expected ‘]’ before ‘:’ token
67 | #pragma omp task depend(in: m1->mat[i:BS*BS], m2->mat[k:BS*BS]) depend(inout: m3->mat[i:BS*BS])
| ^
| ]
mat_mul_blocks.c:67:92: error: expected ‘]’ before ‘:’ token
67 | in: m1->mat[i:BS*BS], m2->mat[k:BS*BS]) depend(inout: m3->mat[i:BS*BS])
基于 OpenMP 5.1 规范的第 2.19.11 节和第 2.1 节:
The syntax of the depend clause is as follows:
depend([depend-modifier,] dependence-type: locator-list)
[...]
A locator-list consists of a comma-separated collection of one or more locator list items
[...]
The list items that appear in the depend clause may include array sections or theomp_all_memory
reserved locator.
因此,简而言之:这完全符合编译器不实现数组部分的要求parsing/support。这实际上是 GCC 的情况,而 Clang 正确地解析了它们。
一些编译器和运行时不care-about/support依赖子句中的数组部分。 AFAIK,所有主流的 OpenMP 实现(包括 GCC 的 GOMP 和 Clang/ICC 的 IOMP)到目前为止都只是在运行时忽略它们......合理的是依赖分析显然在运行时执行起来太昂贵了(一些研究项目试图实现这一点,但性能结果并不理想)。由于使用的 OpenMP 运行时与编译器紧密绑定,并且由于前一点,某些编译器可能根本不支持 depend 子句中的数组部分(这意味着在您的情况下会导致解析错误)。
也就是说,根据第 2.1.5 节,您使用的数组部分语法看起来符合 OpenMP 标准,但请注意 locators/array-sections 不能重叠.在您的情况下,它们似乎与 OpenMP 标准重叠并导致支持数组部分的 OpenMP 运行时出现未定义的行为。
我建议你不要在 depend 子句中使用数组部分。相反,您可以 将指针与在指令外预定义的依赖定位器一起使用 以避免编译器解析问题:
const double* dep1 = &m3->mat[i * m3->ncols + j];
const double* dep2 = &m1->mat[i * m1->ncols + k];
const double* dep3 = &m2->mat[k * m1->ncols + j];
#pragma omp task depend(in: *dep1, *dep2) depend(inout: *dep3)
这段代码应该适用于大多数编译器,包括 GCC、Clang 和 ICC(到目前为止,MSVC 仅支持 OpenMP 2.0)。请注意,自 C++17 起,您可以使用属性 [[maybe_unused]]
来避免编译器在 OpenMP 未被目标编译器 enabled/supported(或错误检测为未使用)时为未使用的变量生成无用的警告。