并行树搜索

时间:2013-06-26 14:14:14

标签: haskell parallel-processing

假设我有一个懒惰的Tree,其叶子可能是解决问题的方法

data Tree a = Node [Tree a] | Leaf (Maybe a)

我需要找到只有一个解决方案(或者发现没有)。

我有一台 P 核心机器。从时间和内存效率的考虑,只有并行地搜索 P 不同的分支才有意义。

例如,假设您有四个具有大约相同计算复杂度的分支(对应于 T 秒的CPU时间),并且每个分支都有答案。

如果你在双核机器上真正并行评估所有四个分支,那么它们都将在 2T 秒内完成。

如果您仅评估前两个分支并推迟其他两个分支,那么您将仅在 T 秒内得到答案,同时使用两倍的内存。

我的问题是,是否可以使用任何并行Haskell基础结构(Par monad,并行策略......)来实现这一目标,还是必须使用async之类的低级工具?

2 个答案:

答案 0 :(得分:5)

如果有可用的CPU,则策略和Par monad将仅开始评估新的并行任务,因此在您的示例中,在2核机器上有四个分支,将只评估两个。此外,一旦你得到答案,策略将GC其他任务(尽管可能需要一段时间才能做到这一点)。

但是,如果这两个分支中的每一个都创建了更多任务,那么您可能希望优先考虑较新的任务(即深度优先),但至少策略将优先考虑旧任务。我认为Par monad优先考虑新的(但我必须检查一下),但Par monad会在返回答案之前评估所有任务,因为这就是它如何强制执行决定论。

因此,目前可能唯一能让它完全按照您的需要工作的方法是为Par monad编写自定义调度程序。

答案 1 :(得分:1)

来自Par包的至少parallel monad和策略允许仅构建纯粹的,无条件的并行系统,这些系统看起来很漂亮:

 a
/ \
b c
\ /\
 d  e
 \ ...

虽然在一般情况下你真的需要不纯的线程间通信:

solve :: Tree a -> Maybe a

smartPartition :: Tree a -> Int -> [[Tree a]]
smartPartition tree P = ... -- split the tree in fairly even chunks,
                            -- one per each machine core

solveP :: Tree a -> IO (Maybe a)
solveP tree = do
    resRef <- newIORef Nothing
    results <- parallel (map work (smartPartition tree))
    return (msum results)
  where work [] = return Nothing
        work (t:ts) = do
            res <- readIORef resRef
            if (isJust res) then (return res) else do
                let tRes = solve t
                if (isNothing tRes) then (work ts) else do
                    writeIORef tRes
                    return tRes

但是,如果您的单叶计算足够并且同样昂贵,那么未使用的策略不应该(我不确定)会对性能造成太大影响:

partitionLeafs :: Tree a -> Int -> [[Tree a]]

solveP :: Tree a -> Maybe a
solveP = msum . map step . transpose . partitionLeafs
  where step = msum . parMap rdeepseq solve

P上。 S.我觉得我理解问题的领域并不比你(至少)好,所以你可能已经知道了以上所有内容。我写了这个答案来开发讨论,因为这个问题对我很有意思。