如果我有一个执行四次非常长计算的函数并返回一个包含四次计算结果的列表,但是每次计算都不依赖于另一次计算,那么如何在Haskell中“并行化”这个? / p>
为了更好地解释我的问题,这里是我想到的一个Clojure示例:
(defn some-function [arg1 arg2 arg3 arg4]
let [c1 (very-long-computation arg1)
c2 (very-long-computation arg2)
c3 (very-long-computation arg3)
c4 (very-long-computation arg4)]
[c1 c2 c3 c4])
你可以产生三个额外的线程,比如说:
(defn some-function [arg1 arg2 arg3 arg4]
let [c1 (future (very-long-computation arg1))
c2 (future (very-long-computation arg2))
c3 (future (very-long-computation arg3))
c4 (very-long-computation arg4)] ; no need to wrap c4 in a future
[@c1 @c2 @c3 c4])
Haskell中的后续内容是否相同?
someFunction :: (a -> a -> a ->a) -> [a]
do c1 <- rpar (very-long-computation arg1)
c2 <- rpar (very-long-computation arg2)
c3 <- rpar (very-long-computation arg3)
c4 <- (very-long-computation arg4)
rseq c1
rseq c2
rseq c3
return (c1, c2, c3, c4)
我需要rpar / rseq c4吗?
rpar / rseq是进行此类并发计算的方法吗?
如果我不使用rseq,当我尝试访问返回的列表中的返回值时程序是否会等待?
这是透明的,还是你需要做一些像使用'@'时在Clojure中发生的“deref”?
答案 0 :(得分:4)
您最有可能寻找async
套餐。例如,如果您想要竞争三个计算并采取先完成的计算:
someFunction :: IO a
someFunction = do
c1 <- async $ veryLongComputation1
c2 <- async $ veryLongComputation2
c3 <- async $ veryLongComputation3
(_, a) <- waitAny $ [c1, c2, c3]
return a
或者您可以在特定wait
个帖子上使用async
,并通过stm
分享状态。对于这类事情来说,它是一个非常有用的软件包。您在OP中要求的精确版本如下所示:
someFunction :: IO (a, b, c, d)
someFunction = do
c1 <- async $ veryLongComputation1
c2 <- async $ veryLongComputation2
c3 <- async $ veryLongComputation3
v4 <- veryLongComputation4
-- wait for all the results and return them as a tuple
wait $ (,,,) <$> c1 <*> c2 <*> c3 <*> (return v4)
这当然假设c1, c2, c3
都是副作用,而您对结果不感兴趣。 wait
和poll
为您提供价值。
我也强烈推荐这本书&#34; Haskell中的并行和并发编程&#34;作者:Simon Marlow。
答案 1 :(得分:1)
我强烈推荐MonadPar。我之前使用过策略,但你仍然必须知道&#34;魔法&#34;命令让所有东西并行评估。根据我的经验,MonadPar正常运作。
这是一个简单的例子:
import Control.Monad.Par
import Control.Monad
foo :: [a] -> a
foo [x] = x
foo xs =
let len = length xs
x1 = take len xs
x2 = drop len xs
in runPar $ do
p1 <- spawnP $ foo x1
p2 <- spawnP $ foo x2
liftM2 (*) (get p1) $ get p2
当然,这确实需要足够的并行性来实际上是有益的。根据我的经验,Haskell中的并行开销非常高。