openMP - 需要原子或还原子句

时间:2012-04-14 19:17:11

标签: c++ multithreading multiprocessing openmp

我使用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_是私有的,因此每个线程处理每个线程的副本。在变量model_中,我复制了varModel_,这样在参数中传递的指针不会被线程改变。同样,command_是成员变量formulaCommand的副本(复制是一个复制构造函数)。

我确定的代码中存在的可能缺陷是

  • functionEvaluation可能会被多个线程同时修改

  • 在语句

    中复制构造函数

    model_ = new Model(varModel _);

读取内存中varModel_的成员以构造新的(model_)实例。可能会发生对varModel_数据成员的并发访问,虽然这不是在这里改变它们的值,而只是读取它们(将它们影响到其他变量)。

另外,我只看到两项改进(直到几天我都无法测试,但无论如何我都会求助):

  • 添加原子句,以便函数评估不会同时写入

  • 添加运算符缩减(+,functionEvaluation),以便自动处理有关访问functionEvaluation的并发性

这些解决方案是否能够准确地解决问题并且总体上更有效?我写的代码问题出在哪里?什么是解决方案?

非常感谢!

2 个答案:

答案 0 :(得分:1)

第一个观察结果是,正如您已经注意到的那样,同时修改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;
}

请注意,由于固有的浮点不准确性,此代码可能会从顺序代码中产生不同的结果。这是不可避免的。

答案 1 :(得分:0)

同时修改functionEvaluation肯定是代码中的问题,处理它的最佳方法是reduction子句。

另一个问题是,您通过并行调用new来分配堆内存,这对于许多迭代来说绝不是一个好主意,因为对new的调用存在系统范围的锁定。考虑切换到堆栈分配,因为堆栈是每个线程专用的,而堆是共享的。