[Haskell-cafe] Parallel Pi

Daniel Fischer daniel.is.fischer at web.de
Wed Mar 17 17:30:30 EDT 2010


Am Mittwoch 17 März 2010 19:49:57 schrieb Artyom Kazak:
> Hello!
> I tried to implement the parallel Monte-Carlo method of computing Pi
> number, using two cores:
<move>
>
> But it uses only on core:
>
<snip>
>
> We see that our one spark is pruned. Why?
>

Well, the problem is that your tasks don't do any real work - yet.
piMonte returns a thunk pretty immediately, that thunk is then evaluated by 
show, long after your chance for parallelism is gone. You must force the 
work to be done _in_ r1 and r2, then you get parallelism:

  Generation 0:  2627 collections,  2626 parallel,  0.14s,  0.12s elapsed
  Generation 1:     1 collections,     1 parallel,  0.00s,  0.00s elapsed

  Parallel GC work balance: 1.79 (429262 / 240225, ideal 2)

                        MUT time (elapsed)       GC time  (elapsed)
  Task  0 (worker) :    0.00s    (  8.22s)       0.00s    (  0.00s)
  Task  1 (worker) :    8.16s    (  8.22s)       0.01s    (  0.01s)
  Task  2 (worker) :    8.00s    (  8.22s)       0.13s    (  0.11s)
  Task  3 (worker) :    0.00s    (  8.22s)       0.00s    (  0.00s)

  SPARKS: 1 (1 converted, 0 pruned)

  INIT  time    0.00s  (  0.00s elapsed)
  MUT   time   16.14s  (  8.22s elapsed)
  GC    time    0.14s  (  0.12s elapsed)
  EXIT  time    0.00s  (  0.00s elapsed)
  Total time   16.29s  (  8.34s elapsed)

  %GC time       0.9%  (1.4% elapsed)

  Alloc rate    163,684,377 bytes per MUT second

  Productivity  99.1% of total user, 193.5% of total elapsed

But alas, it is slower than the single-threaded calculation :(

  INIT  time    0.00s  (  0.00s elapsed)
  MUT   time    7.08s  (  7.10s elapsed)
  GC    time    0.08s  (  0.08s elapsed)
  EXIT  time    0.00s  (  0.00s elapsed)
  Total time    7.15s  (  7.18s elapsed)


> --PROGRAM
> module Main where
>
> import Random
> import Data.Ratio
> import Data.List
> import System.IO
> import GHC.Conc
>
> main = do
>   putStrLn "pi 1"
>   putStr "n: "
>   hFlush stdout
>   t <- getLine
>   piMonte (read t) >>= (putStrLn . show)
>
> piMonte n = do
>   (g1, g2) <- split `fmap` getStdGen
>   let r1 = r (n `div` 2) g1
>       r2 = r (n `div` 2 + n `mod` 2) g2 in
>     return (ratio (r1 `par` (r2 `pseq` (merge r1 r2))))
>   where
>     r n g = (length (filter id lAll), n)
thunk----------^^^^^^^^^^^^^^^^^^^^^^^

That thunk doesn't take much work to produce, only to evaluate, so you must 
force the evaluation within r, e.g. via

    r n g = ln `pseq` (ln,n)
      where
        ln = length (filter id lAll)
        ...

unfortunately, that doesn't give a speed-up, I don't know why.

>       where
>         l = take n . randomRs (0, 1)
>         inCircle :: Double -> Double -> Bool
>         inCircle a b = a*a + b*b <= 0.25
>         lAll = zipWith inCircle (l g1) (l g2)
>         (g1, g2) = split g
>     ratio :: (Int, Int) -> Double
>     ratio (a, b) = fromRational (toInteger a % toInteger b * 16)
>     merge (a, b) (c, d) = (a + c, b + d)
> --END


More information about the Haskell-Cafe mailing list