[Haskell-beginners] missing parallelism

Maurizio Vitale mrz.vtl at gmail.com
Sat Apr 25 01:40:55 UTC 2015


Not even with this simplified version, I can get two async running on
separate threads.
I must be missing something really basic:

{-# LANGUAGE UnicodeSyntax #-}
{-# LANGUAGE TupleSections #-}

import Control.Concurrent.Async (async, waitBoth)

sumEuler :: Int → Int
sumEuler = sum . map euler . mkList
           where
             mkList n = [1..n-1]
             euler n = length (filter (relprime n) [1..n-1])
               where
                 relprime x y = gcd x y == 1

p ∷ IO Int
p = return $ sumEuler 5000

main ∷ IO()
main = do
  a ← async p
  b ← async p
  (av, bv) ← waitBoth a b
  print (av,bv)

On Fri, Apr 24, 2015 at 7:06 PM, Maurizio Vitale <mrz.vtl at gmail.com> wrote:
> You're right, without the show, no work is done.
> But I'm puzzled. I thought waitAny would have caused one task to be executed.
> If that doesn't wait for the async to compute a value (and not some
> thunk) I don't see how to use asyncs, so I'm obviously missing
> something.
> How can I force deeper evaluation?
> [in the end p would be a full parser, so whatever it is needed to
> cause the parsing needs to be done outside of it]
>
> On Fri, Apr 24, 2015 at 10:44 AM,  <haskell at erebe.eu> wrote:
>> On 24/04/2015 14:21, Maurizio Vitale wrote:
>>>
>>> G'day,
>>>    I have a test code that I compile with ghc -threaded -eventlog
>>> -rtsopts --make parallel.hs and run with ./parallel 2 +RTS -ls -N4 on
>>> a laptop with 4 physical cores. I would expect activities in two
>>> threads, but threadscope shows only one active thread.
>>> Can somebody explain me the program's behaviour?
>>>
>>> Thanks a lot
>>>
>>>     Maurizio
>>>
>>> {-# LANGUAGE UnicodeSyntax #-}
>>> {-# LANGUAGE TupleSections #-}
>>>
>>> import Control.Applicative
>>> import Control.Concurrent.Async (async, waitAny, Async)
>>> import Data.List (delete, sortBy)
>>> import Data.Ord (comparing)
>>> import System.CPUTime
>>> import System.Environment
>>> import GHC.Conc (numCapabilities)
>>>
>>> concurrentlyLimited :: Int -> [IO a] -> IO [a]
>>> concurrentlyLimited n tasks = concurrentlyLimited' n (zip [0..] tasks) []
>>> []
>>>
>>> concurrentlyLimited' ∷ Int               -- ^ number of concurrent
>>> evaluations
>>>                       → [(Int, IO b)]     -- ^ still to run (ordered by
>>> first element)
>>>                       → [Async (Int,b)]   -- ^ currently running
>>>                       → [(Int,b)]         -- ^ partial results (ordered
>>> by first element)
>>>                       → IO [b]
>>> concurrentlyLimited' _ [] [] results = return . map snd $ sortBy
>>> (comparing fst) results
>>> concurrentlyLimited' 0 todo ongoing results = do
>>>      (task, newResult) <- waitAny ongoing
>>>      concurrentlyLimited' 1 todo (delete task ongoing) (newResult:results)
>>>
>>> concurrentlyLimited' _ [] ongoing results = concurrentlyLimited' 0 []
>>> ongoing results
>>> concurrentlyLimited' n ((i, task):otherTasks) ongoing results = do
>>>      t <- async $ (i,) <$> task
>>>      concurrentlyLimited' (n-1) otherTasks (t:ongoing) results
>>>
>>> euler :: Int → Int
>>> euler n = length (filter (relprime n) [1..n-1])
>>>            where
>>>              relprime x y = gcd x y == 1
>>>
>>> sumEuler :: Int → Int
>>> sumEuler = sum . (map euler) . mkList
>>>             where
>>>               mkList n = [1..n-1]
>>>
>>> p ∷ IO Int
>>> p = return $ sumEuler 3000
>>>
>>> numThreads ∷ [String] → IO Int
>>> numThreads [] = return numCapabilities
>>> numThreads [cores] = return $ read cores
>>>
>>> main ∷ IO()
>>> main = do
>>>    threads ← getArgs >>= numThreads
>>>    putStrLn $ "Running up to " ++ show threads ++ " threads in parallel
>>> (on " ++ show numCapabilities ++ " cores)"
>>>    startTime ← getCPUTime
>>>    f ← concurrentlyLimited threads $ replicate 10 p
>>>    endTime ← getCPUTime
>>>    putStrLn $ foldr  ((++) . show ) ""  f
>>>    putStrLn $ "Evaluation took " ++ show (fromIntegral (endTime -
>>> startTime) / 1000000000000∷Double)
>>> _______________________________________________
>>> Beginners mailing list
>>> Beginners at haskell.org
>>> http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
>>
>>
>> Hello,
>>
>> I don't have an Haskell environnement at hand, but my advice is to search
>> for when your Async/eulerSum calls are evaluated.
>> For example try to remove this line   -- putStrLn $ foldr  ((++) . show ) ""
>> f
>> Does your program still compute something ? If no that's because your sum is
>> evaluated due to the show and not due to your async.
>>
>> t <- async $ (i,) <$> task
>>
>> Your async will try to compute (i,eulerSum) but you never force the
>> computation of the eulersum inside the async, so the async take no time and
>> return quickly.
>> Instead of this type [Async (Int, b)] you should aim for this one [(Int,
>> Async b)]
>>
>> Let me know if that helps you.
>>
>> Regards,
>> Romain
>>
>> _______________________________________________
>> Beginners mailing list
>> Beginners at haskell.org
>> http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
>
>
> On Fri, Apr 24, 2015 at 1:44 PM,  <haskell at erebe.eu> wrote:
>> On 24/04/2015 14:21, Maurizio Vitale wrote:
>>>
>>> G'day,
>>>    I have a test code that I compile with ghc -threaded -eventlog
>>> -rtsopts --make parallel.hs and run with ./parallel 2 +RTS -ls -N4 on
>>> a laptop with 4 physical cores. I would expect activities in two
>>> threads, but threadscope shows only one active thread.
>>> Can somebody explain me the program's behaviour?
>>>
>>> Thanks a lot
>>>
>>>     Maurizio
>>>
>>> {-# LANGUAGE UnicodeSyntax #-}
>>> {-# LANGUAGE TupleSections #-}
>>>
>>> import Control.Applicative
>>> import Control.Concurrent.Async (async, waitAny, Async)
>>> import Data.List (delete, sortBy)
>>> import Data.Ord (comparing)
>>> import System.CPUTime
>>> import System.Environment
>>> import GHC.Conc (numCapabilities)
>>>
>>> concurrentlyLimited :: Int -> [IO a] -> IO [a]
>>> concurrentlyLimited n tasks = concurrentlyLimited' n (zip [0..] tasks) []
>>> []
>>>
>>> concurrentlyLimited' ∷ Int               -- ^ number of concurrent
>>> evaluations
>>>                       → [(Int, IO b)]     -- ^ still to run (ordered by
>>> first element)
>>>                       → [Async (Int,b)]   -- ^ currently running
>>>                       → [(Int,b)]         -- ^ partial results (ordered
>>> by first element)
>>>                       → IO [b]
>>> concurrentlyLimited' _ [] [] results = return . map snd $ sortBy
>>> (comparing fst) results
>>> concurrentlyLimited' 0 todo ongoing results = do
>>>      (task, newResult) <- waitAny ongoing
>>>      concurrentlyLimited' 1 todo (delete task ongoing) (newResult:results)
>>>
>>> concurrentlyLimited' _ [] ongoing results = concurrentlyLimited' 0 []
>>> ongoing results
>>> concurrentlyLimited' n ((i, task):otherTasks) ongoing results = do
>>>      t <- async $ (i,) <$> task
>>>      concurrentlyLimited' (n-1) otherTasks (t:ongoing) results
>>>
>>> euler :: Int → Int
>>> euler n = length (filter (relprime n) [1..n-1])
>>>            where
>>>              relprime x y = gcd x y == 1
>>>
>>> sumEuler :: Int → Int
>>> sumEuler = sum . (map euler) . mkList
>>>             where
>>>               mkList n = [1..n-1]
>>>
>>> p ∷ IO Int
>>> p = return $ sumEuler 3000
>>>
>>> numThreads ∷ [String] → IO Int
>>> numThreads [] = return numCapabilities
>>> numThreads [cores] = return $ read cores
>>>
>>> main ∷ IO()
>>> main = do
>>>    threads ← getArgs >>= numThreads
>>>    putStrLn $ "Running up to " ++ show threads ++ " threads in parallel
>>> (on " ++ show numCapabilities ++ " cores)"
>>>    startTime ← getCPUTime
>>>    f ← concurrentlyLimited threads $ replicate 10 p
>>>    endTime ← getCPUTime
>>>    putStrLn $ foldr  ((++) . show ) ""  f
>>>    putStrLn $ "Evaluation took " ++ show (fromIntegral (endTime -
>>> startTime) / 1000000000000∷Double)
>>> _______________________________________________
>>> Beginners mailing list
>>> Beginners at haskell.org
>>> http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
>>
>>
>> Hello,
>>
>> I don't have an Haskell environnement at hand, but my advice is to search
>> for when your Async/eulerSum calls are evaluated.
>> For example try to remove this line   -- putStrLn $ foldr  ((++) . show ) ""
>> f
>> Does your program still compute something ? If no that's because your sum is
>> evaluated due to the show and not due to your async.
>>
>> t <- async $ (i,) <$> task
>>
>> Your async will try to compute (i,eulerSum) but you never force the
>> computation of the eulersum inside the async, so the async take no time and
>> return quickly.
>> Instead of this type [Async (Int, b)] you should aim for this one [(Int,
>> Async b)]
>>
>> Let me know if that helps you.
>>
>> Regards,
>> Romain
>>
>> _______________________________________________
>> Beginners mailing list
>> Beginners at haskell.org
>> http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners


More information about the Beginners mailing list