用于非POD的openmp并行

openmp parallel for with non-PODs

本文关键字:openmp 并行 POD 用于      更新时间:2023-10-16

我正在努力加快一个程序的速度,它的核心是一个看起来很琐碎的循环:

double sum=0.;
#pragma omp parallel for reduction(+:sum) // fails
for( size_t i=0; i<_S.size(); ++i ){
  sum += _S[i].first* R(atoms,_S[i].second) ;
}

虽然循环本身很琐碎,但其中的对象不是POD:这里_S实际上是其中std::vector< std::pair<double, std::vector<size_t> > >R(...)是某个对象的过载operator(...) const。它的两个参数都限定为const,这样调用就不会有任何副作用。

由于大约90%的运行时间都花在了这个调用中,所以如上所示,放入一个OpenMP杂注并享受两到三倍的加速似乎是一件简单的事情;但当然——代码在单个线程中工作正常,但在多个线程中给出了明显错误的结果:-)。

不存在数据依赖关系,_SR(...)似乎都可以在线程之间安全共享,但它仍然会产生无意义的内容。

我真的很感激任何关于如何发现问题的建议。

UPD2:

想明白了。和所有的bug一样,这是微不足道的。R(...)调用的是某种类型的operator()

class objR{
 public:
   objR(const size_t N){
     _buffer.reserve(N);
   };
   double operator(...) const{
     // do something, using the _buffer to store intermediaries
   }
 private:
   std::vector<double> _buffer;
};

很明显,不同的线程同时使用_buffer并将其搞砸。到目前为止,我的解决方案是分配更多的空间(内存不是问题,代码受CPU限制):

class objR{
 public:
   objR(const size_t N){
     int nth=1;
     #ifdef _OPENMP
       nth=omp_get_max_threads();
     #endif
     _buffer.resize(N);
   }
   double operator(...) const{
     int thread_id=0;
     #ifdef _OPENMP
       thread_id = omp_get_thread_num();
     #endif
     // do something, using the _buffer[thread_id] to store intermediaries
   }
 private:
   std::vector< std::vector<double> > _buffer;
};

这似乎是正确的。尽管如此,由于这是我第一次涉足多线程领域,如果有知识渊博的人能评论是否有更好的方法,我将不胜感激。

访问_S[i].first_S[i].second是完全安全的(不能保证atoms的任何内容)。这意味着您对R的函数调用一定是导致问题的原因。你需要了解R是什么,并发布它在做什么。

在另一点上,以下划线和大写字符开头的名称是为实现保留的,您可以通过使用它们来调用未定义的行为。