【问题标题】:OMP and parallel operations on a std::set<...>::iteratorstd::set<...>::iterator 上的 OMP 和并行操作
【发布时间】:2018-04-09 21:46:10
【问题描述】:

给定一个基于 ma​​p 的数据结构,如下所示:

std::map<int, std::set<std::vector<int>>> cliques;

表示其中包含的向量的大小

一开始,ma​​p只有一个键(例如[3]),其中包含输入向量(例如{1, 3, 5}{2, 4, 6})。

我的函数把ma​​pbiggest key中存储的vectors分解成所有的可能具有较少元素的组合,并将它们存储在与新向量的大小相对应的中(例如[2] = {1,3} {1,5} {3,5} {2,4} {2,6} {4,6} and [1] = {1} {3} {5} {2} {4} {6})。

我不知道我的解决方案是否最有效,但效果很好。但由于我的项目旨在处理大量数据,我需要并行化我的代码,这导致我进行了以下实现:

/// Declare data structure
std::map<int, std::set<std::vector<int>>> cliques;

/// insert "input" vectors
cliques[5] = {{1, 3, 5, 7, 9}};
cliques[4] = {{1, 2, 3, 4}};

/// set boundaries
int kMax = 5;
int kMin = 1;

/// enable/disable parallel execution
bool parallelExec = true;

/// "decompose" source vectors:
for (int k = kMax; k > kMin; k--)
{
  std::set<std::vector<int>>::iterator it = cliques[k].begin();
  #pragma omp parallel num_threads(max_threads) if(parallelExec)
  {
    for(int s = 0; s < cliques[k].size(); ++s)
    {
      std::vector<int> clique;
      /// maybe should be "omp critical"?
      /// maybe "clique" should be private? (or is it already??) 
      #pragma omp single
      { 
        clique = *it;
      }
      for (int v = 0; v < clique.size(); ++v)
      {
        int& vertex = clique[v];
        std::vector<int> new_clique;
        std::copy_if(clique.begin(), clique.end(), std::back_inserter(new_clique), [vertex](const int& elem) { return elem != vertex; });
        int kNew = k - 1;
        #pragma omp critical
        { 
          cliques[kNew].insert(new_clique);
        }
      }
      #pragma omp single
      {
        it++;
      }
    }
  }
}

/// Display results
for(int i = cliques.size(); i > 0 ; i--)
{
    auto kSet = cliques[i];
    std::cout << "[" << i << "]: ";  
    for(auto& vec : kSet)
    {
        std::cout << "{";
        for(auto& elem : vec)
        {
            std::cout << elem << " ";  
        }
        std::cout << "} ";
    }
    std::cout << std::endl;
}

使用 “omp parallel”“omp single”(而不是“omp for”)允许安全地访问数据结构,同时允许所有其他操作并行运行。该代码几乎完美地工作,几乎......因为它在最终结果中遗漏了一些(非常少的)子向量(如果禁用 omp,则会成功生成这些子向量)。

有没有“OMP 专家”可以帮助我解决这个问题?提前谢谢你。

---------------

更新

Minimal Complete Verifiable Example

【问题讨论】:

  • 标准库容器不支持并发访问,尤其是std::set。查找支持并行访问的 C++ 容器库 - 或创建单独的集合,然后合并它们。
  • @einpoklum stl 容器(向量和队列除外)不支持并发访问,但我阅读了一些示例,其中可以在“omp 并行”区域内使用“omp single”完成(顺序)访问而不是“omp for”同时执行所有其他操作,这就是我尝试的全部内容。

标签: c++ openmp stdmap clique-problem


【解决方案1】:

我不确定我是否理解您算法的所有细节,因此我不能完全确定我的分析。该免责声明说,这就是我认为会发生的事情:

  1. 您没有并行化处理:您不会在线程之间分配工作,您只是在所有线程上复制相同的工作,这些线程会相互影响以将结果写回相同的位置。
  2. 即使由于迭代器的增量(使用omp single nowait 完成)没有正确完成,也允许线程在前一次迭代上工作,因为在此阶段没有对it 的值执行同步。 (注意:没有nowaitomp single 在退出时保护迭代器的增量有一个隐含的barrier 确保该值的线程一致视图,因此差异只能在当前迭代和前一个迭代之间)
  3. 这个cliques[kNew].insert(new_clique); 真的是所有东西都可能爆炸的地方,因为对同一位置的访问是并发的,标准容器不支持这一点。 (无论如何这是错误的,在我的理解范围内)

同样,请记住我最初的免责声明,但我认为你的算法本质上是非常错误的,原因有很多,而且它只是偶然地给出了接近你期望的东西。

最后,我正要向您推荐我的算法,但由于您的代码 sn-p 缺少很多部分,所以我不能。 如果您发布正确的mcve,那么也许我会。

更新 根据您的代码,这是一个可能的并行版本:

for (int k = kMax; k > kMin; k--)
{
    std::set<std::vector<int>>::iterator it = cliques[k].begin();
    for(int s = 0; s < cliques[k].size(); ++s)
    {
        std::vector<int> clique = *it;
        #pragma omp parallel for num_threads(max_threads)
        for (int v = 0; v < clique.size(); ++v)
        {
            int& vertex = clique[v];
            std::vector<int> new_clique;
            std::copy_if(clique.begin(), clique.end(), std::back_inserter(new_clique), [vertex](const int& elem) { return elem != vertex; });
            int kNew = k - 1;
            #pragma omp critical
            cliques[kNew].insert(new_clique);
        }
        it++;
    }
}

【讨论】:

  • 嗨@Gilles,感谢您的宝贵时间。问题不在于算法本身,我已经针对我的案例进行了测试并有效地工作,我只想尝试以并行方式运行它。关于 OMP,据我了解(如果我错了,请原谅,但我是 OMP 新手),在使用“omp 并行”创建并行区域之后,所有操作都是并行的,除了包含为“omp single”的操作。您可以在以下链接中找到 MCVE(请检查并告诉我应该如何并行化它):rextester.com/GGY82679
  • 好吧,1/ 你应该用你的代码更新你的帖子,而不是喜欢外部链接;和 2/ 你的算法看起来像它在迭代之间继承了依赖关系,所以在当前格式下并行化它是不可能的......
  • 事实上我不想在地图的键上并行化迭代(较低的键依赖于较大的键),我想在每个键包含的集合内执行它:每个线程都应该工作集合中包含的每个(独立)向量,并通过从向量中删除一个元素来生成子向量。你觉得可行吗?
  • 很好的简化,而且效果很好,途中没有子向量丢失。如果使循环与 OMP 一起工作是一个经验问题,我认为这是一个关于如何安排变量的好方法。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-29
  • 1970-01-01
  • 1970-01-01
  • 2013-12-13
  • 1970-01-01
相关资源
最近更新 更多