hunk ./Control/Parallel/Strategies.hs 183 --- > using x s = s x +-- > using x s = unEval (s x) hunk ./Control/Parallel/Strategies.hs 123 --- | `Eval` is an Applicative Functor that makes it easier to define --- parallel strategies that involve traversing structures. +-- | 'Eval' is a Monad that makes it easier to define parallel +-- strategies. hunk ./Control/Parallel/Strategies.hs 126 --- a 'Seq' value will be evaluated strictly in sequence in its context, --- whereas a 'Par' value wraps an expression that may be evaluated in --- parallel. The Applicative instance allows sequential composition, --- making it possible to describe an evaluateion strategy by composing --- 'Par' and 'Seq' with '<*>'. +-- An 'Eval' is a value tagged with either 'Seq', 'Par', or 'Lazy'. +-- The Monad instance for 'Eval' is defined as follows: hunk ./Control/Parallel/Strategies.hs 129 --- For example, +-- > instance Monad Eval where +-- > return = Lazy +-- > m >>= k = case m of +-- > Seq a -> a `pseq` k a +-- > Par a -> a `par` k a +-- > Lazy a -> k a +-- +-- the '>>=' operator inspects the value returned by the left argument, and +-- +-- * if it is wrapped in 'Seq', then 'pseq' is applied to the value +-- before passing it to the right argument +-- +-- * if it is wrapped in 'Par', then 'par' is applied to the value +-- before passing it to the right argument +-- +-- * if it is 'Lazy', then it is passed untouched. +-- +-- For example, if you wanted to construct a 'Strategy' for a pair that +-- sparked the first component in parallel and then evaluated the second +-- component, you could write +-- +-- > myStrat :: Strategy (a,b) +-- > myStrat (a,b) = do { a' <- Par a; b' <- Seq b; return (a',b') } +-- +-- Alternatively, you could write this more compactly using the +-- Applicative style as +-- +-- > myStrat (a,b) = (,) <$> Par a <*> Seq b + +-- More examples, using the Applicative instance: hunk ./Control/Parallel/Strategies.hs 215 --- 'using' with the arguments reversed, and is equal to '($)'. +-- 'using' with the arguments reversed. hunk ./Control/Parallel/Strategies.hs 11 --- Parallel Evaluation Strategies, or Strategies for short, specify a --- way to evaluate a structure with components in sequence or in --- parallel. +-- Parallel Evaluation Strategies, or Strategies for short, provide +-- ways to express parallel computations. Strategies have the following +-- key features: hunk ./Control/Parallel/Strategies.hs 15 --- Strategies are for expressing /deterministic parallelism/: --- the result of the program is unaffected by evaluating in parallel. --- For non-deterministic parallel programming, see --- "Control.Concurrent". +-- * Strategies express /deterministic parallelism/: +-- the result of the program is unaffected by evaluating in parallel. +-- The parallel tasks evaluated by a Strategy may have no side effects. +-- For non-deterministic parallel programming, see "Control.Concurrent". +-- +-- * Strategies let you separate the description of the parallelism from the +-- logic of your program, enabling modular parallelism. The basic idea +-- is to build a lazy data structure representing the computation, and +-- then write a Strategy that describes how to traverse the data structure +-- and evaluate components of it sequentially or in parallel. +-- +-- * Strategies are /compositional/: larger strategies can be built +-- by gluing together smaller ones. +-- +-- * 'Monad' and 'Applicative' instances are provided, for quickly building +-- strategies that involve traversing structures in a regular way. hunk ./Control/Parallel/Strategies.hs 32 --- Strategies let you separate the description of parallelism from the --- logic of your program, enabling modular parallelism. +-- The strategies library has a long history. What follows is a +-- summary of how the current design evolved, and is mostly of +-- interest to those who are familiar with an older version, or need +-- to adapt old code to use the newer API. hunk ./Control/Parallel/Strategies.hs 39 --- The original Strategies design is described in --- +-- The original Strategies design is described in /Algorithm + Strategy = Parallelism/ hunk ./Control/Parallel/Strategies.hs 49 --- the paper \"Runtime Support for Multicore Haskell\" . +-- the paper /Runtime Support for Multicore Haskell/ . hunk ./Control/Parallel/Strategies.hs 90 +-- +-- Version 2.1 moved NFData into a separate package, @deepseq@. +-- +-- Version 2.2 changed the type of Strategy to @a -> Eval a@, and +-- re-introduced the @r0@ strategy which was missing in version 2.1. +-- +-- Version 2.3 simplified the @Eval@ type, so that @Eval@ is now just +-- the strict identity monad. This change and various other +-- improvements and refactorings are thanks to Patrick Maier who +-- noticed that @Eval@ didn't satisfy the monad laws, and that a +-- simpler version would fix that problem. hunk ./Control/Parallel/Strategies.hs 109 + dot, hunk ./Control/Parallel/Strategies.hs 145 +infixr 9 `dot` -- same as (.) +infixl 0 `using` -- lowest precedence and associate to the left + hunk ./Control/Parallel/Strategies.hs 152 --- strategies. --- --- An 'Eval' is a value tagged with either 'Seq', 'Par', or 'Lazy'. --- The Monad instance for 'Eval' is defined as follows: --- --- > instance Monad Eval where --- > return = Lazy --- > m >>= k = case m of --- > Seq a -> a `pseq` k a --- > Par a -> a `par` k a --- > Lazy a -> k a --- --- the '>>=' operator inspects the value returned by the left argument, and +-- strategies. It is a strict identity monad: that is, in hunk ./Control/Parallel/Strategies.hs 154 --- * if it is wrapped in 'Seq', then 'pseq' is applied to the value --- before passing it to the right argument +-- > m >>= f hunk ./Control/Parallel/Strategies.hs 156 --- * if it is wrapped in 'Par', then 'par' is applied to the value --- before passing it to the right argument +-- @m@ is evaluated before the result is passed to @f@. hunk ./Control/Parallel/Strategies.hs 158 --- * if it is 'Lazy', then it is passed untouched. +-- > instance Monad Eval where +-- > return = Done +-- > m >>= k = case m of +-- > Done x -> k x hunk ./Control/Parallel/Strategies.hs 163 --- For example, if you wanted to construct a 'Strategy' for a pair that --- sparked the first component in parallel and then evaluated the second +-- If you wanted to construct a 'Strategy' for a pair that sparked the +-- first component in parallel and then evaluated the second hunk ./Control/Parallel/Strategies.hs 168 --- > myStrat (a,b) = do { a' <- Par a; b' <- Seq b; return (a',b') } +-- > myStrat (a,b) = do { a' <- rpar a; b' <- rwhnf b; return (a',b') } hunk ./Control/Parallel/Strategies.hs 173 --- > myStrat (a,b) = (,) <$> Par a <*> Seq b +-- > myStrat (a,b) = (,) <$> rpar a <*> rwhnf b hunk ./Control/Parallel/Strategies.hs 178 --- > parList strat = traverse (Par . (`using` strat)) +-- > parList strat = traverse (rpar `dot` strat)) hunk ./Control/Parallel/Strategies.hs 183 -data Eval a = Seq a | Par a | Lazy a + +data Eval a = Done a hunk ./Control/Parallel/Strategies.hs 187 -unEval (Seq a) = a -unEval (Par a) = a -unEval (Lazy a) = a +unEval (Done x) = x hunk ./Control/Parallel/Strategies.hs 193 - pure a = return a + pure x = return x hunk ./Control/Parallel/Strategies.hs 197 - return = Lazy + return = Done hunk ./Control/Parallel/Strategies.hs 199 - Seq a -> a `pseq` k a - Par a -> a `par` k a - Lazy a -> k a + Done x -> k x + +-- The Eval monad satisfies the monad laws. +-- +-- (1) Left identity: +-- return x >>= f ==> Done x >>= f ==> f x +-- +-- (2) Right identity: +-- (i) m >>= return =*> Done u >>= return +-- ==> return u +-- ==> Done u <*= m +-- (ii) m >>= return =*> undefined >>= return +-- ==> undefined <*= m +-- +-- (3) Associativity: +-- (i) (m >>= f) >>= g =*> (Done u >>= f) >>= g +-- ==> f u >>= g <== (\x -> f x >>= g) u +-- <== Done u >>= (\x -> f x >>= g) +-- <*= m >>= (\x -> f x >>= g) +-- (ii) (m >>= f) >>= g =*> (undefined >>= f) >>= g +-- ==> undefined >>= g +-- ==> undefined <== undefined >>= (\x -> f x >>= g) +-- <*= m >>= (\x -> f x >>= g) + hunk ./Control/Parallel/Strategies.hs 257 +-- | Compose two strategies sequentially; like function composition on +-- strategies +-- +-- > strat2 `dot` strat1 == strat2 . (`using` strat1) +-- +dot :: Strategy a -> Strategy a -> Strategy a +strat2 `dot` strat1 = strat2 . unEval . strat1 + hunk ./Control/Parallel/Strategies.hs 267 -r0 = Lazy +r0 x = Done x hunk ./Control/Parallel/Strategies.hs 272 -rwhnf = Seq +rwhnf x = x `pseq` Done x hunk ./Control/Parallel/Strategies.hs 276 -rpar = Par +rpar x = x `par` Done x hunk ./Control/Parallel/Strategies.hs 283 -rdeepseq a = Seq (rnf a `pseq` a) +rdeepseq a = rnf a `pseq` Done a hunk ./Control/Parallel/Strategies.hs 292 -parPair f g (a,b) = do - a' <- Par (a `using` f) - b' <- Par (b `using` g) - return (a',b') +parPair f g = seqPair (rpar `dot` f) (rpar `dot` g) hunk ./Control/Parallel/Strategies.hs 298 -parTriple f g h (a,b,c) = do - a' <- Par (a `using` f) - b' <- Par (b `using` g) - c' <- Par (c `using` h) - return (a',b',c') +parTriple f g h = seqTriple (rpar `dot` f) (rpar `dot` g) (rpar `dot` h) hunk ./Control/Parallel/Strategies.hs 307 -parTraverse strat = traverse (Par . (`using` strat)) +parTraverse strat = traverse (rpar `dot` strat) hunk ./Control/Parallel/Strategies.hs 335 - x' <- Par (x `using` strat) + x' <- rpar (x `using` strat) hunk ./Control/Parallel/Strategies.hs 461 -{-# DEPRECATED Done "The Strategy type is now a -> a, not a -> Done" #-} +{-# DEPRECATED Done "The Strategy type is now a -> Eval a, not a -> Done" #-} hunk ./parallel.cabal 2 -version: 2.2.0.1 +version: 2.3.0.0 move ./tests/conc057.hs ./tests/par002.hs move ./tests/conc057.stdout ./tests/par002.stdout move ./tests/conc068.hs ./tests/par003.hs move ./tests/conc068.stdout ./tests/par003.stdout hunk ./tests/Makefile 10 -.PHONY: test068 -test068 : +.PHONY: par003 +par003 : hunk ./tests/Makefile 13 - $(TEST_HC) -v0 -fforce-recomp --make conc068.hs -o conc068 -threaded + $(TEST_HC) -v0 -fforce-recomp --make par003.hs -o par003 -threaded -rtsopts hunk ./tests/Makefile 15 - $(PYTHON) -c 'for i in range(11111): print "abqszzzq"' | ./conc068 +RTS -N2 + $(PYTHON) -c 'for i in range(11111): print "abqszzzq"' | ./par003 +RTS -N2 hunk ./tests/all.T 4 -test('conc057', only_ways(['threaded2']), +test('par002', only_ways(['threaded2']), hunk ./tests/all.T 8 -test('conc068', normal, run_command, ['$MAKE -s --no-print-directory test068']) +test('par003', normal, run_command, ['$MAKE -s --no-print-directory par003']) hunk ./tests/Makefile 13 - $(TEST_HC) -v0 -fforce-recomp --make par003.hs -o par003 -threaded -rtsopts + "$(TEST_HC)" -v0 -fforce-recomp --make par003.hs -o par003 -threaded -rtsopts hunk ./Control/Parallel.hs 16 - par, pseq, - seq, -- for backwards compatibility, 6.6 exported this -#if defined(__GRANSIM__) - , parGlobal, parLocal, parAt, parAtAbs, parAtRel, parAtForNow -#endif + par, pseq hunk ./Control/Parallel.hs 19 -import Prelude - hunk ./Control/Parallel.hs 25 -#if defined(__GRANSIM__) -import PrelBase -import PrelErr ( parError ) -import PrelGHC ( parGlobal#, parLocal#, parAt#, parAtAbs#, parAtRel#, parAtForNow# ) - -infixr 0 `par` - -{-# INLINE parGlobal #-} -{-# INLINE parLocal #-} -{-# INLINE parAt #-} -{-# INLINE parAtAbs #-} -{-# INLINE parAtRel #-} -{-# INLINE parAtForNow #-} -parGlobal :: Int -> Int -> Int -> Int -> a -> b -> b -parLocal :: Int -> Int -> Int -> Int -> a -> b -> b -parAt :: Int -> Int -> Int -> Int -> a -> b -> c -> c -parAtAbs :: Int -> Int -> Int -> Int -> Int -> a -> b -> b -parAtRel :: Int -> Int -> Int -> Int -> Int -> a -> b -> b -parAtForNow :: Int -> Int -> Int -> Int -> a -> b -> c -> c - -parGlobal (I# w) (I# g) (I# s) (I# p) x y = case (parGlobal# x w g s p y) of { 0# -> parError; _ -> y } -parLocal (I# w) (I# g) (I# s) (I# p) x y = case (parLocal# x w g s p y) of { 0# -> parError; _ -> y } - -parAt (I# w) (I# g) (I# s) (I# p) v x y = case (parAt# x v w g s p y) of { 0# -> parError; _ -> y } -parAtAbs (I# w) (I# g) (I# s) (I# p) (I# q) x y = case (parAtAbs# x q w g s p y) of { 0# -> parError; _ -> y } -parAtRel (I# w) (I# g) (I# s) (I# p) (I# q) x y = case (parAtRel# x q w g s p y) of { 0# -> parError; _ -> y } -parAtForNow (I# w) (I# g) (I# s) (I# p) v x y = case (parAtForNow# x v w g s p y) of { 0# -> parError; _ -> y } - -#endif - hunk ./Control/Parallel/Strategies.hs 4 --- Copyright : (c) The University of Glasgow 2001-2009 +-- Copyright : (c) The University of Glasgow 2001-2010 hunk ./Control/Parallel/Strategies.hs 32 --- The strategies library has a long history. What follows is a --- summary of how the current design evolved, and is mostly of --- interest to those who are familiar with an older version, or need --- to adapt old code to use the newer API. --- --- Version 1.x --- --- The original Strategies design is described in /Algorithm + Strategy = Parallelism/ --- and the code was written by --- Phil Trinder, Hans-Wolfgang Loidl, Kevin Hammond et al. --- --- Version 2.x --- --- Later, during work on the shared-memory implementation of --- parallelism in GHC, we discovered that the original formulation of --- Strategies had some problems, in particular it lead to space leaks --- and difficulties expressing speculative parallelism. Details are in --- the paper /Runtime Support for Multicore Haskell/ . --- --- This module has been rewritten in version 2. The main change is to --- the 'Strategy a' type synonym, which was previously @a -> Done@ and --- is now @a -> Eval a@. This change helps to fix the space leak described --- in \"Runtime Support for Multicore Haskell\". The problem is that --- the runtime will currently retain the memory referenced by all --- sparks, until they are evaluated. Hence, we must arrange to --- evaluate all the sparks eventually, just in case they aren't --- evaluated in parallel, so that they don't cause a space leak. This --- is why we must return a \"new\" value after applying a 'Strategy', --- so that the application can evaluate each spark created by the --- 'Strategy'. --- --- The simple rule is this: you /must/ use the result of applying --- a 'Strategy' if the strategy creates parallel sparks, and you --- should probably discard the the original value. If you don't --- do this, currently it may result in a space leak. In the --- future (GHC 6.14), it will probably result in lost parallelism --- instead, as we plan to change GHC so that unreferenced sparks --- are discarded rather than retained (we can't make this change --- until most code is switched over to this new version of --- Strategies, because code using the old verison of Strategies --- would be broken by the change in policy). --- --- The other changes in version 2.x are: --- --- * Strategies can now be defined using a convenient Monad/Applicative --- type, 'Eval'. e.g. @parList s = traverse (Par . (``using`` s))@ --- --- * 'parList' has been generalised to 'parTraverse', which works on --- any 'Traversable' type, and similarly 'seqList' has been generalised --- to 'seqTraverse' --- --- * 'parList' and 'parBuffer' have versions specialised to 'rwhnf', --- and there are transformation rules that automatically translate --- e.g. @parList rwnhf@ into a call to the optimised version. --- --- * 'NFData' has been moved to @Control.DeepSeq@ in the @deepseq@ --- package. Note that since the 'Strategy' type changed, 'rnf' --- is no longer a 'Strategy': use 'rdeepseq' instead. --- --- Version 2.1 moved NFData into a separate package, @deepseq@. --- --- Version 2.2 changed the type of Strategy to @a -> Eval a@, and --- re-introduced the @r0@ strategy which was missing in version 2.1. --- --- Version 2.3 simplified the @Eval@ type, so that @Eval@ is now just --- the strict identity monad. This change and various other --- improvements and refactorings are thanks to Patrick Maier who --- noticed that @Eval@ didn't satisfy the monad laws, and that a --- simpler version would fix that problem. +-- For API history and changes in this release, see "Control.Parallel.Strategies#history". hunk ./Control/Parallel/Strategies.hs 37 - -- * Strategy type and basic operations - Strategy, - using, - withStrategy, - dot, - rwhnf, rdeepseq, r0, rpar, - -- * Tuple strategies - seqPair, parPair, - seqTriple, parTriple, - -- * General traversals - seqTraverse, - parTraverse, - -- * List strategies - parList, seqList, - parListN, parListChunk, - parMap, - parBuffer, - -- * Simple list strategies - parListWHNF, - parBufferWHNF, - -- * Strategy composition operators - ($|), ($||), - (.|), (.||), - (-|), (-||), - -- * Building strategies - Eval(..), unEval, + -- * The strategy type + Strategy + + -- * Application of strategies + , using -- :: a -> Strategy a -> a + , withStrategy -- :: Strategy a -> a -> a + + -- * Composition of strategies + , dot -- :: Strategy a -> Strategy a -> Strategy a + + -- * Basic strategies + , r0 -- :: Strategy a + , rseq + , rdeepseq -- :: NFData a => Strategy a + , rpar -- :: Strategy a + + -- * Injection of sequential strategies + , evalSeq -- :: Seq.Strategy a -> Strategy a + + -- * Strategies for traversable data types + , evalTraversable -- :: Traversable t => Strategy a -> Strategy (t a) + , parTraversable hunk ./Control/Parallel/Strategies.hs 60 - -- * re-exported for backwards compatibility - NFData(..), + -- * Strategies for lists + , evalList -- :: Strategy a -> Strategy [a] + , parList + , evalListN -- :: Int -> Strategy a -> Strategy [a] + , parListN + , evalListNth -- :: Int -> Strategy a -> Strategy [a] + , parListNth + , evalListSplitAt -- :: Int -> Strategy [a] -> Strategy [a] -> Strategy [a] + , parListSplitAt + , parMap + + -- * Strategies for lazy lists + , evalBuffer -- :: Int -> Strategy a -> Strategy [a] + , parBuffer + + -- * Strategies for tuples + , evalTuple2 -- :: Strategy a -> ... -> Strategy (a,...) + , evalTuple3 + , evalTuple4 + , evalTuple5 + , evalTuple6 + , evalTuple7 + , evalTuple8 + , evalTuple9 + , parTuple2 -- :: Strategy a -> ... -> Strategy (a,...) + , parTuple3 + , parTuple4 + , parTuple5 + , parTuple6 + , parTuple7 + , parTuple8 + , parTuple9 + + -- * Strategic function application + , ($|) -- :: (a -> b) -> Strategy a -> a -> b + , ($||) + , (.|) -- :: (b -> c) -> Strategy b -> (a -> b) -> a -> c + , (.||) + , (-|) -- :: (a -> b) -> Strategy b -> (b -> c) -> a -> c + , (-||) + + -- * For Strategy programmers + , Eval(Done) -- instances: Monad, Functor, Applicative + , runEval -- :: Eval a -> a + , + + -- * API History + + -- $history + + -- * Backwards compatibility + + -- | These functions and types are all deprecated, and will be + -- removed in a future release. In all cases they have been + -- either renamed or replaced with equivalent functionality. hunk ./Control/Parallel/Strategies.hs 116 - -- * Deprecated functionality hunk ./Control/Parallel/Strategies.hs 117 + rwhnf, unEval, + seqTraverse, parTraverse, + seqList, + seqPair, parPair, + seqTriple, parTriple hunk ./Control/Parallel/Strategies.hs 130 +import qualified Control.Seq as Seq + hunk ./Control/Parallel/Strategies.hs 136 --- Eval +-- Eval monad (isomorphic to Lift monad from MonadLib 3.6.1) hunk ./Control/Parallel/Strategies.hs 173 -unEval :: Eval a -> a -unEval (Done x) = x +-- | Pull the result out of the monad. +runEval :: Eval a -> a +runEval (Done x) = x + +instance Monad Eval where + return x = Done x + Done x >>= k = k x -- Note: pattern 'Done x' makes '>>=' strict hunk ./Control/Parallel/Strategies.hs 182 - fmap f x = x >>= return . f + fmap = liftM hunk ./Control/Parallel/Strategies.hs 185 - pure x = return x hunk ./Control/Parallel/Strategies.hs 186 + pure = return hunk ./Control/Parallel/Strategies.hs 188 -instance Monad Eval where - return = Done - m >>= k = case m of - Done x -> k x hunk ./Control/Parallel/Strategies.hs 232 --- | evaluate a value using the given 'Strategy'. +-- | Evaluate a value using the given 'Strategy'. hunk ./Control/Parallel/Strategies.hs 234 --- > using x s = unEval (s x) +-- > x `using` s = runEval (s x) hunk ./Control/Parallel/Strategies.hs 237 -using x s = unEval (s x) +x `using` strat = runEval (strat x) hunk ./Control/Parallel/Strategies.hs 245 --- | Compose two strategies sequentially; like function composition on --- strategies +-- | Compose two strategies sequentially. +-- This is the analogue to function composition on strategies. hunk ./Control/Parallel/Strategies.hs 248 --- > strat2 `dot` strat1 == strat2 . (`using` strat1) +-- > strat2 `dot` strat1 == strat2 . withStrategy strat1 hunk ./Control/Parallel/Strategies.hs 251 -strat2 `dot` strat1 = strat2 . unEval . strat1 +strat2 `dot` strat1 = strat2 . runEval . strat1 + +-- Proof of strat2 `dot` strat1 == strat2 . withStrategy strat1 +-- +-- strat2 . withStrategy strat1 +-- == \x -> strat2 (withStrategy strat1 x) +-- == \x -> strat2 (x `using` strat1) +-- == \x -> strat2 (runEval (strat1 x)) +-- == \x -> (strat2 . runEval . strat1) x +-- == strat2 `dot` strat1 + +-- One might be tempted to think that 'dot' is equivalent to '(<=<)', +-- the right-to-left Kleisli composition in the Eval monad, because +-- '(<=<)' can take the type @Strategy a -> Strategy a -> Strategy a@ +-- and intuitively does what 'dot' does: First apply the strategy to the +-- right then the one to the left. However, there is a subtle difference +-- in strictness, witnessed by the following example: +-- +-- > (r0 `dot` rseq) undefined == Done undefined +-- > (r0 <=< rseq) undefined == undefined +-- + +-- | Inject a sequential strategy (ie. coerce a sequential strategy +-- to a general strategy). +-- +-- Thanks to 'evalSeq', the type @SeqStrategy a@ (which is a synonym for +-- @'Control.Seq.Strategy' a@) is a subtype +-- of @'Strategy' a@. +evalSeq :: Seq.Strategy a -> Strategy a +evalSeq strat x = strat x `pseq` return x hunk ./Control/Parallel/Strategies.hs 282 --- | A 'Strategy' that does no evaluation of its argument +-- -------------------------------------------------------------------------- +-- Basic strategies (some imported from SeqStrategies) + +-- | 'r0' performs *no* evaluation. +-- +-- > r0 == evalSeq Control.Seq.r0 +-- hunk ./Control/Parallel/Strategies.hs 290 -r0 x = Done x +r0 x = return x hunk ./Control/Parallel/Strategies.hs 292 --- | A 'Strategy' that simply evaluates its argument to Weak Head Normal --- Form (i.e. evaluates it as far as the topmost constructor). -rwhnf :: Strategy a -rwhnf x = x `pseq` Done x +-- Proof of r0 == evalSeq SeqStrategies.r0 +-- +-- evalSeq SeqStrategies.r0 +-- == \x -> SeqStrategies.r0 x `pseq` return x +-- == \x -> SeqStrategies.Done `pseq` return x +-- == \x -> return x +-- == r0 hunk ./Control/Parallel/Strategies.hs 300 --- | A 'Strategy' that evaluates its argument in parallel -rpar :: Strategy a -rpar x = x `par` Done x +-- | 'rseq' evaluates its argument to weak head normal form. +-- +-- > rseq == evalSeq Control.Seq.rseq +-- +rseq :: Strategy a +rseq x = x `pseq` return x hunk ./Control/Parallel/Strategies.hs 307 --- | A 'Strategy' that fully evaluates its argument --- --- > rdeepseq a = rnf a `pseq` a +-- Proof of rseq == evalSeq SeqStrategies.rseq +-- +-- evalSeq SeqStrategies.rseq +-- == \x -> SeqStrategies.rseq x `pseq` return x +-- == \x -> (x `seq` SeqStrategies.Done) `pseq` return x +-- == \x -> x `pseq` return x +-- == rseq + +-- | 'rdeepseq' fully evaluates its argument. +-- +-- > rdeepseq == evalSeq Control.Seq.rdeepseq hunk ./Control/Parallel/Strategies.hs 320 -rdeepseq a = rnf a `pseq` Done a +rdeepseq x = rnf x `pseq` return x hunk ./Control/Parallel/Strategies.hs 322 --- ----------------------------------------------------------------------------- --- Tuples +-- Proof of rdeepseq == evalSeq SeqStrategies.rdeepseq +-- +-- evalSeq SeqStrategies.rdeepseq +-- == \x -> SeqStrategies.rdeepseq x `pseq` return x +-- == \x -> (x `deepseq` SeqStrategies.Done) `pseq` return x +-- == \x -> (rnf x `seq` SeqStrategies.Done) `pseq` return x +-- == \x -> rnf x `pseq` return x +-- == rdeepseq hunk ./Control/Parallel/Strategies.hs 331 -seqPair :: Strategy a -> Strategy b -> Strategy (a,b) -seqPair f g (a,b) = pure (,) <*> f a <*> g b +-- | 'rpar' sparks its argument (for evaluation in parallel). +rpar :: Strategy a +rpar x = x `par` return x hunk ./Control/Parallel/Strategies.hs 335 -parPair :: Strategy a -> Strategy b -> Strategy (a,b) -parPair f g = seqPair (rpar `dot` f) (rpar `dot` g) hunk ./Control/Parallel/Strategies.hs 336 -seqTriple :: Strategy a -> Strategy b -> Strategy c -> Strategy (a,b,c) -seqTriple f g h (a,b,c) = pure (,,) <*> f a <*> g b <*> h c +-- -------------------------------------------------------------------------- +-- Strategy combinators for Traversable data types hunk ./Control/Parallel/Strategies.hs 339 -parTriple :: Strategy a -> Strategy b -> Strategy c -> Strategy (a,b,c) -parTriple f g h = seqTriple (rpar `dot` f) (rpar `dot` g) (rpar `dot` h) +-- | Evaluate the elements of a traversable data structure +-- according to the given strategy. +evalTraversable :: Traversable t => Strategy a -> Strategy (t a) +evalTraversable = traverse hunk ./Control/Parallel/Strategies.hs 344 --- ----------------------------------------------------------------------------- --- General sequential/parallel traversals +-- | Like 'evalTraversable' but evaluates all elements in parallel. +parTraversable :: Traversable t => Strategy a -> Strategy (t a) +parTraversable strat = evalTraversable (rpar `dot` strat) hunk ./Control/Parallel/Strategies.hs 348 --- | A strategy that traverses a container data type with an instance --- of 'Traversable', and sparks each of the elements using the supplied --- strategy. -parTraverse :: Traversable t => Strategy a -> Strategy (t a) -parTraverse strat = traverse (rpar `dot` strat) +{-# SPECIALISE evalTraversable :: Strategy a -> Strategy (Maybe a) #-} +{-# SPECIALISE parTraversable :: Strategy a -> Strategy (Maybe a) #-} +{-# SPECIALISE evalTraversable :: Strategy a -> Strategy [a] #-} +{-# SPECIALISE parTraversable :: Strategy a -> Strategy [a] #-} hunk ./Control/Parallel/Strategies.hs 353 --- | A strategy that traverses a container data type with an instance --- of 'Traversable', and evaluates each of the elements in left-to-right --- sequence using the supplied strategy. -seqTraverse :: Traversable t => Strategy a -> Strategy (t a) -seqTraverse = traverse +-- -------------------------------------------------------------------------- +-- Strategies for lists hunk ./Control/Parallel/Strategies.hs 356 -{-# SPECIALISE parTraverse :: Strategy a -> Strategy [a] #-} -{-# SPECIALISE seqTraverse :: Strategy a -> Strategy [a] #-} +-- | Evaluate each element of a list according to the given strategy. +-- Equivalent to 'evalTraversable' at the list type. +evalList :: Strategy a -> Strategy [a] +evalList = evalTraversable +-- Alternative explicitly recursive definition: +-- evalList strat [] = return [] +-- evalList strat (x:xs) = strat x >>= \x' -> +-- evalList strat xs >>= \xs' -> +-- return (x':xs') hunk ./Control/Parallel/Strategies.hs 366 --- ----------------------------------------------------------------------------- --- Lists - --- | Spark each of the elements of a list using the given strategy. --- Equivalent to 'parTraverse' at the list type. +-- | Evaluate each element of a list in parallel according to given strategy. +-- Equivalent to 'parTraversable' at the list type. hunk ./Control/Parallel/Strategies.hs 369 -parList = parTraverse +parList = parTraversable +-- Alternative definition via evalList: +-- parList strat = evalList (rpar `dot` strat) hunk ./Control/Parallel/Strategies.hs 373 --- | Evaluate each of the elements of a list sequentially from left to right --- using the given strategy. Equivalent to 'seqTraverse' at the list type. -seqList :: Strategy a -> Strategy [a] -seqList = traverse +-- | @'evaListSplitAt' n stratPref stratSuff@ evaluates the prefix +-- (of length @n@) of a list according to @stratPref@ and its the suffix +-- according to @stratSuff@. +evalListSplitAt :: Int -> Strategy [a] -> Strategy [a] -> Strategy [a] +evalListSplitAt n stratPref stratSuff xs + = let (ys,zs) = splitAt n xs in + stratPref ys >>= \ys' -> + stratSuff zs >>= \zs' -> + return (ys' ++ zs') hunk ./Control/Parallel/Strategies.hs 383 +-- | Like 'evalListSplitAt' but evaluates both sublists in parallel. +parListSplitAt :: Int -> Strategy [a] -> Strategy [a] -> Strategy [a] +parListSplitAt n stratPref stratSuff = evalListSplitAt n (rpar `dot` stratPref) (rpar `dot` stratSuff) + +-- | Evaluate the first n elements of a list according to the given strategy. +evalListN :: Int -> Strategy a -> Strategy [a] +evalListN n strat = evalListSplitAt n (evalList strat) r0 + +-- | Like 'evalListN' but evaluates the first n elements in parallel. hunk ./Control/Parallel/Strategies.hs 393 -parListN 0 _strat xs = return xs -parListN !_n _strat [] = return [] -parListN !n strat (x:xs) = do - x' <- rpar (x `using` strat) - xs' <- parListN (n-1) strat xs - return (x':xs') +parListN n strat = evalListN n (rpar `dot` strat) + +-- | Evaluate the nth element of a list (if there is such) according to +-- the given strategy. +-- The spine of the list up to the nth element is evaluated as a side effect. +evalListNth :: Int -> Strategy a -> Strategy [a] +evalListNth n strat = evalListSplitAt n r0 (evalListN 1 strat) hunk ./Control/Parallel/Strategies.hs 401 -parListChunk :: Int -> Strategy a -> Strategy [a] -parListChunk n strat xs = - concat `fmap` parList (seqList strat) (chunk n xs) +-- | Like 'evalListN' but evaluates the nth element in parallel. +parListNth :: Int -> Strategy a -> Strategy [a] +parListNth n strat = evalListNth n (rpar `dot` strat) hunk ./Control/Parallel/Strategies.hs 405 -chunk :: Int -> [a] -> [[a]] -chunk _ [] = [] -chunk n xs = as : chunk n bs where (as,bs) = splitAt n xs +-- Non-compositional version of 'parList', evaluating list elements +-- to weak head normal form. +-- Not to be exported; used for optimisation. + +-- | DEPRECATED: use @'parList' 'rseq'@ instead +parListWHNF :: Strategy [a] +parListWHNF xs = go xs `pseq` return xs + where -- go :: [a] -> [a] + go [] = [] + go (y:ys) = y `par` go ys + +-- The non-compositional 'parListWHNF' might be more efficient than its +-- more compositional counterpart; use RULES to do the specialisation. + +{-# RULES + "parList/rseq" parList rseq = parListWHNF + #-} hunk ./Control/Parallel/Strategies.hs 426 --- ----------------------------------------------------------------------------- --- parBuffer +-- -------------------------------------------------------------------------- +-- Strategies for lazy lists hunk ./Control/Parallel/Strategies.hs 429 --- | Applies a strategy to the nth element of list when the head is demanded. --- More precisely: --- --- * semantics: @parBuffer n s = id :: [a] -> [a]@ +-- List-based non-compositional rolling buffer strategy, evaluating list +-- elements to weak head normal form. +-- Not to be exported; used in evalBuffer' and for optimisation. +evalBufferWHNF :: Int -> Strategy [a] +evalBufferWHNF n0 xs0 = return (ret xs0 (start n0 xs0)) + where -- ret :: [a] -> [a] -> [a] + ret (x:xs) (y:ys) = y `pseq` (x : ret xs ys) + ret xs _ = xs + + -- start :: Int -> [a] -> [a] + start 0 ys = ys + start !_n [] = [] + start !n (y:ys) = y `pseq` start (n-1) ys + +-- | 'evalBuffer' is a rolling buffer strategy combinator for (lazy) lists, +-- much like 'evalBuffer', except that it realises the buffer as a lazy list +-- (with worst-case constant-time operations). hunk ./Control/Parallel/Strategies.hs 447 --- * dynamic behaviour: evalutates the nth element of the list when the --- head is demanded. +-- 'evalBuffer' is not as compositional as the type suggests. In fact, +-- it evaluates list elements at least to weak head normal form, +-- disregarding a strategy argument 'r0'. +-- +-- > evalBuffer n r0 == evalBuffer n rseq hunk ./Control/Parallel/Strategies.hs 453 --- The idea is to provide a `rolling buffer' of length n. It is a --- better than 'parList' for a lazy stream, because p'arList' will --- evaluate the entire list, whereas 'parBuffer' will only evaluate a --- fixed number of elements ahead. +evalBuffer :: Int -> Strategy a -> Strategy [a] +evalBuffer n strat = evalBufferWHNF n . map (withStrategy strat) hunk ./Control/Parallel/Strategies.hs 456 -parBuffer :: Int -> Strategy a -> [a] -> [a] -parBuffer n strat xs = map (`using` strat) xs `using` parBufferWHNF n +-- Like evalBufferWHNF but sparks the list elements when pushing them +-- into the buffer. +-- Not to be exported; used in parBuffer' and for optimisation. +parBufferWHNF :: Int -> Strategy [a] +parBufferWHNF n0 xs0 = return (ret xs0 (start n0 xs0)) + where -- ret :: [a] -> [a] -> [a] + ret (x:xs) (y:ys) = y `par` (x : ret xs ys) + ret xs _ = xs hunk ./Control/Parallel/Strategies.hs 465 --- ----------------------------------------------------------------------------- --- Simple strategies + -- start :: Int -> [a] -> [a] + start 0 ys = ys + start !_n [] = [] + start !n (y:ys) = y `par` start (n-1) ys + +{- + Alternative parBufferWHNF using the Eval monad. Probably slightly + less efficient. + +parBufferWHNF :: Int -> Strategy [a] +parBufferWHNF n xs = return (ret xs (start n xs)) + where -- ret :: [a] -> [a] -> [a] + ret (x:xs) (y:ys) = y `par` (x : ret xs ys) + ret xs [] = xs + + -- start :: Int -> [a] -> [a] + start 0 ys = ys + start !_n [] = [] + start !n (y:ys) = y `par` start (n-1) ys +-} + +-- | Like 'evalBuffer' but evaluates the list elements in parallel when +-- pushing them into the buffer. +parBuffer :: Int -> Strategy a -> Strategy [a] +parBuffer n strat = parBufferWHNF n . map (withStrategy strat) +-- Alternative definition via evalBuffer (may compromise firing of RULES): +-- parBuffer n strat = evalBuffer n (rpar `dot` strat) hunk ./Control/Parallel/Strategies.hs 493 --- These are non-compositional strategies that might be more efficient --- than their more general counterparts. We use RULES to do the --- specialisation. +-- Deforest the intermediate list in parBuffer/evalBuffer when it is +-- unnecessary: hunk ./Control/Parallel/Strategies.hs 497 -"parList/rwhnf" parList rwhnf = parListWHNF -"parBuffer/rwhnf" forall n . parBuffer n rwhnf = (`using` parBufferWHNF n) +"evalBuffer/rseq" forall n . evalBuffer n rseq = evalBufferWHNF n +"parBuffer/rseq" forall n . parBuffer n rseq = parBufferWHNF n hunk ./Control/Parallel/Strategies.hs 501 --- | version of 'parList' specialised to 'rwhnf'. This version is --- much simpler, and may be faster than 'parList rwhnf'. You should --- never need to use this directly, since 'parList rwhnf' is --- automatically optimised to 'parListWHNF'. It is here for --- experimentation purposes only. -parListWHNF :: Strategy [a] -parListWHNF xs = go xs `pseq` return xs - where go [] = [] - go (y:ys) = y `par` go ys +-- -------------------------------------------------------------------------- +-- Strategies for tuples hunk ./Control/Parallel/Strategies.hs 504 --- | version of 'parBuffer' specialised to 'rwhnf'. You should --- never need to use this directly, since 'parBuffer rwhnf' is --- automatically optimised to 'parBufferWHNF'. It is here for --- experimentation purposes only. -parBufferWHNF :: Int -> Strategy [a] -parBufferWHNF n0 xs0 = return (ret xs0 (start n0 xs0)) - where - ret (x:xs) (y:ys) = y `par` (x : ret xs ys) - ret xs _ = xs +-- | Evaluate the components of a tuple according to the given strategies. +evalTuple2 :: Strategy a -> Strategy b -> Strategy (a,b) +evalTuple2 strat1 strat2 (x1,x2) = + pure (,) <*> strat1 x1 <*> strat2 x2 + +evalTuple3 :: Strategy a -> Strategy b -> Strategy c -> Strategy (a,b,c) +evalTuple3 strat1 strat2 strat3 (x1,x2,x3) = + pure (,,) <*> strat1 x1 <*> strat2 x2 <*> strat3 x3 + +evalTuple4 :: Strategy a -> Strategy b -> Strategy c -> Strategy d -> Strategy (a,b,c,d) +evalTuple4 strat1 strat2 strat3 strat4 (x1,x2,x3,x4) = + pure (,,,) <*> strat1 x1 <*> strat2 x2 <*> strat3 x3 <*> strat4 x4 + +evalTuple5 :: Strategy a -> Strategy b -> Strategy c -> Strategy d -> Strategy e -> Strategy (a,b,c,d,e) +evalTuple5 strat1 strat2 strat3 strat4 strat5 (x1,x2,x3,x4,x5) = + pure (,,,,) <*> strat1 x1 <*> strat2 x2 <*> strat3 x3 <*> strat4 x4 <*> strat5 x5 + +evalTuple6 :: Strategy a -> Strategy b -> Strategy c -> Strategy d -> Strategy e -> Strategy f -> Strategy (a,b,c,d,e,f) +evalTuple6 strat1 strat2 strat3 strat4 strat5 strat6 (x1,x2,x3,x4,x5,x6) = + pure (,,,,,) <*> strat1 x1 <*> strat2 x2 <*> strat3 x3 <*> strat4 x4 <*> strat5 x5 <*> strat6 x6 + +evalTuple7 :: Strategy a -> Strategy b -> Strategy c -> Strategy d -> Strategy e -> Strategy f -> Strategy g -> Strategy (a,b,c,d,e,f,g) +evalTuple7 strat1 strat2 strat3 strat4 strat5 strat6 strat7 (x1,x2,x3,x4,x5,x6,x7) = + pure (,,,,,,) <*> strat1 x1 <*> strat2 x2 <*> strat3 x3 <*> strat4 x4 <*> strat5 x5 <*> strat6 x6 <*> strat7 x7 + +evalTuple8 :: Strategy a -> Strategy b -> Strategy c -> Strategy d -> Strategy e -> Strategy f -> Strategy g -> Strategy h -> Strategy (a,b,c,d,e,f,g,h) +evalTuple8 strat1 strat2 strat3 strat4 strat5 strat6 strat7 strat8 (x1,x2,x3,x4,x5,x6,x7,x8) = + pure (,,,,,,,) <*> strat1 x1 <*> strat2 x2 <*> strat3 x3 <*> strat4 x4 <*> strat5 x5 <*> strat6 x6 <*> strat7 x7 <*> strat8 x8 hunk ./Control/Parallel/Strategies.hs 533 - start _ [] = [] - start 0 ys = ys - start n (y:ys) = y `par` start (n-1) ys +evalTuple9 :: Strategy a -> Strategy b -> Strategy c -> Strategy d -> Strategy e -> Strategy f -> Strategy g -> Strategy h -> Strategy i -> Strategy (a,b,c,d,e,f,g,h,i) +evalTuple9 strat1 strat2 strat3 strat4 strat5 strat6 strat7 strat8 strat9 (x1,x2,x3,x4,x5,x6,x7,x8,x9) = + pure (,,,,,,,,) <*> strat1 x1 <*> strat2 x2 <*> strat3 x3 <*> strat4 x4 <*> strat5 x5 <*> strat6 x6 <*> strat7 x7 <*> strat8 x8 <*> strat9 x9 hunk ./Control/Parallel/Strategies.hs 537 ------------------------------------------------------------------------------- --- * Strategic Function Application ------------------------------------------------------------------------------- +-- | Evaluate the components of a tuple in parallel according to +-- the given strategies. +parTuple2 :: Strategy a -> Strategy b -> Strategy (a,b) +parTuple2 strat1 strat2 = + evalTuple2 (rpar `dot` strat1) (rpar `dot` strat2) + +parTuple3 :: Strategy a -> Strategy b -> Strategy c -> Strategy (a,b,c) +parTuple3 strat1 strat2 strat3 = + evalTuple3 (rpar `dot` strat1) (rpar `dot` strat2) (rpar `dot` strat3) + +parTuple4 :: Strategy a -> Strategy b -> Strategy c -> Strategy d -> Strategy (a,b,c,d) +parTuple4 strat1 strat2 strat3 strat4 = + evalTuple4 (rpar `dot` strat1) (rpar `dot` strat2) (rpar `dot` strat3) (rpar `dot` strat4) + +parTuple5 :: Strategy a -> Strategy b -> Strategy c -> Strategy d -> Strategy e -> Strategy (a,b,c,d,e) +parTuple5 strat1 strat2 strat3 strat4 strat5 = + evalTuple5 (rpar `dot` strat1) (rpar `dot` strat2) (rpar `dot` strat3) (rpar `dot` strat4) (rpar `dot` strat5) + +parTuple6 :: Strategy a -> Strategy b -> Strategy c -> Strategy d -> Strategy e -> Strategy f -> Strategy (a,b,c,d,e,f) +parTuple6 strat1 strat2 strat3 strat4 strat5 strat6 = + evalTuple6 (rpar `dot` strat1) (rpar `dot` strat2) (rpar `dot` strat3) (rpar `dot` strat4) (rpar `dot` strat5) (rpar `dot` strat6) + +parTuple7 :: Strategy a -> Strategy b -> Strategy c -> Strategy d -> Strategy e -> Strategy f -> Strategy g -> Strategy (a,b,c,d,e,f,g) +parTuple7 strat1 strat2 strat3 strat4 strat5 strat6 strat7 = + evalTuple7 (rpar `dot` strat1) (rpar `dot` strat2) (rpar `dot` strat3) (rpar `dot` strat4) (rpar `dot` strat5) (rpar `dot` strat6) (rpar `dot` strat7) + +parTuple8 :: Strategy a -> Strategy b -> Strategy c -> Strategy d -> Strategy e -> Strategy f -> Strategy g -> Strategy h -> Strategy (a,b,c,d,e,f,g,h) +parTuple8 strat1 strat2 strat3 strat4 strat5 strat6 strat7 strat8 = + evalTuple8 (rpar `dot` strat1) (rpar `dot` strat2) (rpar `dot` strat3) (rpar `dot` strat4) (rpar `dot` strat5) (rpar `dot` strat6) (rpar `dot` strat7) (rpar `dot` strat8) + +parTuple9 :: Strategy a -> Strategy b -> Strategy c -> Strategy d -> Strategy e -> Strategy f -> Strategy g -> Strategy h -> Strategy i -> Strategy (a,b,c,d,e,f,g,h,i) +parTuple9 strat1 strat2 strat3 strat4 strat5 strat6 strat7 strat8 strat9 = + evalTuple9 (rpar `dot` strat1) (rpar `dot` strat2) (rpar `dot` strat3) (rpar `dot` strat4) (rpar `dot` strat5) (rpar `dot` strat6) (rpar `dot` strat7) (rpar `dot` strat8) (rpar `dot` strat9) + +-- -------------------------------------------------------------------------- +-- Strategic function application hunk ./Control/Parallel/Strategies.hs 575 -These are very -handy when writing pipeline parallelism asa sequence of @$@, @$|@ and -@$||@'s. There is no need of naming intermediate values in this case. The -separation of algorithm from strategy is achieved by allowing strategies -only as second arguments to @$|@ and @$||@. +These are very handy when writing pipeline parallelism asa sequence of +@$@, @$|@ and @$||@'s. There is no need of naming intermediate values +in this case. The separation of algorithm from strategy is achieved by +allowing strategies only as second arguments to @$|@ and @$||@. hunk ./Control/Parallel/Strategies.hs 626 +-- | DEPRECCATED: replaced by the 'Eval' monad hunk ./Control/Parallel/Strategies.hs 630 +-- | DEPRECATED: Use 'pseq' or '$|' instead hunk ./Control/Parallel/Strategies.hs 635 +-- | DEPRECATED: Use 'par' or '$||' instead hunk ./Control/Parallel/Strategies.hs 640 +-- | DEPRECATED: Use 'pseq' or '$|' instead hunk ./Control/Parallel/Strategies.hs 645 +-- | DEPRECATED: Use 'par' or '$||' instead hunk ./Control/Parallel/Strategies.hs 648 + +{-# DEPRECATED rwhnf "renamed to rseq" #-} +-- | DEPRECATED: renamed to 'rseq' +rwhnf :: Strategy a +rwhnf = rseq + +{-# DEPRECATED seqTraverse "renamed to evalTraversable" #-} +-- | DEPRECATED: renamed to 'evalTraversable' +seqTraverse :: Traversable t => Strategy a -> Strategy (t a) +seqTraverse = evalTraversable + +{-# DEPRECATED parTraverse "renamed to parTraversable" #-} +-- | DEPRECATED: renamed to 'parTraversable' +parTraverse :: Traversable t => Strategy a -> Strategy (t a) +parTraverse = parTraversable + +{-# DEPRECATED parListWHNF "use (parList rseq) instead" #-} + +{-# DEPRECATED seqList "renamed to evalList" #-} +-- | DEPRECATED: renamed to 'evalList' +seqList :: Strategy a -> Strategy [a] +seqList = evalList + +{-# DEPRECATED seqPair "renamed to evalTuple2" #-} +-- | DEPRECATED: renamed to 'evalTuple2' +seqPair :: Strategy a -> Strategy b -> Strategy (a,b) +seqPair = evalTuple2 + +{-# DEPRECATED parPair "renamed to parTuple2" #-} +-- | DEPRECATED: renamed to 'parTuple2' +parPair :: Strategy a -> Strategy b -> Strategy (a,b) +parPair = parTuple2 + +{-# DEPRECATED seqTriple "renamed to evalTuple3" #-} +-- | DEPRECATED: renamed to 'evalTuple3' +seqTriple :: Strategy a -> Strategy b -> Strategy c -> Strategy (a,b,c) +seqTriple = evalTuple3 + +{-# DEPRECATED parTriple "renamed to parTuple3" #-} +-- | DEPRECATED: renamed to 'parTuple3' +parTriple :: Strategy a -> Strategy b -> Strategy c -> Strategy (a,b,c) +parTriple = parTuple3 + +{-# DEPRECATED unEval "renamed to runEval" #-} +-- | DEPRECATED: renamed to 'runEval' +unEval :: Eval a -> a +unEval = runEval + +{- $history #history# + +The strategies library has a long history. What follows is a +summary of how the current design evolved, and is mostly of +interest to those who are familiar with an older version, or need +to adapt old code to use the newer API. + +Version 1.x + + The original Strategies design is described in /Algorithm + Strategy = Parallelism/ + and the code was written by + Phil Trinder, Hans-Wolfgang Loidl, Kevin Hammond et al. + +Version 2.x + +Later, during work on the shared-memory implementation of +parallelism in GHC, we discovered that the original formulation of +Strategies had some problems, in particular it lead to space leaks +and difficulties expressing speculative parallelism. Details are in +the paper /Runtime Support for Multicore Haskell/ . + +This module has been rewritten in version 2. The main change is to +the 'Strategy a' type synonym, which was previously @a -> Done@ and +is now @a -> Eval a@. This change helps to fix the space leak described +in \"Runtime Support for Multicore Haskell\". The problem is that +the runtime will currently retain the memory referenced by all +sparks, until they are evaluated. Hence, we must arrange to +evaluate all the sparks eventually, just in case they aren't +evaluated in parallel, so that they don't cause a space leak. This +is why we must return a \"new\" value after applying a 'Strategy', +so that the application can evaluate each spark created by the +'Strategy'. + +The simple rule is this: you /must/ use the result of applying +a 'Strategy' if the strategy creates parallel sparks, and you +should probably discard the the original value. If you don't +do this, currently it may result in a space leak. In the +future (GHC 6.14), it will probably result in lost parallelism +instead, as we plan to change GHC so that unreferenced sparks +are discarded rather than retained (we can't make this change +until most code is switched over to this new version of +Strategies, because code using the old verison of Strategies +would be broken by the change in policy). + +The other changes in version 2.x are: + + * Strategies can now be defined using a convenient Monad/Applicative + type, 'Eval'. e.g. @parList s = traverse (Par . (``using`` s))@ + + * 'parList' has been generalised to 'parTraverse', which works on + any 'Traversable' type, and similarly 'seqList' has been generalised + to 'seqTraverse' + + * 'parList' and 'parBuffer' have versions specialised to 'rwhnf', + and there are transformation rules that automatically translate + e.g. @parList rwnhf@ into a call to the optimised version. + + * 'NFData' has been moved to @Control.DeepSeq@ in the @deepseq@ + package. Note that since the 'Strategy' type changed, 'rnf' + is no longer a 'Strategy': use 'rdeepseq' instead. + +Version 2.1 moved NFData into a separate package, @deepseq@. + +Version 2.2 changed the type of Strategy to @a -> Eval a@, and +re-introduced the @r0@ strategy which was missing in version 2.1. + +Version 2.3 simplified the @Eval@ type, so that @Eval@ is now just +the strict identity monad. This change and various other +improvements and refactorings are thanks to Patrick Maier who +noticed that @Eval@ didn't satisfy the monad laws, and that a +simpler version would fix that problem. + +(version 2.3 was not released on Hackage). + +Version 3 introduced a major overhaul of the API, to match what is +presented in the paper + + /Seq no More: Better Strategies for Parallel Haskell/ + + +The major differenes in the API are: + + * The addition of Sequential strategies ("Control.Seq") as + a composable means for specifying sequential evaluation. + + * Changes to the naming scheme: 'rwhnf' renamed to 'rseq', + 'seqList' renamed to 'evalList', 'seqPair' renamed to + 'evalTuple2', + +The naming scheme is now as follows: + + * Basic polymorphic strategies (of type @'Strategy' a@) are called @r...@. + Examples: 'r0', 'rseq', 'rpar', 'rdeepseq'. + + * A strategy combinator for a particular type constructor + or constructor class @T@ is called @evalT...@, @parT...@ or @seqT...@. + + * The @seqT...@ combinators (residing in module + "Control.Seq") yield sequential strategies. + Thus, @seqT...@ combinators cannot spark, nor can the sequential + strategies to which they may be applied. + Examples: 'seqTuple2', 'seqListN', 'seqFoldable'. + + * The @evalT...@ combinators do not spark themselves, yet they may + be applied to strategies that do spark. (They may also be applied + to non-sparking strategies; however, in that case the corresponding + @seqT...@ combinator might be a better choice.) + Examples: 'evalTuple2', 'evalListN', 'evalTraversable'. + + * The @parT...@ combinators, which are derived from their @evalT...@ + counterparts, do spark. They may be applied to all strategies, + whether sparking or not. + Examples: 'parTuple2', 'parListN', 'parTraversable'. + + * An exception to the type driven naming scheme are 'evalBuffer' and + 'parBuffer', which are not named after their type constructor (lists) + but after their function (rolling buffer of fixed size). + + * A strategy combinator that is not as compositional as its type + suggests is suffixed with @'@. + Examples: 'evalFunctor'', 'parBuffer''. +-} + addfile ./Control/Seq.hs hunk ./Control/Seq.hs 1 +{-# LANGUAGE BangPatterns #-} + +----------------------------------------------------------------------------- +-- | +-- Module : Control.Parallel.SeqStrategies +-- Copyright : (c) The University of Glasgow 2001-2009 +-- License : BSD-style (see the file libraries/base/LICENSE) +-- +-- Maintainer : libraries@haskell.org +-- Stability : experimental +-- Portability : portable +-- +-- Sequential strategies provide ways to compositionally specify +-- the degree of evaluation of a data type between the extremes of +-- no evaluation and full evaluation. +-- Sequential strategies may be viewed as complimentary to the parallel +-- ones (see module "Control.Parallel.Strategies"). +-- + +module Control.Seq + ( + -- * The sequential strategy type + Strategy + + -- * Application of sequential strategies + , using -- :: a -> Strategy a -> a + , withStrategy -- :: Strategy a -> a -> a + + -- * Composition of sequential strategies + , dot -- :: Strategy a -> Strategy a -> Strategy a + + -- * Basic sequential strategies + , r0 -- :: Strategy a + , rseq + , rdeepseq -- :: NFData a => Strategy a + + -- * Sequential strategies for lists + , seqList -- :: Strategy a -> Strategy [a] + , seqListN -- :: Int -> Strategy a -> Strategy [a] + , seqListNth + + -- * Sequential strategies for foldable data types + , seqFoldable -- :: Foldable t => Strategy a -> Strategy (t a) + , seqMap -- :: Strategy k -> Strategy v -> Strategy (Map k v) + , seqArray -- :: Ix i => Strategy a -> Strategy (Array i a) + , seqArrayBounds -- :: Ix i => Strategy i -> Strategy (Array i a) + + -- * Sequential strategies for tuples + , seqTuple2 -- :: Strategy a -> ... -> Strategy (a,...) + , seqTuple3 + , seqTuple4 + , seqTuple5 + , seqTuple6 + , seqTuple7 + , seqTuple8 + , seqTuple9 + ) where + +import Prelude +import Control.DeepSeq (NFData, deepseq) +import Data.Foldable (Foldable, toList) +import Data.Map (Map) +import qualified Data.Map (toList) +import Data.Ix (Ix) +import Data.Array (Array) +import qualified Data.Array (bounds, elems) + +infixr 9 `dot` -- same as function composition (.) +infixl 0 `using` -- lowest precedence and associate to the left + +-- -------------------------------------------------------------------------- +-- Sequential strategies + +-- | The type @'Strategy' a@ is @a -> ()@. +-- Thus, a strategy is a function whose sole purpose it is to evaluate +-- its argument (either in full or in part). +type Strategy a = a -> () + +-- | Evaluate a value using the given strategy. +using :: a -> Strategy a -> a +x `using` strat = strat x `seq` x + +-- | Evaluate a value using the given strategy. +-- This is simply 'using' with arguments reversed. +withStrategy :: Strategy a -> a -> a +withStrategy = flip using + +-- | Compose two strategies sequentially. +-- This is the analogue to function composition on strategies. +-- (Probably not very useful; provided because "Control.Parallel.Strategies" +-- provides the same function.) +-- +-- > strat2 `dot` strat1 == strat2 . withStrategy strat1 +-- +dot :: Strategy a -> Strategy a -> Strategy a +(strat2 `dot` strat1) x = strat1 x `seq` strat2 x + +-- More reasons for removing dot: +-- * It is inefficient: Traverses 'x' twice. +-- * It does not satisfy the property that 'Strategies.dot' has; there is +-- a counter-example to the equation +-- > strat2 `dot` strat1 == strat2 . withStrategy strat1 +-- Try strat2 = r0 and strat1 = rseq and apply to 'undefined'; +-- the LHS will diverge while the RHS will evaluate to '()'. + + +-- -------------------------------------------------------------------------- +-- Basic sequential strategies + +-- | 'r0' performs *no* evaluation. +r0 :: Strategy a +r0 _ = () + +-- | 'rseq' evaluates its argument to weak head normal form. +rseq :: Strategy a +rseq x = x `seq` () + +-- | 'rdeepseq' fully evaluates its argument. +-- Relies on class 'NFData' from module "Control.DeepSeq". +rdeepseq :: NFData a => Strategy a +rdeepseq x = x `deepseq` () + + +-- -------------------------------------------------------------------------- +-- Sequential strategies for lists + +-- | Evaluate each element of a list according to the given strategy. +-- This function is a specialisation of 'seqFoldable' to lists. +seqList :: Strategy a -> Strategy [a] +seqList _strat [] = () +seqList strat (x:xs) = strat x `seq` seqList strat xs +-- Alternative definition via seqFoldable: +-- seqList = seqFoldable + +-- | Evaluate the first n elements of a list according to the given strategy. +seqListN :: Int -> Strategy a -> Strategy [a] +seqListN 0 _strat _ = () +seqListN !_ _strat [] = () +seqListN !n strat (x:xs) = strat x `seq` seqListN (n-1) strat xs + +-- | Evaluate the nth element of a list (if there is such) according to +-- the given strategy. +-- The spine of the list up to the nth element is evaluated as a side effect. +seqListNth :: Int -> Strategy a -> Strategy [a] +seqListNth 0 strat (x:_) = strat x +seqListNth !_ _strat [] = () +seqListNth !n strat (_:xs) = seqListNth (n-1) strat xs + + +-- -------------------------------------------------------------------------- +-- Sequential strategies for foldable data types + +-- | Evaluate the elements of a foldable data structure according to +-- the given strategy. +seqFoldable :: Foldable t => Strategy a -> Strategy (t a) +seqFoldable strat = seqList strat . toList +-- Alternative definition via foldl': +-- seqFoldable strat = foldl' (const strat) () + +{-# SPECIALISE seqFoldable :: Strategy a -> Strategy [a] #-} + +-- | Evaluate the elements of an array according to the given strategy. +-- Evaluation of the array bounds may be triggered as a side effect. +seqArray :: Ix i => Strategy a -> Strategy (Array i a) +seqArray strat = seqList strat . Data.Array.elems + +-- | Evaluate the bounds of an array according to the given strategy. +seqArrayBounds :: Ix i => Strategy i -> Strategy (Array i a) +seqArrayBounds strat = seqTuple2 strat strat . Data.Array.bounds + +-- | Evaluate the keys and values of a map according to the given strategies. +seqMap :: Strategy k -> Strategy v -> Strategy (Map k v) +seqMap stratK stratV = seqList (seqTuple2 stratK stratV) . Data.Map.toList + + +-- -------------------------------------------------------------------------- +-- Sequential strategies for tuples + +-- | Evaluate the components of a tuple according to the given strategies. +-- No guarantee is given as to the order of evaluation. +seqTuple2 :: Strategy a -> Strategy b -> Strategy (a,b) +seqTuple2 strat1 strat2 (x1,x2) = + strat1 x1 `seq` strat2 x2 + +seqTuple3 :: Strategy a -> Strategy b -> Strategy c -> Strategy (a,b,c) +seqTuple3 strat1 strat2 strat3 (x1,x2,x3) = + strat1 x1 `seq` strat2 x2 `seq` strat3 x3 + +seqTuple4 :: Strategy a -> Strategy b -> Strategy c -> Strategy d -> Strategy (a,b,c,d) +seqTuple4 strat1 strat2 strat3 strat4 (x1,x2,x3,x4) = + strat1 x1 `seq` strat2 x2 `seq` strat3 x3 `seq` strat4 x4 + +seqTuple5 :: Strategy a -> Strategy b -> Strategy c -> Strategy d -> Strategy e -> Strategy (a,b,c,d,e) +seqTuple5 strat1 strat2 strat3 strat4 strat5 (x1,x2,x3,x4,x5) = + strat1 x1 `seq` strat2 x2 `seq` strat3 x3 `seq` strat4 x4 `seq` strat5 x5 + +seqTuple6 :: Strategy a -> Strategy b -> Strategy c -> Strategy d -> Strategy e -> Strategy f -> Strategy (a,b,c,d,e,f) +seqTuple6 strat1 strat2 strat3 strat4 strat5 strat6 (x1,x2,x3,x4,x5,x6) = + strat1 x1 `seq` strat2 x2 `seq` strat3 x3 `seq` strat4 x4 `seq` strat5 x5 `seq` strat6 x6 + +seqTuple7 :: Strategy a -> Strategy b -> Strategy c -> Strategy d -> Strategy e -> Strategy f -> Strategy g -> Strategy (a,b,c,d,e,f,g) +seqTuple7 strat1 strat2 strat3 strat4 strat5 strat6 strat7 (x1,x2,x3,x4,x5,x6,x7) = + strat1 x1 `seq` strat2 x2 `seq` strat3 x3 `seq` strat4 x4 `seq` strat5 x5 `seq` strat6 x6 `seq` strat7 x7 + +seqTuple8 :: Strategy a -> Strategy b -> Strategy c -> Strategy d -> Strategy e -> Strategy f -> Strategy g -> Strategy h -> Strategy (a,b,c,d,e,f,g,h) +seqTuple8 strat1 strat2 strat3 strat4 strat5 strat6 strat7 strat8 (x1,x2,x3,x4,x5,x6,x7,x8) = + strat1 x1 `seq` strat2 x2 `seq` strat3 x3 `seq` strat4 x4 `seq` strat5 x5 `seq` strat6 x6 `seq` strat7 x7 `seq` strat8 x8 + +seqTuple9 :: Strategy a -> Strategy b -> Strategy c -> Strategy d -> Strategy e -> Strategy f -> Strategy g -> Strategy h -> Strategy i -> Strategy (a,b,c,d,e,f,g,h,i) +seqTuple9 strat1 strat2 strat3 strat4 strat5 strat6 strat7 strat8 strat9 (x1,x2,x3,x4,x5,x6,x7,x8,x9) = + strat1 x1 `seq` strat2 x2 `seq` strat3 x3 `seq` strat4 x4 `seq` strat5 x5 `seq` strat6 x6 `seq` strat7 x7 `seq` strat8 x8 `seq` strat9 x9 hunk ./parallel.cabal 19 + Control.Seq hunk ./parallel.cabal 2 -version: 2.3.0.0 +version: 3.0.0.0 hunk ./Control/Parallel/Strategies.hs 69 + , parListChunk hunk ./Control/Parallel/Strategies.hs 72 - -- * Strategies for lazy lists + -- ** Strategies for lazy lists hunk ./Control/Parallel/Strategies.hs 406 +-- | Divides a list into chunks, and applies the strategy +-- @'evalList' strat@ to each chunk in parallel. +-- +-- It is expected that this function will be replaced by a more +-- generic clustering infrastructure in the future. +-- +parListChunk :: Int -> Strategy a -> Strategy [a] +parListChunk n strat xs = + concat `fmap` parList (evalList strat) (chunk n xs) + +chunk :: Int -> [a] -> [[a]] +chunk _ [] = [] +chunk n xs = as : chunk n bs where (as,bs) = splitAt n xs + hunk ./Control/Parallel/Strategies.hs 438 +-- -------------------------------------------------------------------------- +-- Convenience + +-- | A combination of 'parList' and 'map', encapsulating a common pattern: +-- +-- > parMap strat f = withStrategy strat . map f +-- hunk ./Control/Parallel/Strategies.hs 55 + , SeqStrategy hunk ./Control/Parallel/Strategies.hs 78 + + -- | Evaluate the components of a tuple according to the + -- given strategies. + hunk ./Control/Parallel/Strategies.hs 90 + + + -- | Evaluate the components of a tuple in parallel according to + -- the given strategies. + hunk ./Control/Parallel/Strategies.hs 141 -import qualified Control.Seq as Seq +import qualified Control.Seq hunk ./Control/Parallel/Strategies.hs 166 --- > myStrat (a,b) = do { a' <- rpar a; b' <- rwhnf b; return (a',b') } +-- > myStrat (a,b) = do { a' <- rpar a; b' <- rseq b; return (a',b') } hunk ./Control/Parallel/Strategies.hs 171 --- > myStrat (a,b) = (,) <$> rpar a <*> rwhnf b +-- > myStrat (a,b) = (,) <$> rpar a <*> rseq b hunk ./Control/Parallel/Strategies.hs 178 --- > seqPair :: Strategy a -> Strategy b -> Strategy (a,b) --- > seqPair f g (a,b) = pure (,) <$> f a <*> g b +-- > evalPair :: Strategy a -> Strategy b -> Strategy (a,b) +-- > evalPair f g (a,b) = pure (,) <$> f a <*> g b hunk ./Control/Parallel/Strategies.hs 287 --- Thanks to 'evalSeq', the type @SeqStrategy a@ (which is a synonym for --- @'Control.Seq.Strategy' a@) is a subtype +-- Thanks to 'evalSeq', the type @Control.Seq.Strategy a@ is a subtype hunk ./Control/Parallel/Strategies.hs 289 -evalSeq :: Seq.Strategy a -> Strategy a +evalSeq :: SeqStrategy a -> Strategy a hunk ./Control/Parallel/Strategies.hs 292 +-- | a name for @Control.Seq.Strategy@, for documetnation only. +type SeqStrategy a = Control.Seq.Strategy a + hunk ./Control/Parallel/Strategies.hs 305 --- Proof of r0 == evalSeq SeqStrategies.r0 +-- Proof of r0 == evalSeq Control.Seq.r0 hunk ./Control/Parallel/Strategies.hs 307 --- evalSeq SeqStrategies.r0 --- == \x -> SeqStrategies.r0 x `pseq` return x --- == \x -> SeqStrategies.Done `pseq` return x +-- evalSeq Control.Seq.r0 +-- == \x -> Control.Seq.r0 x `pseq` return x +-- == \x -> Control.Seq.Done `pseq` return x hunk ./Control/Parallel/Strategies.hs 320 --- Proof of rseq == evalSeq SeqStrategies.rseq +-- Proof of rseq == evalSeq Control.Seq.rseq hunk ./Control/Parallel/Strategies.hs 322 --- evalSeq SeqStrategies.rseq --- == \x -> SeqStrategies.rseq x `pseq` return x --- == \x -> (x `seq` SeqStrategies.Done) `pseq` return x +-- evalSeq Control.Seq.rseq +-- == \x -> Control.Seq.rseq x `pseq` return x +-- == \x -> (x `seq` Control.Seq.Done) `pseq` return x hunk ./Control/Parallel/Strategies.hs 335 --- Proof of rdeepseq == evalSeq SeqStrategies.rdeepseq +-- Proof of rdeepseq == evalSeq Control.Seq.rdeepseq hunk ./Control/Parallel/Strategies.hs 337 --- evalSeq SeqStrategies.rdeepseq --- == \x -> SeqStrategies.rdeepseq x `pseq` return x --- == \x -> (x `deepseq` SeqStrategies.Done) `pseq` return x --- == \x -> (rnf x `seq` SeqStrategies.Done) `pseq` return x +-- evalSeq Control.Seq.rdeepseq +-- == \x -> Control.Seq.rdeepseq x `pseq` return x +-- == \x -> (x `deepseq` Control.Seq.Done) `pseq` return x +-- == \x -> (rnf x `seq` Control.Seq.Done) `pseq` return x hunk ./Control/Parallel/Strategies.hs 465 --- Not to be exported; used in evalBuffer' and for optimisation. +-- Not to be exported; used in evalBuffer and for optimisation. hunk ./Control/Parallel/Strategies.hs 477 --- | 'evalBuffer' is a rolling buffer strategy combinator for (lazy) lists, --- much like 'evalBuffer', except that it realises the buffer as a lazy list --- (with worst-case constant-time operations). +-- | 'evalBuffer' is a rolling buffer strategy combinator for (lazy) lists. hunk ./Control/Parallel/Strategies.hs 490 --- Not to be exported; used in parBuffer' and for optimisation. +-- Not to be exported; used in parBuffer and for optimisation. hunk ./Control/Parallel/Strategies.hs 502 -{- - Alternative parBufferWHNF using the Eval monad. Probably slightly - less efficient. - -parBufferWHNF :: Int -> Strategy [a] -parBufferWHNF n xs = return (ret xs (start n xs)) - where -- ret :: [a] -> [a] -> [a] - ret (x:xs) (y:ys) = y `par` (x : ret xs ys) - ret xs [] = xs - - -- start :: Int -> [a] -> [a] - start 0 ys = ys - start !_n [] = [] - start !n (y:ys) = y `par` start (n-1) ys --} hunk ./Control/Parallel/Strategies.hs 521 --- | Evaluate the components of a tuple according to the given strategies. hunk ./Control/Parallel/Strategies.hs 553 --- | Evaluate the components of a tuple in parallel according to --- the given strategies. hunk ./Control/Seq.hs 29 - -- * Composition of sequential strategies - , dot -- :: Strategy a -> Strategy a -> Strategy a - hunk ./Control/Seq.hs 46 + + -- | Evaluate the components of a tuple according to the given strategies. + -- No guarantee is given as to the order of evaluation. + hunk ./Control/Seq.hs 69 -infixr 9 `dot` -- same as function composition (.) hunk ./Control/Seq.hs 88 --- | Compose two strategies sequentially. --- This is the analogue to function composition on strategies. --- (Probably not very useful; provided because "Control.Parallel.Strategies" --- provides the same function.) --- --- > strat2 `dot` strat1 == strat2 . withStrategy strat1 --- -dot :: Strategy a -> Strategy a -> Strategy a -(strat2 `dot` strat1) x = strat1 x `seq` strat2 x - --- More reasons for removing dot: --- * It is inefficient: Traverses 'x' twice. --- * It does not satisfy the property that 'Strategies.dot' has; there is --- a counter-example to the equation --- > strat2 `dot` strat1 == strat2 . withStrategy strat1 --- Try strat2 = r0 and strat1 = rseq and apply to 'undefined'; --- the LHS will diverge while the RHS will evaluate to '()'. - - hunk ./Control/Seq.hs 160 --- | Evaluate the components of a tuple according to the given strategies. --- No guarantee is given as to the order of evaluation. hunk ./Control/Parallel/Strategies.hs 827 - - * A strategy combinator that is not as compositional as its type - suggests is suffixed with @'@. - Examples: 'evalFunctor'', 'parBuffer''. hunk ./parallel.cabal 2 -version: 3.0.0.0 +version: 3.1.0.0 adddir ./tests/2185 addfile ./tests/2185/2185.hs hunk ./tests/2185/2185.hs 1 +{-# LANGUAGE BangPatterns,TypeSynonymInstances #-} +{-# OPTIONS_GHC -funbox-strict-fields #-} +module Main (main) where + +import Control.DeepSeq +import Control.Parallel.Strategies +import System.Environment +import System.IO + +type CFlt = Float +data Color = Color !CFlt !CFlt !CFlt deriving Show + +c_black :: Color +c_black = Color 0.0 0.0 0.0 +c_white :: Color +c_white = Color 1.0 1.0 1.0 + +get_color :: Flt -> Flt -> Scene -> Color +get_color x y scn = + let (Scene _ (Camera pos fwd up right) _ _) = scn + dir0 = vnorm $ vadd3 fwd (vscale right (-x)) (vscale up y) + ray = Ray pos dir0 + in + trace scn ray infinity + +gen_pixel_list :: Flt -> Flt -> Flt -> Flt -> Flt -> Flt -> Scene + -> [(Float,Float,Float,Float,Float)] +gen_pixel_list curx cury stopx stopy maxx maxy scene = + [ let scx = (x - midx) / midx + scy = (y - midy) / midy + Color r g b = get_color scx (scy * (midy / midx)) scene + in (scx, scy, r, g, b) + | x <- [curx .. (stopx - 1)], + y <- [cury .. (stopy - 1)] + ] + where midx = maxx / 2 + midy = maxy / 2 + +gen_blocks_list :: Bool -> Flt -> Flt -> Flt -> Scene -> IO () +gen_blocks_list par maxx maxy block_size scene = + let xblocks = maxx / block_size + yblocks = maxy / block_size + blocks = [ (x*block_size, y*block_size) + | x <- [0..xblocks-1], + y <- [0..yblocks-1] ] + mapper = if par then parMap rdeepseq else map + pixels = mapper + (\(x,y) -> gen_pixel_list x y (x+block_size) (y+block_size) maxx maxy scene) + blocks + in + do + print ('A', xblocks) + print ('B', yblocks) + print ('C', blocks) + rnf pixels `seq` return () + + +main :: IO () +main = do + args <- getArgs + let par = null args + display par xscene + display par xscene + display par xscene + display par xscene + display par xscene + +display :: Bool -> Scene -> IO () +display par scene = do + gen_blocks_list par 512 512 128 scene + +data Rayint = RayHit !Flt !Vec !Vec !Texture | RayMiss deriving Show + +data Material = Material Color !Flt !Flt !Flt !Flt !Flt deriving Show +type Texture = Rayint -> Material + +showTexture :: Texture -> String +showTexture t = show $ t RayMiss + +instance Show Texture where + show = showTexture + +t_white :: Rayint -> Material +t_white _ = Material c_white 0 0 0 1 2 + +data Solid = Sphere !Vec !Flt !Flt !Flt + | SNothing deriving Show + +sphere :: Vec -> Flt -> Solid +sphere c r = + Sphere c r (r*r) (1.0/r) + +rayint :: Solid -> Ray -> Flt -> Texture -> Rayint + +rayint (Sphere center r rsqr _) (Ray e dir0) dist t = + let eo = vsub center e + v = vdot eo dir0 + in + if (dist >= (v - r)) && (v > 0.0) + then + let vsqr = v*v + csqr = vdot eo eo + disc = rsqr - (csqr - vsqr) in + if disc < 0.0 then + RayMiss + else + let d = sqrt disc + p = vscaleadd e dir0 (v - d) + n = vnorm (vsub p center) in + RayHit (v-d) p n t + else + RayMiss + +rayint SNothing _ _ _ = RayMiss + +data Camera = Camera !Vec !Vec !Vec !Vec deriving Show + +camera :: Vec -> Vec -> Vec -> Flt -> Camera +camera pos at up angle = + let fwd = vnorm $ vsub at pos + right = vnorm $ vcross up fwd + up_ = vnorm $ vcross fwd right + cam_scale = tan ((pi/180)*(angle/2)) + in + Camera pos fwd + (vscale up_ cam_scale) + (vscale right cam_scale) + +data Scene = Scene !Solid !Camera !Texture !Color deriving Show + +cam :: Camera +cam = camera (Vec 2.1 1.3 1.7) + (Vec 0 0 0) + (Vec 0 0 1) + 45 + +bgc :: Color +bgc = Color 0.078 0.361 0.753 + +xscene :: Scene +xscene = let prim = sphere (Vec 0.272166 0.272166 0.544331) 0.166667 + in Scene prim cam t_white bgc + +shade :: Rayint -> Color +shade ri = + case ri of + RayHit _ _ _ _ -> c_black + RayMiss -> c_white + +trace :: Scene -> Ray -> Flt -> Color +trace scn ray depth = + let (Scene xsld _ dtex _) = scn + ri = rayint xsld ray depth dtex + in shade ri + +type Flt = Float + +infinity :: Flt +infinity = 1.0 / 0.0 + +data Vec = Vec {vec_x, vec_y, vec_z :: !Flt} deriving Show +data Ray = Ray !Vec !Vec deriving Show + +vdot :: Vec -> Vec -> Flt +vdot !v1 !v2 = + ((vec_x v1) * (vec_x v2)) + ((vec_y v1) * (vec_y v2)) + ((vec_z v1) * (vec_z v2)) + +vcross :: Vec -> Vec -> Vec +vcross !(Vec x1 y1 z1) !(Vec x2 y2 z2) = + Vec + ((y1 * z2) - (z1 * y2)) + ((z1 * x2) - (x1 * z2)) + ((x1 * y2) - (y1 * x2)) + +vadd3 :: Vec -> Vec -> Vec -> Vec +vadd3 !(Vec x1 y1 z1) !(Vec x2 y2 z2) !(Vec x3 y3 z3) = + Vec (x1 + x2 + x3) + (y1 + y2 + y3) + (z1 + z2 + z3) + +vsub :: Vec -> Vec -> Vec +vsub !(Vec x1 y1 z1) !(Vec x2 y2 z2) = + Vec (x1 - x2) + (y1 - y2) + (z1 - z2) + +vscale :: Vec -> Flt -> Vec +vscale v1 fac = + Vec ((vec_x v1) * fac) + ((vec_y v1) * fac) + ((vec_z v1) * fac) + +vscaleadd :: Vec -> Vec -> Flt -> Vec +vscaleadd v1 v2 fac = + Vec ((vec_x v1) + ((vec_x v2) * fac)) + ((vec_y v1) + ((vec_y v2) * fac)) + ((vec_z v1) + ((vec_z v2) * fac)) + +vnorm :: Vec -> Vec +vnorm (Vec x1 y1 z1) = + let len = 1.0 / (sqrt ((x1*x1)+(y1*y1)+(z1*z1))) in + Vec (x1*len) (y1*len) (z1*len) addfile ./tests/2185/2185.stdout hunk ./tests/2185/2185.stdout 1 +('A',4.0) +('B',4.0) +('C',[(0.0,0.0),(0.0,128.0),(0.0,256.0),(0.0,384.0),(128.0,0.0),(128.0,128.0),(128.0,256.0),(128.0,384.0),(256.0,0.0),(256.0,128.0),(256.0,256.0),(256.0,384.0),(384.0,0.0),(384.0,128.0),(384.0,256.0),(384.0,384.0)]) +('A',4.0) +('B',4.0) +('C',[(0.0,0.0),(0.0,128.0),(0.0,256.0),(0.0,384.0),(128.0,0.0),(128.0,128.0),(128.0,256.0),(128.0,384.0),(256.0,0.0),(256.0,128.0),(256.0,256.0),(256.0,384.0),(384.0,0.0),(384.0,128.0),(384.0,256.0),(384.0,384.0)]) +('A',4.0) +('B',4.0) +('C',[(0.0,0.0),(0.0,128.0),(0.0,256.0),(0.0,384.0),(128.0,0.0),(128.0,128.0),(128.0,256.0),(128.0,384.0),(256.0,0.0),(256.0,128.0),(256.0,256.0),(256.0,384.0),(384.0,0.0),(384.0,128.0),(384.0,256.0),(384.0,384.0)]) +('A',4.0) +('B',4.0) +('C',[(0.0,0.0),(0.0,128.0),(0.0,256.0),(0.0,384.0),(128.0,0.0),(128.0,128.0),(128.0,256.0),(128.0,384.0),(256.0,0.0),(256.0,128.0),(256.0,256.0),(256.0,384.0),(384.0,0.0),(384.0,128.0),(384.0,256.0),(384.0,384.0)]) +('A',4.0) +('B',4.0) +('C',[(0.0,0.0),(0.0,128.0),(0.0,256.0),(0.0,384.0),(128.0,0.0),(128.0,128.0),(128.0,256.0),(128.0,384.0),(256.0,0.0),(256.0,128.0),(256.0,256.0),(256.0,384.0),(384.0,0.0),(384.0,128.0),(384.0,256.0),(384.0,384.0)]) addfile ./tests/2185/Makefile hunk ./tests/2185/Makefile 1 +TOP=../../../.. +include $(TOP)/mk/boilerplate.mk +include $(TOP)/mk/test.mk addfile ./tests/2185/all.T hunk ./tests/2185/all.T 1 +test('2185', [skip_if_fast, reqlib('parallel'), + extra_run_opts('+RTS -M16m -RTS'), + only_ways(['threaded1','threaded2'])], + # threaded1 demonstrates the bug: sparks were treated as roots by GC + multimod_compile_and_run, ['2185',''])