为了减少并发,为什么需要OpenMP压缩条款?
OpenMP'parallel'构造和'SIMD'构造(修订版4.0中的新增内容)定义了reduction子句,它告诉编译器哪个变量被执行,以及reduce操作符是什么。 但为什么编译器需要程序员告诉它这些信息呢? 例如,GCC有能力在没有得到编程人员帮助的情况下确定减少(见这里和这里)。 如果没有指定约简条款,是否有任何循环的例子不能成为并发的?
简化机制通过消除同步点并以放宽内存视图的一致性为代价来提高并行应用程序的性能。 从下面的例子中可以明确地看到需要明确订立减少条款:
假设您有一个代码可以搜索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
子句。
上一篇: Why is the OpenMP reduction clause needed to make reductions concurrent?