c++ - 结构中的 OMP 数据依赖数组
问题描述
我是使用 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 节:
依赖子句的语法如下:
depend([depend-modifier,] dependence-type: locator-list)
[...]
一个定位器列表由一个或多个定位器列表项的逗号分隔集合组成
[...]依赖子句
中出现的列表项可能包括数组部分或保留的定位器。omp_all_memory
因此,简而言之:这完全符合编译器不实现数组部分解析/支持的要求。这实际上是 GCC 的情况,而 Clang 正确解析它们。
一些编译器和运行时不关心/支持依赖子句中的数组部分。AFAIK,到目前为止,所有主流的 OpenMP 实现(包括 GCC 的 GOMP 和 Clang/ICC 的 IOMP)都只是在运行时忽略它们......原因是在运行时执行依赖分析显然太昂贵了(一些研究项目尝试过来实现这一点,但性能结果不是很好)。因为所使用的 OpenMP 运行时与编译器紧密绑定,并且由于前面的一点,某些编译器可能在依赖子句中根本不支持数组部分(这意味着在您的情况下它会导致解析错误)。
话虽如此,根据第 2.1.5 节,您使用的数组部分语法看起来符合 OpenMP 标准,但请注意定位器/数组部分不得重叠。在您的情况下,它们似乎重叠打破了 OpenMP 标准并导致支持数组部分的 OpenMP 运行时出现未定义的行为。
我建议您不要在依赖子句中使用数组部分。相反,您可以使用带有在指令之外预定义的依赖定位器的指针,以避免编译器解析问题:
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(或错误地检测为未使用)时为未使用的变量生成无用的警告。
推荐阅读
- mysql - 使用列中的日期作为查询结果中的列标题
- reactjs - 如何减少 React 构建文件夹的大小
- event-sourcing - 事件溯源 - 提供给命令的数据和保存在事件中的数据
- json - 如何在 JSON 数据的索引中获取所有可用的 JSON 数据
- python - Python 3.8.5 不会将特定的字符串值分配为字典键
- masstransit - 在 UseConsumeFilter 之后 MassTransit.dll 中的 NullReferenceException
- json - SWIFT:尝试解码 JSON,它返回 nil
- python - 如何用python绘制混淆矩阵
- swift - 由于 base64 编码的 aesKey 和 aesIV,CryptoSwift 抛出 invalidKeySize
- c# - 了解内存数据库的 Dispose 方法