所以我自己遇到了答案:
有两种方法可以执行此操作,一种是使用自定义缩减,另一种是使用关键部分。我目前建议使用后者,主要是因为前者在当前的clang编译器(v9.0.0,修复已在trunk / master中)中损坏。
OpenMP并行进行自定义缩减
解决问题的第一种方法是使用OpenMP简化,通常看起来像这样:
// this does not work for maps!
#pragma omp parallel for reduction(+: mymap)
由于未为+
定义内置缩减std::map
,因此该代码片段无法编译。
相反,我们将必须定义自己的减少量。快速浏览一些OpenMP规范(https://www.openmp.org/spec-html/5.0/openmpsu107.html)会发现以下用于定义自定义归约的语法:
#pragma omp declare reduction(reduction-identifier : typename-list :
combiner) [initializer-clause] newline
- reduce-identifier:这是我们可以为自定义归约指定的名称。
- typename-list:这是为其定义此归约的类型名称的列表。对我们来说,这是
std::map<size_t,double>
- combiner:这是表达式,它进行实际的归约。它以
omp_in
和omp_out
作为输入,并将合并的结果存储在omp_out
中。对于简单的+
约简,这是omp_out += omp_in
。
- initializer-clause:这是一个可选表达式,其格式应为
initializer(expression)
。如果缺少,则将默认初始化还原变量的线程本地副本。如果is存在,则表达式的格式必须为omp_priv = initializer
的{{1}}。它还可以使用omp_priv = function-name(argument-list)
,它对应于归约变量的初始值。
- 在编译指示末尾需要换行符。
在这种情况下,我们要使用相同的键添加两个映射的值。这可以通过以下函数来完成:
omp_orig
如前所述,线程局部变量通常是默认初始化的。
但是,对于void mapAdd(std::map<size_t,double> &inout,std::map<size_t,double> &in) {
for (auto initer = in.begin(),outiter = inout.begin(); initer != in.end(); ++initer,++outiter) {
outiter->second += initer->second;
}
}
,不需要默认的初始化。
相反,应该使用已经存在的映射来初始化每个线程局部变量。
可以在初始化程序内部指定它,因此我们的编译指示如下:
std::map
,它可以从上方用于#pragma omp declare reduction( \
mapAdd : \
std::map<size_t,double> : \
mapAdd(omp_out,omp_in) \
) \
initializer (omp_priv=omp_orig)
:
omp parallel for
缺点
这不适用于当前的clang编译器。
它可以很好地编译,但是却产生了分段错误,经过一番挖掘后,我发现这不是我的错,而是编译器错误,因为同一程序可以在当前的gcc和intel编译器上找到。
此外,当在模板函数内部声明OpenMP简化时,clang编译器会出现问题(未定义的引用),因为它没有实例化OpenMP简化内部所需的所有函数。
另请参阅以下问题:
在自定义归约中使用lambdas
标准中未指定在自定义OpenMP缩减中使用lambda(根据https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60228)。
因此,我不建议您这样做。
但是,它确实可以与当前的intel编译器一起使用,并且可以与clang编译器的下一个版本(9.0.1或10)一起使用。 GCC尚不支持它(请参阅:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60228)。
使用关键部分
一种避免减少需求的方法是在非常基本的水平上重现它们,即,我们为每个线程创建一个本地副本,然后在关键部分内手动累积结果。这样做的好处是,它易于阅读,但比起通过自定义缩减的解决方案,它可能是一种较慢的解决方案,因为没有实现扇入。
采用这种方法的解决方案如下:
#pragma omp parallel for reduction(mapAdd : mymap)
for (size_t i = 0; i < typeDoubleVec.size(); ++i) {
mymap.at(typeDoubleVec[i].first) += typeDoubleVec[i].second;
}
代码
使用自定义归约法的代码可以在https://gist.github.com/SteffenSeckler/404c214bcccf506d261264672e2b9341
中找到
使用关键部分https://gist.github.com/SteffenSeckler/91943b881677f3cbe7b2d7d475471ee8
的代码
P.S。
感谢您反馈有关将其拆分为Q + A的反馈
本文链接:https://www.f2er.com/3035672.html