on master help sign in
improve performance of foldable instance
007df99Harendra Kumar
src/Streamly/Internal/Data/Stream/Instances.hs+45−5
@@ -109,13 +109,53 @@ instance NFData a => NFData (STREAM Identity a) where { \
109-- Foldable
109-- Foldable
110-------------------------------------------------------------------------------
110-------------------------------------------------------------------------------
111
111
112−-- XXX the foldable instance seems to be quit slow. We can try writing
112+-- The default Foldable instance has several issues:
113−-- custom implementations of foldr and foldl'. If nothing works we can also try
113+-- 1) several definitions do not have INLINE on them, so we provide
114−-- writing a Foldable for Identity monad rather than for "Foldable m".
114+-- re-implementations with INLINE pragmas.
115+-- 2) the definitions of sum/product/maximum/minimum are inefficient as they
116+-- use right folds, they cannot run in constant memory. We provide
117+-- implementations using strict left folds here.
118+
115#define FOLDABLE_INSTANCE(STREAM) \
119#define FOLDABLE_INSTANCE(STREAM) \
116instance (Foldable m, Monad m) => Foldable (STREAM m) where { \
120instance (Foldable m, Monad m) => Foldable (STREAM m) where { \
121+ \
117− {-# INLINE foldMap #-}; \
122+ {-# INLINE foldMap #-}; \
118− foldMap f = fold . P.foldr mappend mempty . fmap f }
123+ foldMap f = fold . P.foldr (mappend . f) mempty; \
124+ \
125+ {-# INLINE foldr #-}; \
126+ foldr f z t = appEndo (foldMap (Endo #. f) t) z; \
127+ \
128+ {-# INLINE foldl' #-}; \
129+ foldl' f z0 xs = foldr f' id xs z0 \
130+ where { f' x k z = k $! f z x}; \
131+ \
132+ {-# INLINE length #-}; \
133+ length = foldl' (\n _ -> n + 1) 0; \
134+ \
135+ {-# INLINE elem #-}; \
136+ elem = any . (==); \
137+ \
138+ {-# INLINE maximum #-}; \
139+ maximum = \
140+ fromMaybe (errorWithoutStackTrace $ "maximum: empty stream") \
141+ . toMaybe \
142+ . foldl' getMax Nothing' where { \
143+ getMax Nothing' x = Just' x; \
144+ getMax (Just' mx) x = Just' $! max mx x }; \
145+ \
146+ {-# INLINE minimum #-}; \
147+ minimum = \
148+ fromMaybe (errorWithoutStackTrace $ "minimum: empty stream") \
149+ . toMaybe \
150+ . foldl' getMin Nothing' where { \
151+ getMin Nothing' x = Just' x; \
152+ getMin (Just' mn) x = Just' $! min mn x }; \
153+ \
154+ {-# INLINE sum #-}; \
155+ sum = foldl' (+) 0; \
156+ \
157+ {-# INLINE product #-}; \
158+ product = foldl' (*) 1 }
119
159
120-------------------------------------------------------------------------------
160-------------------------------------------------------------------------------
121-- Traversable
161-- Traversable
src/Streamly/Internal/Data/Stream/Serial.hs+10−1
@@ -61,8 +61,11 @@ import Control.Monad.IO.Class (MonadIO(..))
61import Control.Monad.Reader.Class (MonadReader(..))
61import Control.Monad.Reader.Class (MonadReader(..))
62import Control.Monad.State.Class (MonadState(..))
62import Control.Monad.State.Class (MonadState(..))
63import Control.Monad.Trans.Class (MonadTrans(lift))
63import Control.Monad.Trans.Class (MonadTrans(lift))
64+import Data.Coerce (Coercible, coerce)
65+import Data.Foldable (Foldable(foldl'), fold)
64import Data.Functor.Identity (Identity(..), runIdentity)
66import Data.Functor.Identity (Identity(..), runIdentity)
65−import Data.Foldable (fold)
67+import Data.Maybe (fromMaybe)
68+import Data.Semigroup (Endo(..))
66#if __GLASGOW_HASKELL__ < 808
69#if __GLASGOW_HASKELL__ < 808
67import Data.Semigroup (Semigroup(..))
70import Data.Semigroup (Semigroup(..))
68#endif
71#endif
@@ -73,6 +76,7 @@ import Prelude hiding (map, mapM)
73
76
74import Streamly.Internal.Data.Stream.StreamK (IsStream(..), adapt, Stream, mkStream,
77import Streamly.Internal.Data.Stream.StreamK (IsStream(..), adapt, Stream, mkStream,
75 foldStream)
78 foldStream)
79+import Streamly.Internal.Data.Strict (Maybe'(..), toMaybe)
76import qualified Streamly.Internal.Data.Stream.Prelude as P
80import qualified Streamly.Internal.Data.Stream.Prelude as P
77import qualified Streamly.Internal.Data.Stream.StreamK as K
81import qualified Streamly.Internal.Data.Stream.StreamK as K
78import qualified Streamly.Internal.Data.Stream.StreamD as D
82import qualified Streamly.Internal.Data.Stream.StreamD as D
@@ -80,6 +84,11 @@ import qualified Streamly.Internal.Data.Stream.StreamD as D
80#include "Instances.hs"
84#include "Instances.hs"
81#include "inline.hs"
85#include "inline.hs"
82
86
87+-- XXX move this to Streamly.Internal.Data.Coerce?
88+{-# INLINE (#.) #-}
89+(#.) :: Coercible b c => (b -> c) -> (a -> b) -> (a -> c)
90+(#.) _f = coerce
91+
83------------------------------------------------------------------------------
92------------------------------------------------------------------------------
84-- SerialT
93-- SerialT
85------------------------------------------------------------------------------
94------------------------------------------------------------------------------
src/Streamly/Internal/Data/Stream/Zip.hs+11−2
@@ -45,8 +45,11 @@ import Control.DeepSeq (NFData(..))
45#if MIN_VERSION_deepseq(1,4,3)
45#if MIN_VERSION_deepseq(1,4,3)
46import Control.DeepSeq (NFData1(..))
46import Control.DeepSeq (NFData1(..))
47#endif
47#endif
48+import Data.Coerce (Coercible, coerce)
49+import Data.Foldable (Foldable(foldl'), fold)
48−import Data.Functor.Identity (Identity, runIdentity)
50+import Data.Functor.Identity (Identity(..), runIdentity)
49−import Data.Foldable (fold)
51+import Data.Maybe (fromMaybe)
52+import Data.Semigroup (Endo(..))
50#if __GLASGOW_HASKELL__ < 808
53#if __GLASGOW_HASKELL__ < 808
51import Data.Semigroup (Semigroup(..))
54import Data.Semigroup (Semigroup(..))
52#endif
55#endif
@@ -56,6 +59,7 @@ import Text.Read (Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec,
56import Prelude hiding (map, repeat, zipWith)
59import Prelude hiding (map, repeat, zipWith)
57
60
58import Streamly.Internal.Data.Stream.StreamK (IsStream(..), Stream)
61import Streamly.Internal.Data.Stream.StreamK (IsStream(..), Stream)
62+import Streamly.Internal.Data.Strict (Maybe'(..), toMaybe)
59import Streamly.Internal.Data.SVar (MonadAsync)
63import Streamly.Internal.Data.SVar (MonadAsync)
60
64
61import qualified Streamly.Internal.Data.Stream.Prelude as P
65import qualified Streamly.Internal.Data.Stream.Prelude as P
@@ -70,6 +74,11 @@ import qualified Streamly.Internal.Data.Stream.StreamD as S
70
74
71#include "Instances.hs"
75#include "Instances.hs"
72
76
77+-- XXX move this to Streamly.Internal.Data.Coerce?
78+{-# INLINE (#.) #-}
79+(#.) :: Coercible b c => (b -> c) -> (a -> b) -> (a -> c)
80+(#.) _f = coerce
81+
73-- | Like 'zipWith' but using a monadic zipping function.
82-- | Like 'zipWith' but using a monadic zipping function.
74--
83--
75-- @since 0.4.0
84-- @since 0.4.0