8

I'm working through Chapter 3 of "Parallel and Concurrent Programming in Haskell" and it has the following example of running the fibonacci sequence in parallel using strategies:

import Control.Parallel
import Control.Parallel.Strategies (rpar, Strategy, using)
import Text.Printf
import System.Environment

-- <<fib
fib :: Integer -> Integer
fib 0 = 1
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
-- >>

main = print pair
 where
  pair =
-- <<pair
   (fib 35, fib 36) `using` parPair
-- >>

-- <<parPair
parPair :: Strategy (a,b)
parPair (a,b) = do
  a' <- rpar a
  b' <- rpar b
  return (a',b')
-- >>

when I compile this with:

ghc -O2 strat.hs -threaded -rtsopts -eventlog

and run it with 1 core

./strat +RTS -N1 -s -l

I get the following time output:

Total   time    1.23s  (  1.24s elapsed)

With two cores ./strat +RTS -N2 -s -l

 Total   time    1.47s  (  1.46s elapsed)

When I inspect the log with threadscope, It's easy to see that only 1 core is ever being used to run haskell code and the 2nd core is gc'ing the entire time.

I thought this might be because the evaluation of each function call to to fib wasn't actually occurring until the call the print resulting in the computing not happening in parallel. I made a slight modification to the original code:

import Control.DeepSeq(force, NFData)
parPair :: (NFData a, NFData b) => Strategy (a,b)
parPair (a,b) = do
  a' <- rpar $ force a
  b' <- rpar $ force b
  return (a',b')

However this didn't help. What am I missing here, Why is this computation not occurring in parallel?

jvans
  • 2,765
  • 2
  • 22
  • 23
  • I'm not a Haskell person so I can't help you with making it go parallel. I am interested in what happens (serial and parallel) in terms of memory space demands. See http://stackoverflow.com/a/5086087/120163 – Ira Baxter Nov 24 '15 at 15:24
  • 3
    Tripped over this, maybe useful: http://stackoverflow.com/questions/9024672/writing-fib-to-run-in-parallel-n2-is-slower?rq=1 – Ira Baxter Nov 24 '15 at 15:52
  • 1
    I can write a single-threaded program in ruby and run it on an Intel Core i3 - which runs faster than `fib n = fib (n-1) + fib (n-2)` on an IBM Main Frame with 128 CPUs ;) - sorry, but the fibonacci sequence is a classical example of a problem that should NOT be solved with recursion. – Michael Nov 24 '15 at 23:31
  • I just saw that you had tried to do a force. I missed that part and hence the deletion. That said, when I ran this code on my system, I'm getting a speed benefit of around 1.5 (averaged around 10). Also, parallelism of this sort doesn't really help since the fibonacci generation itself is of the order 2^n, which is quite bad for large numbers. As Michael said, fib is not a good suit for recursion (well, memoization will turn it into a linear time complexity algorithm and you get to keep the recursion). The crux is this:This problem is not inherently very parallel. – Akaberto Nov 25 '15 at 06:17
  • @Michael, this is intentionally using a very slow algorithm. The point here is to learn how to write parallel code. This should be something that can be easily parallelized because we're running two computations of fibonacci, one should be able to run on each core. – jvans Nov 25 '15 at 11:40
  • [A post on reddit](https://www.reddit.com/r/haskellquestions/comments/4cbh58) suggests that running with `-A16M` allows GC to run less frequently and somehow fixes this. I can confirm this on my setup. Behind this was apparently a bug in GHC, as described in [another question on stackoverflow](http://stackoverflow.com/q/36270447/3026489). – Yosh Oct 02 '16 at 07:36

0 Answers0