on master help sign in
Change mkParallel and mkAsync signatures

Use stream to stream transformation instead of returning a stream in the monad.

commita285b55ede499630f2a8bf92a6da77d4d6e22d5b authorHarendra Kumar <harendra.kumar@gmail.com> dateThu 9 Jan 2020, 20:02 (7 years ago) parenta3c6ad3
6 files changed+105−153
src/Streamly.hs+16−1 src/Streamly/Internal/Data/Stream/Async.hs+41−14 src/Streamly/Internal/Data/Stream/Parallel.hs+9−33 src/Streamly/Internal/Data/Stream/StreamD.hs+25−56 src/Streamly/Internal/Data/Stream/Zip.hs+2−16 src/Streamly/Internal/Prelude.hs+12−33
src/Streamly.hs+16−1
@@ -52,6 +52,7 @@
5252-- package.
5353
5454{-# LANGUAGE CPP #-}
55+{-# LANGUAGE FlexibleContexts #-}
5556{-# LANGUAGE MultiParamTypeClasses #-}
5657
5758#if __GLASGOW_HASKELL__ >= 800
@@ -225,7 +226,7 @@ where
225226import Data.Semigroup (Semigroup(..))
226227import Streamly.Internal.Data.SVar (MonadAsync, Rate(..))
227228import Streamly.Internal.Data.Stream.Ahead
228−import Streamly.Internal.Data.Stream.Async
229+import Streamly.Internal.Data.Stream.Async hiding (mkAsync)
229230import Streamly.Internal.Data.Stream.Combinators
230231import Streamly.Internal.Data.Stream.Parallel
231232import Streamly.Internal.Data.Stream.Serial
@@ -235,6 +236,7 @@ import Streamly.Internal.Data.Stream.Zip
235236import qualified Streamly.Prelude as P
236237import qualified Streamly.Internal.Prelude as IP
237238import qualified Streamly.Internal.Data.Stream.StreamK as K
239+import qualified Streamly.Internal.Data.Stream.Async as Async
238240
239241-- XXX provide good succinct examples of pipelining, merging, splitting etc.
240242-- below.
@@ -451,6 +453,19 @@ forEachWith :: (IsStream t, Foldable f)
451453forEachWith = P.forEachWith
452454-}
453455
456+-- XXX Deprecate it in 0.8.0
457+--
458+-- | Make a stream asynchronous, triggers the computation and returns a stream
459+-- in the underlying monad representing the output generated by the original
460+-- computation. The returned action is exhaustible and must be drained once. If
461+-- not drained fully we may have a thread blocked forever and once exhausted it
462+-- will always return 'empty'.
463+--
464+-- @since 0.2.0
465+{-# INLINABLE mkAsync #-}
466+mkAsync :: (IsStream t, MonadAsync m) => t m a -> m (t m a)
467+mkAsync = return . Async.mkAsync
468+
454469------------------------------------------------------------------------------
455470-- Documentation
456471------------------------------------------------------------------------------
src/Streamly/Internal/Data/Stream/Async.hs+41−14
@@ -9,6 +9,8 @@
99{-# LANGUAGE ScopedTypeVariables #-}
1010{-# LANGUAGE UndecidableInstances #-} -- XXX
1111
12+#include "inline.hs"
13+
1214-- |
1315-- Module : Streamly.Internal.Data.Stream.Async
1416-- Copyright : (c) 2017 Harendra Kumar
@@ -27,7 +29,7 @@ module Streamly.Internal.Data.Stream.Async
2729 , async
2830 , (<|) --deprecated
2931 , mkAsync
30− , mkAsync'
32+ , mkAsyncK
3133
3234 , WAsyncT
3335 , WAsync
@@ -494,21 +496,46 @@ newAsyncVar st m = do
494496 sv <- liftIO $ getLifoSVar st mrun
495497 sendFirstWorker sv m
496498
497−-- XXX Get rid of this?
498−-- | Make a stream asynchronous, triggers the computation and returns a stream
499−-- in the underlying monad representing the output generated by the original
500−-- computation. The returned action is exhaustible and must be drained once. If
501−-- not drained fully we may have a thread blocked forever and once exhausted it
502−-- will always return 'empty'.
499+-- | Generate a stream asynchronously to keep it buffered, lazily consume
500+-- from the buffer.
503501--
504−-- @since 0.2.0
505−{-# INLINABLE mkAsync #-}
506−mkAsync :: (IsStream t, MonadAsync m) => t m a -> m (t m a)
507−mkAsync = mkAsync' defState
502+-- /Internal/
503+--
504+{-# INLINABLE mkAsyncK #-}
505+mkAsyncK :: (IsStream t, MonadAsync m) => t m a -> t m a
506+mkAsyncK m = mkStream $ \st yld sng stp -> do
507+ sv <- newAsyncVar (adaptState st) (toStream m)
508+ foldStream st yld sng stp $ fromSVar sv
509+
510+{-# INLINE_NORMAL mkAsyncD #-}
511+mkAsyncD :: MonadAsync m => D.Stream m a -> D.Stream m a
512+mkAsyncD m = D.Stream step Nothing
513+ where
508514
509−{-# INLINABLE mkAsync' #-}
510−mkAsync' :: (IsStream t, MonadAsync m) => State Stream m a -> t m a -> m (t m a)
511−mkAsync' st m = fmap fromSVar (newAsyncVar st (toStream m))
515+ step gst Nothing = do
516+ sv <- newAsyncVar gst (D.fromStreamD m)
517+ return $ D.Skip $ Just $ D.fromSVar sv
518+
519+ step gst (Just (D.UnStream step1 st)) = do
520+ r <- step1 gst st
521+ return $ case r of
522+ D.Yield a s -> D.Yield a (Just $ D.Stream step1 s)
523+ D.Skip s -> D.Skip (Just $ D.Stream step1 s)
524+ D.Stop -> D.Stop
525+
526+-- This is slightly faster than the CPS version above
527+--
528+-- | Make the stream producer and consumer run concurrently by introducing a
529+-- buffer between them. The producer thread evaluates the input stream until
530+-- the buffer fills, it terminates if the buffer is full and a worker thread is
531+-- kicked off again to evaluate the remaining stream when there is space in the
532+-- buffer. The consumer consumes the stream lazily from the buffer.
533+--
534+-- /Internal/
535+--
536+{-# INLINE_NORMAL mkAsync #-}
537+mkAsync :: (K.IsStream t, MonadAsync m) => t m a -> t m a
538+mkAsync = D.fromStreamD . mkAsyncD . D.toStreamD
512539
513540-- | Create a new SVar and enqueue one stream computation on it.
514541{-# INLINABLE newWAsyncVar #-}
src/Streamly/Internal/Data/Stream/Parallel.hs+9−33
@@ -33,13 +33,10 @@ module Streamly.Internal.Data.Stream.Parallel
3333
3434 -- * Evaluate Concurrently
3535 , mkParallel
36− , applyParallel
37− , foldParallel
3836
3937 -- * Tap Concurrently
4038 , tapAsync
4139 , distributeAsync_
42−
4340 )
4441where
4542
@@ -248,38 +245,17 @@ parallelMin = joinStreamVarPar ParallelVar StopAny
248245-- Convert a stream to parallel
249246------------------------------------------------------------------------------
250247
251−mkParallel :: (IsStream t, MonadAsync m)
252− => State Stream m a -> t m a -> m (t m a)
253−mkParallel st m = do
254− sv <- newParallelVar StopNone defState
248+-- | Generate a stream asynchronously to keep it buffered, lazily consume
249+-- from the buffer.
250+--
251+-- /Internal/
252+--
253+mkParallel :: (IsStream t, MonadAsync m) => t m a -> t m a
254+mkParallel m = mkStream $ \st yld sng stp -> do
255+ sv <- newParallelVar StopNone (adaptState st)
255256 -- pushWorkerPar sv (runOne st{streamVar = Just sv} $ toStream m)
256257 D.toSVarParallel st sv $ D.toStreamD m
257− return $ fromSVar sv
258−
259−------------------------------------------------------------------------------
260−-- Stream to stream concurrent function application
261−------------------------------------------------------------------------------
262−
263−{-# INLINE applyParallel #-}
264−applyParallel :: (IsStream t, MonadAsync m)
265− => (t m a -> t m b) -> t m a -> t m b
266−applyParallel f m = mkStream $ \st yld sng stp -> do
267− sv <- newParallelVar StopNone (adaptState st)
268− -- pushWorkerPar sv (runOne st{streamVar = Just sv} (toStream m))
269− D.toSVarParallel (adaptState st) sv $ D.toStreamD m
270− foldStream st yld sng stp $ f $ fromSVar sv
271−
272−------------------------------------------------------------------------------
273−-- Stream runner concurrent function application
274−------------------------------------------------------------------------------
275−
276−{-# INLINE foldParallel #-}
277−foldParallel :: (IsStream t, MonadAsync m) => (t m a -> m b) -> t m a -> m b
278−foldParallel f m = do
279− sv <- newParallelVar StopNone defState
280− -- pushWorkerPar sv (runOne defState{streamVar = Just sv} $ toStream m)
281− D.toSVarParallel defState sv $ D.toStreamD m
282− f $ fromSVar sv
258+ foldStream st yld sng stp $ fromSVar sv
283259
284260------------------------------------------------------------------------------
285261-- Clone and distribute a stream in parallel
src/Streamly/Internal/Data/Stream/StreamD.hs+25−56
@@ -277,8 +277,6 @@ module Streamly.Internal.Data.Stream.StreamD
277277 , indexedR
278278 , zipWith
279279 , zipWithM
280− , zipAsyncWith
281− , zipAsyncWithM
282280
283281 -- * Comparisons
284282 , eqBy
@@ -306,8 +304,6 @@ module Streamly.Internal.Data.Stream.StreamD
306304
307305 -- * Concurrent Application
308306 , mkParallel
309− , applyParallel
310− , foldParallel
311307
312308 , lastN
313309
@@ -3857,37 +3853,6 @@ zipWithM f (Stream stepa ta) (Stream stepb tb) = Stream step (ta, tb, Nothing)
38573853zipWith :: Monad m => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c
38583854zipWith f = zipWithM (\a b -> return (f a b))
38593855
3860−-- | Like 'zipWithM' but zips concurrently i.e. both the streams being zipped
3861−-- are generated concurrently.
3862−--
3863−{-# INLINE_NORMAL zipAsyncWithM #-}
3864−zipAsyncWithM :: MonadAsync m
3865− => (a -> b -> m c) -> Stream m a -> Stream m b -> Stream m c
3866−zipAsyncWithM f m1 m2 = Stream step Nothing
3867−
3868− where
3869−
3870− {-# INLINE_LATE step #-}
3871− step gst Nothing = do
3872− ma <- mkParallel (adaptState gst) m1
3873− mb <- mkParallel (adaptState gst) m2
3874− return $ Skip (Just $ zipWithM f ma mb)
3875−
3876− step gst (Just (UnStream step1 st)) = do
3877− r <- step1 gst st
3878− return $ case r of
3879− Yield a s -> Yield a (Just $ Stream step1 s)
3880− Skip s -> Skip (Just $ Stream step1 s)
3881− Stop -> Stop
3882−
3883−-- | Like 'zipWith' but zips concurrently i.e. both the streams being zipped
3884−-- are generated concurrently.
3885−--
3886−{-# INLINE zipAsyncWith #-}
3887−zipAsyncWith :: MonadAsync m
3888− => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c
3889−zipAsyncWith f = zipAsyncWithM (\a b -> return (f a b))
3890−
38913856------------------------------------------------------------------------------
38923857-- Merging
38933858------------------------------------------------------------------------------
@@ -3968,6 +3933,8 @@ runFold (Fold step begin done) = foldlMx' step begin done
39683933-- Concurrent application and fold
39693934-------------------------------------------------------------------------------
39703935
3936+-- XXX These functions should be moved to Stream/Parallel.hs
3937+--
39713938-- Using StreamD the worker stream producing code can fuse with the code to
39723939-- queue output to the SVar giving some perf boost.
39733940--
@@ -4034,24 +4001,16 @@ toSVarParallel st sv xs =
40344001 (handleChildException sv)
40354002 modifyThread sv tid
40364003
4037−{-# INLINE_NORMAL mkParallel #-}
4038−mkParallel :: MonadAsync m => State t m a -> Stream m a -> m (Stream m a)
4039−mkParallel st m = do
4040− sv <- newParallelVar StopNone defState
4041− toSVarParallel st sv m
4042− return $ fromSVar sv
4043−
4044−{-# INLINE_NORMAL applyParallel #-}
4045−applyParallel :: MonadAsync m
4046− => (Stream m a -> Stream m b) -> Stream m a -> Stream m b
4047−applyParallel f m = Stream step Nothing
4004+{-# INLINE_NORMAL mkParallelD #-}
4005+mkParallelD :: MonadAsync m => Stream m a -> Stream m a
4006+mkParallelD m = Stream step Nothing
40484007 where
40494008
4050− {-# INLINE_LATE step #-}
40514009 step gst Nothing = do
4052− sv <- newParallelVar StopNone (adaptState gst)
4053− toSVarParallel (adaptState gst) sv m
4054− return $ Skip $ Just $ f (fromSVar sv)
4010+ sv <- newParallelVar StopNone gst
4011+ toSVarParallel gst sv m
4012+ -- XXX use unfold instead?
4013+ return $ Skip $ Just $ fromSVar sv
40554014
40564015 step gst (Just (UnStream step1 st)) = do
40574016 r <- step1 gst st
@@ -4060,12 +4019,22 @@ applyParallel f m = Stream step Nothing
40604019 Skip s -> Skip (Just $ Stream step1 s)
40614020 Stop -> Stop
40624021
4063−{-# INLINE foldParallel #-}
4064−foldParallel :: (K.IsStream t, MonadAsync m) => (t m a -> m b) -> t m a -> m b
4065−foldParallel f m = do
4066− sv <- newParallelVar StopNone defState
4067− toSVarParallel defState sv (toStreamD m)
4068− f $ fromStreamD $ fromSVar sv
4022+-- Compare with mkAsync. mkAsync uses an Async style SVar whereas this uses a
4023+-- parallel style SVar for evaluation. Currently, parallel style cannot use
4024+-- rate control whereas Async style can use rate control. In async style SVar
4025+-- the worker thread terminates when the buffer is full whereas in Parallel
4026+-- style it blocks.
4027+--
4028+-- | Make the stream producer and consumer run concurrently by introducing a
4029+-- buffer between them. The producer thread evaluates the input stream until
4030+-- the buffer fills, it blocks if the buffer is full until there is space in
4031+-- the buffer. The consumer consumes the stream lazily from the buffer.
4032+--
4033+-- /Internal/
4034+--
4035+{-# INLINE_NORMAL mkParallel #-}
4036+mkParallel :: (K.IsStream t, MonadAsync m) => t m a -> t m a
4037+mkParallel = fromStreamD . mkParallelD . toStreamD
40694038
40704039-------------------------------------------------------------------------------
40714040-- Concurrent tap
src/Streamly/Internal/Data/Stream/Zip.hs+2−16
@@ -56,7 +56,7 @@ import Text.Read (Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec,
5656import Prelude hiding (map, repeat, zipWith)
5757
5858import Streamly.Internal.Data.Stream.StreamK (IsStream(..), Stream)
59−import Streamly.Internal.Data.SVar (MonadAsync, adaptState)
59+import Streamly.Internal.Data.SVar (MonadAsync)
6060
6161import qualified Streamly.Internal.Data.Stream.Prelude as P
6262import qualified Streamly.Internal.Data.Stream.StreamK as K
@@ -93,11 +93,6 @@ zipWith f m1 m2 = P.fromStreamS $ S.zipWith f (P.toStreamS m1) (P.toStreamS m2)
9393-- Parallel Zipping
9494------------------------------------------------------------------------------
9595
96−-- The CPS version and the direct version of zipAsyncWithM below seem to have
97−-- identical performance. However, we need to use the StreamD version of
98−-- mkParallel which uses a direct implementation of fromSVar. In comparison to
99−-- CPS version of fromSVar the direct version gives a 2x improvement.
100−
10196-- | Like 'zipWithM' but zips concurrently i.e. both the streams being zipped
10297-- are generated concurrently.
10398--
@@ -105,16 +100,7 @@ zipWith f m1 m2 = P.fromStreamS $ S.zipWith f (P.toStreamS m1) (P.toStreamS m2)
105100{-# INLINE zipAsyncWithM #-}
106101zipAsyncWithM :: (IsStream t, MonadAsync m)
107102 => (a -> b -> m c) -> t m a -> t m b -> t m c
108−zipAsyncWithM f m1 m2 = K.mkStream $ \st stp sng yld -> do
109− ma <- D.mkParallel (adaptState st) (D.toStreamD m1)
110− mb <- D.mkParallel (adaptState st) (D.toStreamD m2)
111− K.foldStream st stp sng yld $ zipWithM f (D.fromStreamD ma)
112− (D.fromStreamD mb)
113−{-
114−zipAsyncWithM f m1 m2 =
115− fromStreamD $ D.zipAsyncWithM f (toStreamD m1) (toStreamD m2)
116−-}
117−
103+zipAsyncWithM f m1 m2 = zipWithM f (D.mkParallel m1) (D.mkParallel m2)
118104-- | Like 'zipWith' but zips concurrently i.e. both the streams being zipped
119105-- are generated concurrently.
120106--
src/Streamly/Internal/Prelude.hs+12−33
@@ -172,6 +172,8 @@ module Streamly.Internal.Prelude
172172 -- , lprescanlM'
173173
174174 -- ** Concurrent Transformation
175+ , D.mkParallel
176+ -- Par.mkParallel
175177 , applyAsync
176178 , (|$)
177179 , (|&)
@@ -413,9 +415,6 @@ module Streamly.Internal.Prelude
413415 , usingStateT
414416 , runStateT
415417
416− -- * Concurrency
417− , mkParallel
418−
419418 -- * MonadFix
420419 , K.mfix
421420
@@ -468,8 +467,7 @@ import Streamly.Internal.Data.Fold.Types (Fold (..), Fold2 (..))
468467import Streamly.Internal.Data.Unfold.Types (Unfold)
469468import Streamly.Internal.Memory.Array.Types (Array, writeNUnsafe)
470469-- import Streamly.Memory.Ring (Ring)
471−import Streamly.Internal.Data.SVar (MonadAsync, State, defState)
472−import Streamly.Internal.Data.Stream.Async (mkAsync')
470+import Streamly.Internal.Data.SVar (MonadAsync, defState)
473471import Streamly.Internal.Data.Stream.Combinators (inspectMode, maxYields)
474472import Streamly.Internal.Data.Stream.Prelude
475473 (fromStreamS, toStreamS, foldWith, foldMapWith, forEachWith)
@@ -498,6 +496,7 @@ import qualified Streamly.Internal.Data.Stream.StreamK as S
498496import qualified Streamly.Internal.Data.Stream.StreamD as S
499497#endif
500498
499+-- import qualified Streamly.Internal.Data.Stream.Async as Async
501500import qualified Streamly.Internal.Data.Stream.Serial as Serial
502501import qualified Streamly.Internal.Data.Stream.Parallel as Par
503502import qualified Streamly.Internal.Data.Stream.Zip as Z
@@ -1541,19 +1540,6 @@ toPureRev = foldl' (flip K.cons) K.nil
15411540-- Concurrent Application
15421541------------------------------------------------------------------------------
15431542
1544−-- XXX mkParallel is usually used to make serial streams concurrent by
1545−-- evaluating the stream in another thread. Ideally, we should not be creating
1546−-- an SVar if the stream itself is concurrent. For example, if we are zipping
1547−-- two streams that are concurrently composed, we should be able to pull from
1548−-- the SVar of that concurrent stream instead of creating another redundant
1549−-- SVar on top of it.
1550−--
1551−{-# INLINE_NORMAL mkParallel #-}
1552−mkParallel :: (IsStream t, MonadAsync m)
1553− => State K.Stream m a -> t m a -> m (t m a)
1554−mkParallel st m = fmap fromStreamD $ D.mkParallel st (toStreamD m)
1555−-- mkParallel = Par.mkParallel
1556−
15571543infixr 0 |$
15581544infixr 0 |$.
15591545
@@ -1586,11 +1572,10 @@ infixl 1 |&.
15861572-- @since 0.3.0
15871573{-# INLINE (|$) #-}
15881574(|$) :: (IsStream t, MonadAsync m) => (t m a -> t m b) -> (t m a -> t m b)
1589−f |$ x = D.fromStreamD $
1590− D.applyParallel (D.toStreamD . f . D.fromStreamD) (D.toStreamD x)
1591−-- (|$) = Par.applyParallel
1575+-- (|$) f = f . Async.mkAsync
1576+(|$) f = f . D.mkParallel
15921577
1593−-- | Same as (|$).
1578+-- | Same as '|$'.
15941579--
15951580-- /Internal/
15961581--
@@ -1639,10 +1624,10 @@ x |& f = f |$ x
16391624-- @since 0.3.0
16401625{-# INLINE (|$.) #-}
16411626(|$.) :: (IsStream t, MonadAsync m) => (t m a -> m b) -> (t m a -> m b)
1642−(|$.) = D.foldParallel
1643−-- (|$.) = Par.foldParallel
1627+-- (|$.) f = f . Async.mkAsync
1628+(|$.) f = f . D.mkParallel
16441629
1645−-- | Same as '(|$.)'.
1630+-- | Same as '|$.'.
16461631--
16471632-- /Internal/
16481633--
@@ -2404,10 +2389,7 @@ merge = mergeBy compare
24042389-- @since 0.6.0
24052390mergeAsyncBy :: (IsStream t, MonadAsync m)
24062391 => (a -> a -> Ordering) -> t m a -> t m a -> t m a
2407−mergeAsyncBy f m1 m2 = K.mkStream $ \st stp sng yld -> do
2408− ma <- mkAsync' st m1
2409− mb <- mkAsync' st m2
2410− K.foldStream st stp sng yld (K.mergeBy f ma mb)
2392+mergeAsyncBy f m1 m2 = K.mergeBy f (D.mkParallel m1) (D.mkParallel m2)
24112393
24122394-- | Like 'mergeByM' but merges concurrently (i.e. both the elements being
24132395-- merged are generated concurrently).
@@ -2415,10 +2397,7 @@ mergeAsyncBy f m1 m2 = K.mkStream $ \st stp sng yld -> do
24152397-- @since 0.6.0
24162398mergeAsyncByM :: (IsStream t, MonadAsync m)
24172399 => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
2418−mergeAsyncByM f m1 m2 = K.mkStream $ \st stp sng yld -> do
2419− ma <- mkAsync' st m1
2420− mb <- mkAsync' st m2
2421− K.foldStream st stp sng yld (K.mergeByM f ma mb)
2400+mergeAsyncByM f m1 m2 = K.mergeByM f (D.mkParallel m1) (D.mkParallel m2)
24222401
24232402------------------------------------------------------------------------------
24242403-- Nesting