0

我正在使用 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 的副本(duplicate 大致是一个复制构造函数)。

我发现的代码中可能存在的缺陷是

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

  • 语句中的复制构造函数

    模型_ = 新模型(varModel_);

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

另外,我只看到了两项改进(我要几天才能测试,但无论如何我征求意见):

  • 添加 atomic 子句,使得 functionEvalution 不会同时写入

  • 添加 operator reduction(+,functionEvaluation),以便自动处理有关访问 functionEvaluation 的并发

这些解决方案是否似乎准确地解决了问题,并且总体上哪个更有效?我写的代码哪里有问题?什么是解决方案?

非常感谢!

4

2 回答 2

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;
}

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

于 2012-04-14T19:49:29.967 回答
0

并发修改functionEvaluation绝对是您的代码中的一个问题,处理它的最佳方法是reduction子句。

另一个问题是,您通过new并行调用来分配堆内存,这对于许多迭代来说从来都不是一个好主意,因为调用new. 考虑切换到堆栈分配,因为堆栈是每个线程私有的,而堆是共享的。

于 2012-04-14T19:59:18.230 回答