c++ OpenMP/__gnu_parallel用于无序Map

elcex8rz  于 2023-03-09  发布在  其他
关注(0)|答案(4)|浏览(209)

在我的代码中,我必须对unordered_map中的所有元素进行操作,为了加速这个过程,我想使用OpenMP,但是这种简单的方法不起作用:

std::unordered_map<size_t, double> hastTable;

#pragma omp for
for(auto it = hastTable.begin();
    it != hastTable.end();
    it ++){
//do something
}

这是因为unordered_map的迭代器不是随机访问迭代器,作为一种替代方法,我尝试了__gnu_parallel指令来处理for_each,但是下面的代码

#include <parallel/algorithm>
#include <omp.h>

__gnu_parallel::for_each (hashTable.begin(), hashTable.end(),[](std::pair<const size_t, double> & item)
                        {
                          //do something with item.secon
                        });

汇编依据(gcc 4.8.2)

g++ -fopenmp -march=native -std=c++11

使用向量切换unordered_map并使用相同的__gnu_parallel指令是并行运行的。
在无序Map的情况下,为什么不并行运行?有解决方法吗?
在下面我给予你一些简单的代码,它重现了我的问题。

#include <unordered_map>
#include <parallel/algorithm>
#include <omp.h>

int main(){

//unordered_map                                                                                                                                      
std::unordered_map<size_t, double> hashTable;
double val = 1.;
for(size_t i = 0; i<100000000; i++){
  hashTable.emplace(i, val);
  val += 1.;
}
__gnu_parallel::for_each (hashTable.begin(), hashTable.end(),[](std::pair<const size_t, double> & item)
                        {
                          item.second *= 2.;
                        });

//vector                                                                                                                                             
std::vector<double> simpleVector;
val = 1.;
for(size_t i = 0; i<100000000; i++){
  simpleVector.push_back(val);
  val += 1.;
}
__gnu_parallel::for_each (simpleVector.begin(), simpleVector.end(),[](double & item)
                        {
                          item *= 2.;
                        });

}

我期待着你的答案。

92vpleto

92vpleto1#

对于不支持随机迭代器的容器,规范方法是使用显式OpenMP任务:

std::unordered_map<size_t, double> hastTable;

#pragma omp parallel
{
   #pragma omp single
   {
      for(auto it = hastTable.begin(); it != hastTable.end(); it++) {
         #pragma omp task
         {
            //do something
         }
      }
   }
}

这为每个迭代创建了一个单独的任务,这带来了一些开销,因此只有当//do something实际上意味着//do quite a bit of work时才有意义。

7cwmlq89

7cwmlq892#

您可以在bucket索引的范围内拆分循环,然后创建一个bucket内迭代器来处理元素。unordered_map.bucket_count()和特定于bucket的迭代器begin(bucket_number)end(bucket_number),它们允许这样做。假设您没有修改1.0的默认max_load_factor(),并且有一个合理的散列函数,您将 * 平均 * 每个桶1个元素,不应该在空桶上浪费太多时间。

mpbci0fu

mpbci0fu3#

您可以通过在unordered_map的存储桶上进行迭代来完成此操作,如下所示:

#include <cmath>
#include <iostream>
#include <unordered_map>

int main(){
  const int N = 10000000;
  std::unordered_map<int, double> mymap(1.5*N);

  //Load up a hash table
  for(int i=0;i<N;i++)
    mymap[i] = i+1;

  #pragma omp parallel for default(none) shared(mymap)
  for(size_t b=0;b<mymap.bucket_count();b++)
  for(auto bi=mymap.begin(b);bi!=mymap.end(b);bi++){
    for(int i=0;i<20;i++)
      bi->second += std::sqrt(std::log(bi->second) + 1);
  }

  std::cout<<mymap.begin()->first<<" "<<mymap.begin()->second<<std::endl;

  return 0;
}
tcomlyy6

tcomlyy64#

std::unordered_map<size_t, BigClass> hastTable;
std::vector<BigClass*> vec;
vec.reserve(hasTable.size());
for(auto it = hastTable.begin(); it != hastTable.end(); it++) 
   vec.push_back(&it->second);
#pragma omp for
for(auto it = vec.begin(); it != vec.end(); it++) {
   //do something
}

对于具有大型类对象的容器,这种方法似乎更容易理解

相关问题