了解Eval Monad的`rpar`

Kev*_*ith 8 haskell

查看Haskell中并行和并发编程的以下示例:

main = do
  [n] <- getArgs
  let test = [test1,test2,test3,test4] !! (read n - 1) 
  t0 <- getCurrentTime
  r <- evaluate (runEval test)
  printTimeSince t0
  print r
  printTimeSince t0


test1 = do
  x <- rpar (fib 36)
  y <- rpar (fib 35)
  return (x,y)
Run Code Online (Sandbox Code Playgroud)

这本书展示了它的编译:

 $ ghc -O2 rpar.hs -threaded
Run Code Online (Sandbox Code Playgroud)

然后运行上面的测试:

$ ./rpar 1 +RTS -N2
time: 0.00s
(24157817,14930352)
time: 0.83s
Run Code Online (Sandbox Code Playgroud)

如果我理解正确中,eval单子(使用rpar)导致两个fib 36fib 35并联被计算.

是否实际工作,即fib ... 在调用时计算功能(runEval test)?或许evaluate ...是必需的?或者,最后,也许在调用print r完全评估它时会计算出来?

目前尚不清楚对我来说,当实际工作获取执行rpar.

Ale*_*lec 5

这是我的猜测,但我似乎无法在我的笔记本电脑上复制这个,我必须从cabal获得太多的进口.

test1 = do
  x <- rpar (fib 36)
  y <- rpar (fib 35)
  return (x,y)
Run Code Online (Sandbox Code Playgroud)

在这一点上,你激发了评价(fib 36)(fib 35)并行,但你不要等待它们 - 你只是(x,y)立刻回来,x而且y还在评估.然后,我们到达print r,你被迫等到xy完成评估.

从理论上讲,下面的代码应强制test1等到xy完成返回之前评估.

test1 = do
  x <- rpar (fib 36)
  y <- rpar (fib 35)
  rseq x
  rseq y
  return (x,y)
Run Code Online (Sandbox Code Playgroud)

那么,运行它应该给你大概

$ ./rpar 1 +RTS -N2
time: 0.83s
(24157817,14930352)
time: 0.83s
Run Code Online (Sandbox Code Playgroud)

希望...

编辑

最后回到我的机器,复制了条件,我的建议代码给出了预期的结果.然而,OP提出了另一个好问题:如果evaluate仅评估WHNF,为什么它甚至会在调用print之前完成工作?

答案是在monad定义Control.Parallel.Strategies- 换句话说,它并不是evaluate推动评估xy,而是runEval.该Eval单子是在第一个参数严格:在x >>= f将评估x(请看看这个问题,然后再继续).然后,脱糖test1给出:

test1 = (rpar (fib 36)) >>= (\x -> 
        (rpar (fib 35)) >>= (\y ->
        (rseq x)        >>= (\_ ->
        (rseq y)        >>= (\_ ->
        return (x,y)))))
Run Code Online (Sandbox Code Playgroud)

然后,由于rpar只有"火花"评估,它使用par(开始评估第一个参数但立即返回第二个参数)并立即返回Done,但是,rseq(例如seq,但仅在第一个参数中严格)不会返回,Done直到其参数为实际评估(对WHNF).因此,在没有rseq调用的情况下,您已经知道x并且y已经开始被评估但不能保证它们已经完成,但是通过这些调用,您知道它们x并且yreturn被调用它们之前也被评估.