假设我有一个懒惰的Tree
,其叶子可能是解决问题的方法
data Tree a = Node [Tree a] | Leaf (Maybe a)
我需要找到只有一个解决方案(或者发现没有)。
我有一台 P 核心机器。从时间和内存效率的考虑,只有并行地搜索 P 不同的分支才有意义。
例如,假设您有四个具有大约相同计算复杂度的分支(对应于 T 秒的CPU时间),并且每个分支都有答案。
如果你在双核机器上真正并行评估所有四个分支,那么它们都将在 2T 秒内完成。
如果您仅评估前两个分支并推迟其他两个分支,那么您将仅在 T 秒内得到答案,同时使用两倍的内存。
我的问题是,是否可以使用任何并行Haskell基础结构(Par monad,并行策略......)来实现这一目标,还是必须使用async之类的低级工具?
答案 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.我觉得我理解问题的领域并不比你(至少)好,所以你可能已经知道了以上所有内容。我写了这个答案来开发讨论,因为这个问题对我很有意思。