基于Heap的优先级队列导致Haskell中的大量垃圾收集

时间:2017-07-03 15:33:59

标签: haskell

我正在尝试在Haskell中开发一个启发式搜索库,并且使算法以某种方式表现的主要因素之一是数据结构;所以我宣布了这个课程:

class DataStructure ds where  
  add :: Eq a => Node a -> ds a -> ds a
  addList :: Eq a => [Node a] -> ds a -> ds a
  next :: Eq a => ds a -> (ds a, Node a)
  isEmpty :: Eq a => ds a -> Bool

然后,这个类的实例用于扩展节点的通用搜索算法:

generalSearch :: (DataStructure ds, Eq a, Hashable a)
  => ProblemSpace a   -- ^ 'ProblemSpace' to be solved
  -> Cost a           -- ^ 'Cost' function to use
  -> Heuristic a      -- ^ 'Heuristic' function to use
  -> ds a             -- ^ 'DataStructure' that manages the node expansion
  -> [Node a]         -- ^ Returns the infinite list of final nodes (solutions)

generalSearch problem g h open
  | isEmpty open                    = []
  | getGoalF problem (getState n)   = n : generalSearch problem g h open'
  | otherwise                       = generalSearch problem g h open''
  where (open', n) = next open
        expanded = expand n g h (getActions problem)
        open'' = addList expanded open'

如果没有模块的完整上下文,最后一段代码很难理解,但重要的部分是最后一行:它使用DataStructure方法addList将刚刚合并到一起扩展节点与执行所携带的其余打开列表。

有些算法要求打开列表以某种方式排序:升序成本,升序启发式...我使用heap包来构建PriorityQueue实现,如下所示: / p>

data PriorityQueue a =
  PriorityQueue { priorityQueueToHeap :: MinPrioHeap Double (Node a)
                , valueFunction :: Node a -> Double }

因此,此队列按照valueFunction按升序存储节点。此问题的相关方法addList的实现方式如下:

addList :: Eq a => [Node a] -> PriorityQueue a -> PriorityQueue a
addList xs (PriorityQueue h f) = PriorityQueue (union h xs') f
  where xs' = fromList $ map (\n -> (f n, n)) xs

由于我对Haskell的经验不足,这似乎是一个很好的方法,但事实证明,在测试它时,执行速度非常慢。在进行了一些分析之后,我发现由于垃圾收集而导致问题停顿了很多,成本中心发现union中的addList方法是时间上最昂贵的({I}}可以理解,由于排序),但也在空间(这是最远的分配)。

COST CENTRE   MODULE             SRC                                     %time %alloc

union         Data.Heap.Internal Data/Heap/Internal.hs:(134,1)-(141,79)   42.8   32.7
visited       Search.General     src/Search/General.hs:77:43-53           14.6    0.0
expand        Search.General     src/Search/General.hs:(70,40)-(76,78)    13.6   14.0
cost          Search.General     src/Search/General.hs:74:70-81            7.8    7.9
generalSearch Search.General     src/Search/General.hs:(52,1)-(58,37)      6.7   13.6
fromList      Data.Heap          Data/Heap.hs:137:1-34                     4.7   11.7
size          Data.Heap.Internal Data/Heap/Internal.hs:(98,1)-(99,23)      3.4    8.3
rank          Data.Heap.Internal Data/Heap/Internal.hs:(93,1)-(94,23)      2.7    8.0
expand        Search.General     src/Search/General.hs:(69,1)-(77,53)      1.4    3.4

时间问题似乎只在开放节点列表很大时才会出现,所以我猜它与堆的不可变性有关,在每次新扩展中都会被写入和删除。有没有办法防止这种行为,并减少垃圾收集?我应该使用不同类型的数据结构来构建优先级队列吗?

0 个答案:

没有答案