我正在使用 openMP 并行化一些语句。我正在使用 parallel for 结构。并行化的 for 循环如下所示:
double solverFunction::apply(double* parameters_ , Model* varModel_)
{
double functionEvaluation = 0;
Command* command_ = 0;
Model* model_ = 0;
#pragma omp parallel for shared (functionEvaluation) private (model_,command_)
for (int i=rowStart;i<rowEnd+1;i++)
{
model_ = new Model(varModel_);
model_->addVariable("i", i);
model_->addVariable("j", 1);
command_ = formulaCommand->duplicate(model_);
functionEvaluation += command_->execute().toDouble();
}
}
平均而言,它很有效。执行时间显着减少,结果如预期。然而,有时,特别是对于大问题(i 上的大量迭代,在复制构造函数调用中要复制的大量数据
model_ = new Model(varModel_);
,其他人?),它崩溃了。调用堆栈以 qAtomicBasic(它是用 C++/Qt 编写的程序)、QHash 等类结束,我认为它会因为内存中的并发读/写访问而崩溃。
但是,model_ 和 command_ 是私有(private)的,因此每个线程都处理各自的拷贝。在变量 model_ 中,我复制了 varModel_,这样传入参数的指针就不会被线程更改。同样,command_ 是成员变量 formulaCommand 的拷贝(duplicate 大致是一个复制构造函数)。
我发现我的代码中可能存在的缺陷是
functionEvaluation 可以被多个线程同时修改
在语句中复制构造函数
model_ = new Model(varModel_);
读取内存中 varModel_ 的成员以构造新的 (model_) 实例。可能会发生对 varModel_ 数据成员的并发访问,尽管这不是在此处更改它们的值,而只是读取它们(将它们影响到其他变量)。
另外,我只看到两个改进(我要过几天才能测试,但我还是征求意见):
添加原子子句,使functionEvalution不并发写入
添加运算符缩减(+,functionEvaluation),以便自动处理有关访问 functionEvaluation 的并发
这些解决方案是否似乎准确地解决了问题?总体而言,哪个更有效?我写的代码哪里有问题?什么是解决方案?
非常感谢!
最佳答案
第一个观察是,正如您自己注意到的那样,同时修改 functionEvaluation
不是一个好主意。它将失败。
另一方面,varModel_
的只读访问不是问题。复制构造函数调用也没有(但它在哪里?您的代码没有显示它)。
与此无关,在 C++ 中使用 private
子句是个坏主意。只需在并行 block (在本例中为 for
循环) 内声明线程私有(private)变量。
我也不明白你为什么在这里使用指针。它们的使用没有立即意义——而是使用堆栈分配的对象。
以下修改后的代码应该可以工作(我也冒昧地统一了编码风格……为什么尾随下划线?):
double solverFunction::apply(double parameters, Model const& varModel)
{
double result = 0;
#pragma omp parallel for reduction(+:result)
for (int i = rowStart; i < rowEnd + 1; ++i)
{
Model model(varModel);
mode.addVariable("i", i);
mode.addVariable("j", i);
Command command = formulaCommand->duplicate(model);
result += command.execute().toDouble();
}
return result;
}
请注意,由于固有的浮点不准确性,此代码可能会产生与顺序代码不同的结果。这是不可避免的。
关于c++ - openMP - 需要原子或减少条款,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10156564/