【问题标题】:Lock strategy of OpenMP programming in C++ for nested loopsC++ OpenMP 编程中嵌套循环的锁定策略
【发布时间】:2013-12-07 11:51:49
【问题描述】:

昨天我用一个大数据集(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

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

但是,如上图所示,后续进程可以处理类似的流

A-> F -> Q -> N 
B-> F -> Q -> N 
C-> F -> Q -> N 

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

根据我刚刚从 OpenMP 的文档中读到的内容,flushlock 可能是执行此操作的正确方法,但我现在和那里仍然一无所知可能仍然是此循环中的其他潜在问题(例如 OpenMP 的 gcc 版本不支持“!=”)。

====更新====== 面积有两种:vArea,即每个节点的面积; drArea 是当前节点的面积与其所有上游节点的面积之和。 我想知道是否可以将当前功能更改为:

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

【问题讨论】:

  • 这似乎很容易适用于动态编程——在尝试多线程之前使用更好的算法通常不是一个坏主意。您基本上是在 O(n^2) 而不是 O(n) 中执行此操作。
  • 你能给我一些关于更好算法的建议吗?我确实意识到这是一个 O(n*n) 并且不知道如何使它更简单。谢谢。
  • 这取决于您的算法究竟是如何工作的,因为从给定的代码中并不清楚,但基本思想是F 的值为A+B+C+F,而Q 的值为@987654328 @ 等等。因此,要计算一个节点的值,您只需查看其所有前任 一次 的已计算值,而不是从每个起始节点遍历所有节点。

标签: c++ multithreading openmp nested-loops


【解决方案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 值添加到其所有子节点,而不是遍历整个子树并仅添加自己的值。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-27
    • 2022-11-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多