昨天我用大数据集(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;
}
答案 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 值添加到它的所有子节点,而不是遍历整个子树并只添加它自己的值。
对于一个简单的顺序算法,您可以继续使用一个节点列表迭代地实现它,这些节点的前驱已全部被评估(因此不是从根节点开始从叶节点开始)。这里最简单的并行实现是使用并发队列和原子添加操作,尽管每个处理器使用一个普通队列,并且在实践中可能是一个非常好的工作。