使用c ++ 17算法并行化一个简单的循环

met*_*fox 7 c++ iterator stl-algorithm c++17

我有一个可以简化为基本的并行代码:

#include <algorithm>
#include <vector>

struct TKeyObjPtr;

class TObj
{
public:
  virtual void Calculate(TKeyObjPtr const &) = 0;
};

struct TKeyObjPtr
{
  int Key;
  TObj *ObjPtr;
};

void Calculate(std::vector<TKeyObjPtr> const &KeyObjPtrVec)
{
  #pragma omp parallel for
  for (auto It1= KeyObjPtrVec.begin(); It1!=KeyObjPtrVec.end(); ++It1)
    for (auto It2= It1+1; It2!=KeyObjPtrVec.end() && It2->Key==It1->Key; ++It2)
      It1->ObjPtr->Calculate(*It2);
}
Run Code Online (Sandbox Code Playgroud)

我想通过使用并行算法来实现代码的现代化.不幸的是,我在重写这么简单的代码时遇到了麻烦.

一个选项将使用boost::counting_iterator:

void Calculate(std::vector<TKeyObjPtr> const &KeyObjPtrVec)
{
  std::for_each(std::execution::par_unseq,
    boost::counting_iterator<std::size_t>(0u),
    boost::counting_iterator<std::size_t>(KeyObjPtrVec.size()),
    [&KeyObjPtrVec](auto i)
      {
        for (auto j= i+1; j<KeyObjPtrVec.size() && KeyObjPtrVec[j].Key==KeyObjPtrVec[i].Key; ++j)
          KeyObjPtrVec[i].ObjPtr->Calculate(KeyObjPtrVec[j]);
      });
}
Run Code Online (Sandbox Code Playgroud)

这可行,但是更加冗长,更糟糕的是,我认为它不符合标准,因为它boost::counting_iterator是一个存储迭代器,因此不符合Cpp17ForwardIterator的要求.

是否可以像OpenMP一样简洁地编写上述代码,同时满足并行算法标准的约束条件?

h22*_*h22 0

如果我的算法正确,我们必须

  • 迭代结构向量中所有可能的值对,前提是 KeyObjPtrVec向量,
  • 但没有将对象与其自身配对
  • 并且成对成员的顺序并不重要
  • 然后,如果Key结构体的字段匹配,我们必须调用Calculate结构Obj体的字段。

C++17 具有出色的内置并行化支持,这可以通过标准库轻松完成:

#include <algorithm>
#include <execution>

void Calculate(std::vector<TKeyObjPtr> const &KeyObjPtrVec) {
  std::vector<size_t> indices(KeyObjPtrVec.size());
  for (size_t k = 0; k < indices.size(); k++) {
    indices[k] = k;
  }

  std::for_each(std::execution::par, indices.begin(), indices.end(),
      [KeyObjPtrVec, indices](size_t k) {
        std::for_each(std::execution::par, indices.begin() + k + 1, indices.end(),
            [k, KeyObjPtrVec](size_t l) {
              if (KeyObjPtrVec[k].Key == KeyObjPtrVec[l].Key) {
                KeyObjPtrVec[k].ObjPtr->Calculate(KeyObjPtrVec[l]);
              }
            });
      });
}
Run Code Online (Sandbox Code Playgroud)

如果您指定std::execution::par为 的第一个参数std::for_each,则并行版本运行。无需关心执行程序、线程同步或任何清理 - 所有这些都可以开箱即用。只需链接库tbbtarget_link_libraries(myexecutable tbb)用于 cmake)。

使用std::for_each您可以访问您应该处理的元素,而不是迭代器。为了获得设置第二个所需的更多自由访问权限std::for_each,我们创建索引数组并对其进行迭代。有了元素的索引,我们就可以在两个 lambda 中的任何相对位置访问数组。

该代码经过测试并确认可以按照答案开头所述工作。