danbroooks
danbroooks

Reputation: 2800

Cabal seems unable to build executable that runs in parallel

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

Answers (1)

soupi
soupi

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

Related Questions