我正在使用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;
}


请注意,由于固有的浮点错误,此代码可能会产生与顺序代码不同的结果。这是不可避免的。

10-07 12:07