用户定义的缩减不返回每个 运行 的预期结果
User defined reduction not returning expected result with each run
我试图同时使用 OpenMP 查找二维数组中的最小值和最大值以及最小值和最大值的索引。在我的尝试中,我使用了用户定义的缩减,但是每个 运行.
都得到了意想不到的结果
我已经尝试检查 for 循环中的最小值和最大值,似乎在并行化的 for 循环中,最小值和最大值符合预期。但是,在 运行 的末尾,最小值和最大值包含完全古怪的值。
我的还原定义
typedef struct {
int value;
int index_i;
int index_j;
} Point;
#pragma omp declare reduction(minimum : Point : \
omp_out = omp_in.value < omp_out.value ? omp_in : omp_out) \
initializer(omp_priv = {INT_MAX, 0, 0})
#pragma omp declare reduction(maximum : Point : \
omp_out = omp_in.value > omp_out.value ? omp_in : omp_out) \
initializer(omp_priv = {0, 0, 0})
初始化二维数组,其中 size
为 10000
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
matrix[i][j] = rand()%99;
}
}
并行循环:
int i, j, total=0;
Point min, max;
#pragma omp parallel for reduction (+:total) reduction(minimum : min) reduction(maximum : max) private(j)
for (i = 0; i < size; i++) {
for (j = 0; j < size; j++) {
total += matrix[i][j];
if (matrix[i][j] < min.value) {
min.value = matrix[i][j];
min.index_i = i;
min.index_j = j;
}
if (matrix[i][j] > max.value) {
max.value = matrix[i][j];
max.index_i = i;
max.index_j = j;
}
}
}
预期结果是 min = 0
在索引 (0, 70)
和 max = 98
在索引 (0, 20)
.
实际结果每次都不同,但示例输出:
The min is -290390323 at index (21850, -9176672)
The max is 32595 at index (0, 0)
OpenMP 的部分思想是它支持现有的正确 串行代码的并行化。一般来说,从正确的 OpenMP 代码中删除或忽略所有 omp 编译指示——以便它 运行s 严格连续——不应更改计算结果。您的代码不满足该要求,因为您没有初始化 min
和 max
累积变量。
我猜您希望将归约定义的初始化子句应用于共享变量,但您误解了。初始化子句用于初始化每线程本地副本,而不是共享变量。本地副本在某些时候与共享变量结合作为缩减的一部分,否则代码在 运行 串行时不会产生相同的结果。
此外,请注意,对于 C,OpenMP 缩减初始化器子句实际上提供了 初始化器 ,在 C 标准的术语意义上。这些与赋值语句不同,在您的情况下,差异尤为明显,其中列表项具有结构类型。您的初始化器很好 as 初始化器,但它们不是有效的赋值表达式。因此,它们不能用于为并行区域内的共享变量分配初始值,因为初始化程序仅作为变量声明的一部分出现。
我试图同时使用 OpenMP 查找二维数组中的最小值和最大值以及最小值和最大值的索引。在我的尝试中,我使用了用户定义的缩减,但是每个 运行.
都得到了意想不到的结果我已经尝试检查 for 循环中的最小值和最大值,似乎在并行化的 for 循环中,最小值和最大值符合预期。但是,在 运行 的末尾,最小值和最大值包含完全古怪的值。
我的还原定义
typedef struct {
int value;
int index_i;
int index_j;
} Point;
#pragma omp declare reduction(minimum : Point : \
omp_out = omp_in.value < omp_out.value ? omp_in : omp_out) \
initializer(omp_priv = {INT_MAX, 0, 0})
#pragma omp declare reduction(maximum : Point : \
omp_out = omp_in.value > omp_out.value ? omp_in : omp_out) \
initializer(omp_priv = {0, 0, 0})
初始化二维数组,其中 size
为 10000
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
matrix[i][j] = rand()%99;
}
}
并行循环:
int i, j, total=0;
Point min, max;
#pragma omp parallel for reduction (+:total) reduction(minimum : min) reduction(maximum : max) private(j)
for (i = 0; i < size; i++) {
for (j = 0; j < size; j++) {
total += matrix[i][j];
if (matrix[i][j] < min.value) {
min.value = matrix[i][j];
min.index_i = i;
min.index_j = j;
}
if (matrix[i][j] > max.value) {
max.value = matrix[i][j];
max.index_i = i;
max.index_j = j;
}
}
}
预期结果是 min = 0
在索引 (0, 70)
和 max = 98
在索引 (0, 20)
.
实际结果每次都不同,但示例输出:
The min is -290390323 at index (21850, -9176672)
The max is 32595 at index (0, 0)
OpenMP 的部分思想是它支持现有的正确 串行代码的并行化。一般来说,从正确的 OpenMP 代码中删除或忽略所有 omp 编译指示——以便它 运行s 严格连续——不应更改计算结果。您的代码不满足该要求,因为您没有初始化 min
和 max
累积变量。
我猜您希望将归约定义的初始化子句应用于共享变量,但您误解了。初始化子句用于初始化每线程本地副本,而不是共享变量。本地副本在某些时候与共享变量结合作为缩减的一部分,否则代码在 运行 串行时不会产生相同的结果。
此外,请注意,对于 C,OpenMP 缩减初始化器子句实际上提供了 初始化器 ,在 C 标准的术语意义上。这些与赋值语句不同,在您的情况下,差异尤为明显,其中列表项具有结构类型。您的初始化器很好 as 初始化器,但它们不是有效的赋值表达式。因此,它们不能用于为并行区域内的共享变量分配初始值,因为初始化程序仅作为变量声明的一部分出现。