Reputation: 2800
I'm currently working through the book 'Parallel and Concurrent Programming in Haskell', and I'm trying to get one of the first examples to run on my machine, it is effectively some sudoku solver algorithm that the book shows how to use haskell to divide solving multiple iterations of the solver across two cores. Here is the code:
module Main where
import Control.DeepSeq
import Control.Monad
import Control.Parallel.Strategies
import qualified Data.Maybe as M
import Data.Monoid ((<>))
import Sudoku
import System.Environment
data ExecutionMode
= ExecPar
| ExecSeq
main :: IO ()
main = do
execMode <- getExecMode
filepath <- getFilepath
attemptRun execMode filepath
where
attemptRun execMode filepath =
case (execMode, filepath) of
(Just mode, Just file) -> sudoku mode file
(Nothing, _) -> putStrLn "Please provide valid execution mode: 'par' / 'seq'"
_ -> putStrLn "Please choose a file, 1000 / 16000 / 49151"
getExecMode =
(parseExecMode <=< M.listToMaybe) <$> getArgs
getFilepath =
(parseFilepath <=< M.listToMaybe . drop 1) <$> getArgs
parseExecMode "par" = Just ExecPar
parseExecMode "seq" = Just ExecSeq
parseExecMode _ = Nothing
parseFilepath str =
(\n -> "sudoku17." <> n <> ".txt") <$> case str of
"1000" -> Just "1000"
"16000" -> Just "16000"
"49151" -> Just "49151"
_ -> Nothing
sudoku :: ExecutionMode -> String -> IO ()
sudoku execMode filepath = determineMode
where
determineMode =
case execMode of
ExecPar -> runParallel
ExecSeq -> runSequential
runParallel = do
(as, bs) <- (\p -> splitAt (length p `div` 2) p) . lines <$> readFile ("sudoku/data/" <> filepath)
print . length . filter M.isJust . runEval $ do
as' <- rpar (force (map solve as))
bs' <- rpar (force (map solve bs))
_ <- rseq as'
_ <- rseq bs'
return (as' ++ bs')
return ()
runSequential = do
puzzles <- lines <$> readFile ("sudoku/data/" <> filepath)
print . length . filter M.isJust $ solve <$> puzzles
return ()
The algorithm solve
comes from Sudoku
, which has been lifted from the book and I am pretty sure is immaterial to my issue here.
So here is what happens when I try and run this code with cabal:
λ time cabal new-run sudoku par 16000 +RTS -N2
Up to date
16000
cabal new-run sudoku par 16000 +RTS -N2 17.82s user 0.15s system 100% cpu 17.954 total
And then in parallel:
λ time cabal new-run sudoku seq 16000 +RTS -N2
Up to date
16000
cabal new-run sudoku seq 16000 +RTS -N2 17.50s user 0.07s system 100% cpu 17.546 total
Note there is no difference in time. Here is what happens when I compile using ghc
:
λ ghc -O2 sudoku/Main.hs -threaded -v src/Sudoku.hs
λ time sudoku/Main par 16000 +RTS -N2
16000
sudoku/Main par 16000 +RTS -N2 19.59s user 0.28s system 198% cpu 10.034 total
And with the seq
flag it takes longer, as you would expect:
λ time sudoku/Main seq 16000 +RTS -N2
16000
sudoku/Main seq 16000 +RTS -N2 19.99s user 1.44s system 109% cpu 19.557 total
Here is my .cabal
file:
build-type: Simple
extra-source-files: ChangeLog.md
cabal-version: >=1.10
library
build-depends: base >=4.8 && <4.9
, array
, time
hs-source-dirs: src
exposed-modules: Sudoku
default-language: Haskell2010
executable sudoku
main-is: Main.hs
ghc-options: -O2 -Wall -Werror -threaded
build-depends: base >=4.8 && <4.9
, deepseq
, parallel
, parallel-and-concurrent-programming-in-haskell
hs-source-dirs: sudoku
default-language: Haskell2010
Am I missing something here? is this the correct behaviour for cabal? Or has something gone wrong between ghc-options
and ghc
perhaps?
Upvotes: 1
Views: 348
Reputation: 1013
Maybe try running:
time cabal new-run sudoku -- par 16000 +RTS -N2
The --
usually says "these flags belong to the executed program".
Upvotes: 4