OpenMP 'parallel' 构造和 'SIMD' 构造(4.0 版中新增)定义了归约子句,它告诉编译器对哪个变量执行归约以及归约运算符是什么。但是为什么编译器需要程序员告诉它这些信息呢?例如,GCC 具有识别缩减的能力,而无需程序员的任何帮助(请参阅此处和此处)。有没有指定减少子句就不能并发的循环示例?
1 回答
减少是一种简单的机制,可以通过删除同步点来提高并行应用程序的性能,并以内存视图的宽松一致性为代价。从以下示例中应该可以立即清楚地看到明确具有归约子句的需要:
想象一下,您有一个代码对无序的NUM_ITEMS
项目集合进行搜索,目标是找到所有符合给定条件的项目并将它们收集到一个数组matches
中,并计算这些项目的某些属性的总和。找到项目的顺序无关紧要。序列号可能是这样的:
int num_matches = 0;
int prop_sum = 0;
for (i = 0; i < NUM_ITEMS; i++)
{
if (criteria(item[i]))
{
match[num_matches] = item[i];
num_matches++;
prop_sum += item[i]->some_property;
}
}
num_matches
和都是prop_sum
变量,其值随着循环的进行而累积。但是这两个变量具有完全不同的语义。虽然prop_sum
可以将其计算为部分和的总和,num_matches
但不能因为它用作输出数组中的索引。prop_sum
是减少的典型候选者num_matches
,但不仅可以减少,而且还必须利用显式同步结构来防止数据竞争和不同的线程覆盖 的相同元素match
:
int num_matches = 0;
int prop_sum = 0;
#pragma omp parallel for reduction(+:prop_sum)
for (i = 0; i < NUM_ITEMS; i++)
{
if (criteria(item[i]))
{
#pragma omp critical(update_matches)
{
match[num_matches] = item[i];
num_matches++;
}
prop_sum += item[i]->some_property;
}
}
尽管您可能会争辩说编译器可能足够聪明,可以注意到使用的方式num_matches
并自动生成原子增量,但 OpenMP 的目标是在平台和编译器供应商之间可移植。这意味着,如果您编写的 OpenMP 程序符合标准并且可以在一个编译器上正常编译和工作,那么它也应该可以在其他编译器上正常编译和工作。该标准由许多不同的供应商编写,并不是每个供应商都有这种超级智能的数据依赖发现机制。此外,当涉及编译单元外部的数据时,很难进行可靠的检测。
reduction
不是必需品——它只是一种便利。您可以实现自己的缩减,例如使用原子增量,这对于您的平台可能是最佳的,但在其他不提供有效原子增量的平台上可能远非最佳。另一方面,预计每个编译器都将生成代码,reduction
以针对给定目标平台以最佳方式实现该子句。