我正在尝试加速一个程序,该程序的核心是一个看起来很普通的循环:
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 pragma 放入如上所示并享受两到三倍的加速似乎是一件简单的事情; 但当然---代码在单个线程下工作正常,但对于多个线程给出明显错误的结果:-)。
没有数据依赖,两者都是_S
和 R(...)
在线程之间共享似乎是安全的,但它仍然会产生废话。
我非常感谢任何关于如何找出问题所在的指示。
UPD2:
想通了。与所有错误一样,它是微不足道的。 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
是绝对安全的(不能保证任何关于 atom
).这意味着您对 R
的函数调用一定是导致问题的原因。您需要了解什么是 R
并发布它的作用。
另一方面,以下划线开头和大写字符开头的名称保留用于实现,您可以通过使用它们来调用未定义的行为。
关于c++ - openmp parallel for with non-PODs,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6285715/