用于嵌套循环的C ++中OpenMP编程的锁定策略

时间:2013-12-07 11:51:50

标签: c++ multithreading openmp nested-loops

昨天我用大数据集(2000万个节点)测试了我的景观演变程序,毫无疑问,运行速度是不可接受的。在调试过程中,我注意到这是一个减慢整个系统速度的特定功能。所以我想为它添加多线程处理。

但是,函数本身是一个带指针迭代器的嵌套循环,我相信在这个过程中必须锁定一些数据。

基本上我试图做的是计算每个节点的贡献区域。如其名称所示,贡献区域是来自上游节点的所有区域的产品(总和)。

以下是这两个函数的代码,

for( curnode = nodIter.FirstP(); nodIter.IsActive();
    curnode = nodIter.NextP() )  //iterating thru a linked-list of pointers to active stream nodes (*IsActive* returns a  bool value)
{
  CalcDRArea( curnode, curnode->getVArea() ); //calls another function and pass a pointer to current node  as well as a value of its VArea
}

void CalcDRArea( NodeClass *curnode, double addedArea )
{
  // As long as the current node is neither a boundary nor non-valid, add
  // _addedArea_ to its total drainage area and advance to the next node downstream

  while( (curnode->ReachBoundary() == NonBoundary) &&
        (curnode->valid()!=Nonvalid) )
  {
       curnode->AddDrArea( addedArea );  //*AddDrArea* is a simple inline function *`"drarea +=value"`*

    curnode = curnode->getDownStreammNeighbor();      // *getDownstrmNbr()* reruns a pointer to the downstream node
  }

}

以下是节点及其流向的简单说明

A     B     C     D  
   \  |  /        |
E     F     G     H
      |           |
I     Q     K     L
      |        / 
M     N     O     P
//letters are stream nodes and slashes are their flowing directions

我的计划是使用OpenMP在第一个函数的开头实现多线程, for loop 。理想情况下,它将创建多个线程来分别计算每个节点。

但是,如上图所示,后续流程可以处理像

这样的流
A-> F -> Q -> N 
B-> F -> Q -> N 
C-> F -> Q -> N 

很容易,但在多线程条件下肯定会引起问题。

从我刚刚从OpenMP的文档中读到的内容, flush lock 可能是正确的方法,但是我现在仍然很无能为力可能仍然是这个循环中的其他潜在问题(如OpenMP的gcc版本不支持“!=”)。

====更新====== 有两种区域:vArea,即每个节点的区域;和drArea,它是当前节点的区域和来自其所有上游节点的区域的总和。 我想知道我是否可以将当前功能更改为:

for( active node iterator)  
{
    if(currentNode.hasDownStreamNode)
    {
       downStreamNode.drArea += currentNode.vArea + currentNode.DrArea;
    }
    CurrentNode.drArea += currentNode.varea;
}

1 个答案:

答案 0 :(得分:1)

在担心并行性之前,首先应该选择更好的算法。虽然在这种情况下,一个实际上与另一个相同。

您需要O(N)中的动态编程解决方案,而不是当前的O(n ^ 2)方法。直觉很简单,只需在树中的每个叶节点上调用以下方法:

def compute_value(node):
     if node.DrArea != 0: return node.DrArea
     total = node.vArea
     for n in node.upstream_nodes():
          total += compute_value(n)
     node.DrArea = total
     return node.DrArea

要了解为什么效率更高,让我们来看看你的例子。此时,您将A的值添加到F,Q和N.然后,对B和C执行相同的操作。因此,您有12个添加操作。 另一方面,递归方法首先计算A,B和C的值,然后我们从已知的A,B和C值计算F.Q从F计算,依此类推。所以这只有8个补充。基本上每个节点只将 total 值添加到它的所有子节点,而不是遍历整个子树并只添加它自己的值。

对于一个简单的顺序算法,您可以继续使用一个节点列表迭代地实现它,这些节点的前驱已全部被评估(因此不是从根节点开始从叶节点开始)。这里最简单的并行实现是使用并发队列和原子添加操作,尽管每个处理器使用一个普通队列,并且在实践中可能是一个非常好的工作。