2013-04-23 89 views
6

我试图并行在下面的函数循环使用OpenMPOpenMP的减少与重载运营商

void CEnergymulti::forcetwobody(vector<CMolecule*> m_mols,CPnt force0,CPnt torque0) 
{ 
const int nmol=m_mols.size(); 
vector<CMolecule*> twomols(2); 
CPnt forcetemp,torquetemp; 
twomols.clear(); 
force0.zero(); 
torque0.zero(); 
forcetemp.zero(); 
torquetemp.zero(); 
#pragma omp parallel for reduction(+:force0,torque0) private(twomols) 
for(int j=1;j<nmol;j++) 
     { twomols.push_back(m_mols[0]); 
     twomols.push_back(m_mols[j]); 
     CMolecule::polarize_mutual(twomols,false, 1000); 
     twomols[0]->computeMol_Force_and_Torque(forcetemp,torquetemp); 
     force0+=forcetemp; 
     torque0+=torquetemp; 
     forcetemp.zero(); 
     torquetemp.zero(); 
     twomols.clear(); 
     } 
    REAL converter=COUL_K*IKbT; 
    force0*=converter; 
    torque0*=converter; 
    return; 
    } 

当我编译的代码,它提供了以下消息:

EnergyD_multi.cpp: In static member function ‘static void 
CEnergymulti::forcetwobody(std::vector<CMolecule*, 
std::allocator<CMolecule*> >, CPnt, CPnt)’: EnergyD_multi.cpp:226: 
error: ‘torque0’ has invalid type for ‘reduction’ 
EnergyD_multi.cpp:226: error: ‘force0’ has invalid type for 
‘reduction’ 

我明白变量'force0'和'torque0'既不是双重数据也不是整型数据,而是'CPnt'类型,这是一种定义为表示空间中三维向量的类。对于'CPnt'类,运算符'+'和' - '已经由运算符重载定义。所以我的问题是:在OpenMP中的减少不能处理这样的重载操作符吗?有没有其他方法可以将这个循环与OpenMP并行化,而不需要减少'force0'和'torque0'的每个组件?

非常感谢。

+0

您是否得到了应有的工作?我更改了代码以使用OpenMP执行过载操作。让我知道它是否有效。如果有错误,请参阅链接以了解主要思想。 – 2013-04-25 19:32:24

+0

非常感谢@raxman。使用并行化方案,您建议可以编译代码。不过,我仍然对与'nowait'的循环并行化有点困惑。这是因为我仍然认为对于每个线程来说,循环'twomols'和'forcetemp'中的变量应该被视为'私人',但是您没有将它们列为私人。 – user2226358 2013-04-26 06:35:55

+0

“#pragma omp parallel”中的所有内容都是私人定义的。只有force0和torque0是共享的,因为它们不在omp pragma – 2013-04-26 06:40:14

回答

7

的确,OpenMP减少无法处理这样的重载操作符。但是,还有一个选择。重写OpenMP减少的一种方法是使用nowaitatomic参数。 http://bisqwit.iki.fi/story/howto/openmp/#ReductionClause 。这与正常方式一样快。

如果将atomic替换为critical,则可以使用更复杂的重载操作符。这并不像使用atomic那么快,但它仍然适用于我的经验。

我这样做,所以我可以使用运算符一次操作4或8浮动(与SEE或AVX)。 reduction with OpenMP with SSE/AVX

编辑:我改变了你的代码,以反映我认为会做你想做的事。

void CEnergymulti::forcetwobody(vector<CMolecule*> m_mols,CPnt force0,CPnt torque0) 
{ 
    const int nmol=m_mols.size(); 
    force0.zero(); 
    torque0.zero(); 
    #pragma omp parallel 
    { 
     CPnt force0_private; 
     CPnt torque0_private; 
     force0_private.clear(); 
     torque0_private.clear(); 
     #pragma omp for nowait 
     for(int j=1;j<nmol;j++) 
     { 
      CPnt forcetemp,torquetemp; 
      forcetemp.zero(); 
      torquetemp.zero(); 
      vector<CMolecule*> twomols(2); 
      twomols.clear(); 
      twomols.push_back(m_mols[0]); 
      twomols.push_back(m_mols[j]); 
      CMolecule::polarize_mutual(twomols,false, 1000); 
      twomols[0]->computeMol_Force_and_Torque(forcetemp,torquetemp); 
      force0_private+=forcetemp; 
      torque0_private+=torquetemp; 
     } 
     #pragma omp critical 
     { 
      force0 += force0_private; 
      torque0 += torque0_private; 
     } 

    } 
    REAL converter=COUL_K*IKbT; 
    force0*=converter; 
    torque0*=converter; 
    return; 
} 
+0

我认为第二个编译指示(即'#pragma omp parallel for nowait')应该是'#pragma omp for nowait',即没有并行(并行结构已经在其外部设置)。我的编译器抱怨说,你不能在parallel中加入nowait,因为在parallel的后面应该有一个隐含的障碍。似乎nowait和parallel是矛盾的指令。 – 2013-08-22 23:02:46

+0

@VarunGulshan,你是对的,它应该是'#pragma omp for nowait'。我只是修复它。 – 2014-08-04 07:32:53

+0

Arent你失去这样的对数复杂性? --- 最糟糕的情况是每个线程都等待进入关键部分,然后他们会连续添加结果。 --- 所以最后你有'O(nmol/thread_num)+ O(thread_num)'而不是'O(nmol/thread_num)+ O(log(thread_num))' --- 这可能会使强大的电脑(即超级计算机) – Jan 2017-02-17 07:05:32