在std :: map的

所以我遇到的问题大致如下:

我有一个相对较大的数据集,该数据集始终包含一对标识符和与之相关的值。因此,存在相对较少的不同但任意的标识符。

在c ++中,它看起来像是std::vector<std::pair<size_t,double> >

我现在想生成一个std::map,它告诉我们每个标识符的所有值的总和,因此在这种情况下为std::map<size_t,double>

因此输入

std::vector<std::pair<size_t,double>> typeDoubleVec{{1,2.},{3,4.},{1,3.},5.},{2,1.}};

我想要一张等于:

的地图
std::map<size_t,double> result{{1,1.},9.}}

执行此任务的函数如下所示。因此,第二个输入向量指定存在哪些标识符:

std::map<size_t,double> foo(const std::vector<std::pair<size_t,double>> &typeDoubleVec,const std::vector<size_t> &typeVec) {
  // create the map that contains our return values.
  std::map<size_t,double> mymap;

  // ensure that mymap contains all identifiers.
  for (auto &elem : typeVec) {
    mymap[elem];
  }

  // iterate over the elements
  for (size_t i = 0; i < typeDoubleVec.size(); ++i) {
    mymap.at(typeDoubleVec[i].first) += typeDoubleVec[i].second;
  }
  return mymap;
}

有人知道如何使用OpenmP加快速度吗?我认为这种工作方式是您需要自定义OpenmP缩减功能?

gn666 回答:在std :: map的

所以我自己遇到了答案: 有两种方法可以执行此操作,一种是使用自定义缩减,另一种是使用关键部分。我目前建议使用后者,主要是因为前者在当前的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_inomp_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

大家都在问