openMP - 需要原子或减少子句
我正在使用openMP来并行化几个语句。我正在使用平行于构造。并行化for循环看起来像:openMP - 需要原子或减少子句
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();
}
}
这是平均工作。执行时间显着减少,结果为如预期。然而,不时,尤其是对于大的问题(在我迭代的大数字,数据的大数量的拷贝构造函数调用
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;
}
需要注意的是,由于固有的浮点不准确,这个代码可能产生不同的结果来自顺序代码。这是不可避免的。
同时修改functionEvaluation
在你的代码中肯定是一个问题,处理它的最好方法是reduction
子句。
还有一个问题是,您通过并行调用new
来分配堆内存,这对于许多迭代来说绝不是一个好主意,因为系统在调用new
时存在系统范围锁定。考虑切换到堆栈分配,因为堆栈对每个线程都是私有的,而堆是共享的。
非常感谢! Konrad,我没有看到没有指针的做法:调用拷贝构造函数'Model()'接受一个参数,它是一个指向Model的指针,'duplicate()'接受一个指向Command的指针。是否更多的内存消耗/使用指针的时间,即使我在循环中删除它们? – octoback 2012-04-14 20:03:20
@dlib第一个问题是:为什么这些函数需要指针作为参数?但是,如果这不能改变,那么你可以完全按照你在评论中写的内容来做。在这里使用堆分配对堆栈分配没有任何好处,使事情更加复杂(手动内存管理),并增加了一个小的性能开销。另外,请参阅我的更新回答,我之前忽略了代码中的另一个大(!)错误。 – 2012-04-14 20:06:05
这是没有错误,它的复制粘贴误解(耻辱),我上面修改。我没有看到如何在这里使用堆栈分配。模型(&model_)不适合,因为我想在新实例model_中复制varModel;我不能没有复制构造函数,即与新的。 – octoback 2012-04-14 20:20:03