Haskell parMap中的共享变量

时间:2013-09-23 02:20:20

标签: haskell parallel-processing

我有一个基本上具有以下功能的计算:

f :: [a] -> ([b],Bool)

实际上可以写这个函数

f = foldr h ([],False) . map g
    where h (b,bool) (bs,boolSoFar) = (b:bs,bool || boolSoFar)

其中g :: a -> (b,Bool)是一些耗费大量时间的函数。此外,f通常在小列表中调用,因此尝试并行计算地图似乎很有趣。这可以通过Control.Parallel.Strategies parMap来完成。所以现在我们使用

f = foldr h ([],False) . parMap rseq g
    where h (b,bool) (bs,boolSoFar) = (b:bs, bool || boolSoFar)

这一切都很棒。现在,您将注意到可以在f的第一个定义中执行顺序优化。也就是说,我可以使用map-fold fusion将其作为单个折叠编写,因此在列表中循环一次。但是,我失去了做并行的好处。

现在,有人可能会说在f的第二个定义中,再次循环列表并不是那么糟糕,所以为什么不这样做呢。我想我的想法是,如果Haskell有可变变量,那么可以在map的主体中更新这个布尔变量(我想你必须锁定并解锁它)。做这样的事情有什么建议吗?

2 个答案:

答案 0 :(得分:1)

这最终会成为懒惰作家Applicative下的一个遍历,作者状态为Bool,因为(False, (||))形成一个幺半群。您还需要unamb个包,因此您可以在g的任何并行调用返回True时第一次获取该值。

import Control.Parallel.Strategies
import Data.Unamb

newtype EvalWB a = EvalWB { runEvalWB :: Eval (a, Bool) }

instance Functor EvalWB where
  fmap f (EvalWB m) = EvalWB $ fmap (\ ~(a, b) -> (f a, b)) m

instance Applicative EvalWB where
  pure a = EvalWB $ pure (a, False)

  EvalWB mf <*> EvalWB ma = EvalWB $ (\ ~(f, bf) ~(a, ba) -> (f a, por bf ba)) <$> mf <*> ma

然后你有

f :: [a] -> ([b], Bool)
f l = runEval $ runEvalWB $ traverse (\a -> EvalWB $ rpar $ g a) l

这会并行传递整个列表,懒惰地累积值和标志。它使用por在第一个True返回时短路。

答案 1 :(得分:-1)

你不能使用State Monad吗?更改函数f

f :: [a] -> ([b], Bool)

为:

f :: [a] -> State Bool [b]

你只需要通过一次折叠你的名单来更新你的州的价值,不是吗?不过,我不确定你是否可以将它应用于并行的东西。我对Haskell的了解有些局限。