我正在使用openMP并行化一些语句。我正在使用并行进行构造。并行化的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_是私有的,因此每个线程都处理每个副本。在变量model_中,我复制了varModel_,以便传入参数的指针不会被线程更改。类似地,command_是成员变量FormulaCommand的副本(重复的副本通常是副本构造函数)。
我确定的代码中可能存在的缺陷是
functionEvaluation可以同时由多个线程修改
在语句中复制构造函数
model_ =新模型(varModel_);
读取内存中varModel_的成员以构造新的(model_)实例。可以同时访问varModel_数据成员,尽管这并不是要更改此处的值,而只是读取它们(将它们影响到其他变量)。
此外,我仅看到两项改进(我几天后才能测试,但无论如何我都寻求建议):
添加原子子句,以便不会同时写入functionEvalution
添加运算符减少(+,functionEvaluation),以便自动处理有关访问functionEvaluation的并发性
这些解决方案似乎正确地解决了问题,并且总体上更有效吗?问题出在哪里与我编写的代码有关?有什么解决方案?
非常感谢!
最佳答案
最初的发现是,正如您所知,同时修改functionEvaluation
是个坏主意。它会失败。
另一方面,varModel_
的只读访问权限不是问题。复制构造函数调用也不是(但是它在哪里?您的代码未显示)。
无关地,在C ++中使用private
子句是一个坏主意。只需在并行块内声明线程专用变量(在本例中为for
循环)。
我也看不到您为什么在这里使用指针。它们的使用没有直接意义-而是使用堆栈分配的对象。
修改后的以下代码应该可以正常工作(我还自由地统一了编码风格,为什么后面加了下划线?):
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;
}
请注意,由于固有的浮点错误,此代码可能会产生与顺序代码不同的结果。这是不可避免的。