如何提高Boost Fibonacci Heap的性能

时间:2014-02-15 13:53:10

标签: c++ boost fibonacci-heap

我正在实施Fast Marching算法,这是一种连续的Dijkstra。正如我在许多论文中所读到的,Fibonacci堆是用于此目的的最合适的堆。

但是,在使用callgrind我的代码进行分析时,我发现以下函数占用了58%的执行时间:

int popMinIdx () {
    const int idx = heap_.top()->getIndex();
    heap_.pop();
    return idx; 
}

具体而言,pop()占整个执行时间的57.67%。

heap_定义如下:

boost::heap::fibonacci_heap<const FMCell *, boost::heap::compare<compare_cells>> heap_;

“需要多长时间”是否正常,或者我可以采取哪些措施来提高性能?

很抱歉,如果没有提供足够的信息。我试着尽可能简短。如果需要,我会添加更多信息。

谢谢!

2 个答案:

答案 0 :(得分:3)

其他答案并未提及大部分内容:当然pop()占用了大部分时间:它是执行任何实际工作的唯一功能!

正如您可能已经读过的,斐波那契堆运算的界限是摊销边界。这意味着如果以良好的顺序执行足够的操作,则界限将平均为此。但是,实际成本是完全隐藏的。

每次插入元素时,都不会发生任何事情。它只是被扔到根列表中。热潮,O(1)时间。每次合并两棵树时,它的根只会链接到根列表中。热潮,O(1)时间。但坚持下去,你的结构不是有效的斐波那契堆! pop()(或extract-root)的来源:每次调用此操作时,整个堆都会重新构建回正确的形状。删除Root,将其子项切换到根列表,然后我们开始合并根列表中的树,以便根列表中不存在具有相同度数(子项数)的两棵树。

所以插入(e)和Merge(t)的所有工作实际上都被延迟,直到调用Pop(),然后执行所有工作。其他操作怎么样?

删除(e)很漂亮。我们执行Decrease-Key(e,-inf)使元素e成为根。现在我们执行Pop()!同样,这项工作由Pop()完成。

Decrease-Key(e,v)自己完成它的工作:它将e切割到根列表并开始切割级联,以便将其子项放入根列表中(这也可以删除它们的子列表)。所以Decrease-Key将很多元素放入根列表中。你能猜出哪个功能必须解决这个问题吗?

TL; DR:Pop()是Fibonacci Heap的工作马。所有其他操作都有效地完成,因为它们为Pop()操作创建了工作。 Pop()收集工作并一次执行(最多可能需要O(n))。这实际上非常有效,因为&#34;分组&#34;工作可以比单独的每个操作更快地完成。

所以是的,Pop()占用你大部分时间是很自然的!

答案 1 :(得分:1)

Fibanacci Heap的pop()具有O(log n)的分摊运行时间和O(n)的最差情况。如果你的堆很大,它可能很容易消耗你的算法中的大部分CPU时间,特别是因为你可能使用的大多数其他操作都有O(1)运行时(插入,顶部等)

我建议使用调试信息(-g)尝试使用首选优化级别(例如-O3)的callgrind,因为模板化的数据结构/容器(如fibonacci_heap)对内联函数的使用很重要。可能是您测量的大多数CPU周期甚至都不存在于优化的可执行文件中。