on master help sign in
improve performance of foldable instance

* use foldl' based implementations of type class methods instead of using default implementations * reimplement some type class methods to use INLINE pragmas

commit007df998b99bcbea168763fccd79d49164ab04c9 authorHarendra Kumar <harendra.kumar@gmail.com> dateMon 17 Feb 2020, 21:02 (7 years ago) parentb50e1a5
3 files changed+66−8
src/Streamly/Internal/Data/Stream/Instances.hs+45−5 src/Streamly/Internal/Data/Stream/Serial.hs+10−1 src/Streamly/Internal/Data/Stream/Zip.hs+11−2
src/Streamly/Internal/Data/Stream/Instances.hs+45−5
@@ -109,13 +109,53 @@ instance NFData a => NFData (STREAM Identity a) where { \
109109-- Foldable
110110-------------------------------------------------------------------------------
111111
112−-- XXX the foldable instance seems to be quit slow. We can try writing
113−-- custom implementations of foldr and foldl'. If nothing works we can also try
114−-- writing a Foldable for Identity monad rather than for "Foldable m".
112+-- The default Foldable instance has several issues:
113+-- 1) several definitions do not have INLINE on them, so we provide
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+
115119#define FOLDABLE_INSTANCE(STREAM) \
116120instance (Foldable m, Monad m) => Foldable (STREAM m) where { \
117− {-# INLINE foldMap #-}; \
118− foldMap f = fold . P.foldr mappend mempty . fmap f }
121+ \
122+ {-# INLINE foldMap #-}; \
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 }
119159
120160-------------------------------------------------------------------------------
121161-- Traversable
src/Streamly/Internal/Data/Stream/Serial.hs+10−1
@@ -61,8 +61,11 @@ import Control.Monad.IO.Class (MonadIO(..))
6161import Control.Monad.Reader.Class (MonadReader(..))
6262import Control.Monad.State.Class (MonadState(..))
6363import Control.Monad.Trans.Class (MonadTrans(lift))
64+import Data.Coerce (Coercible, coerce)
65+import Data.Foldable (Foldable(foldl'), fold)
6466import Data.Functor.Identity (Identity(..), runIdentity)
65−import Data.Foldable (fold)
67+import Data.Maybe (fromMaybe)
68+import Data.Semigroup (Endo(..))
6669#if __GLASGOW_HASKELL__ < 808
6770import Data.Semigroup (Semigroup(..))
6871#endif
@@ -73,6 +76,7 @@ import Prelude hiding (map, mapM)
7376
7477import Streamly.Internal.Data.Stream.StreamK (IsStream(..), adapt, Stream, mkStream,
7578 foldStream)
79+import Streamly.Internal.Data.Strict (Maybe'(..), toMaybe)
7680import qualified Streamly.Internal.Data.Stream.Prelude as P
7781import qualified Streamly.Internal.Data.Stream.StreamK as K
7882import qualified Streamly.Internal.Data.Stream.StreamD as D
@@ -80,6 +84,11 @@ import qualified Streamly.Internal.Data.Stream.StreamD as D
8084#include "Instances.hs"
8185#include "inline.hs"
8286
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+
8392------------------------------------------------------------------------------
8493-- SerialT
8594------------------------------------------------------------------------------
src/Streamly/Internal/Data/Stream/Zip.hs+11−2
@@ -45,8 +45,11 @@ import Control.DeepSeq (NFData(..))
4545#if MIN_VERSION_deepseq(1,4,3)
4646import Control.DeepSeq (NFData1(..))
4747#endif
48−import Data.Functor.Identity (Identity, runIdentity)
49−import Data.Foldable (fold)
48+import Data.Coerce (Coercible, coerce)
49+import Data.Foldable (Foldable(foldl'), fold)
50+import Data.Functor.Identity (Identity(..), runIdentity)
51+import Data.Maybe (fromMaybe)
52+import Data.Semigroup (Endo(..))
5053#if __GLASGOW_HASKELL__ < 808
5154import Data.Semigroup (Semigroup(..))
5255#endif
@@ -56,6 +59,7 @@ import Text.Read (Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec,
5659import Prelude hiding (map, repeat, zipWith)
5760
5861import Streamly.Internal.Data.Stream.StreamK (IsStream(..), Stream)
62+import Streamly.Internal.Data.Strict (Maybe'(..), toMaybe)
5963import Streamly.Internal.Data.SVar (MonadAsync)
6064
6165import qualified Streamly.Internal.Data.Stream.Prelude as P
@@ -70,6 +74,11 @@ import qualified Streamly.Internal.Data.Stream.StreamD as S
7074
7175#include "Instances.hs"
7276
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+
7382-- | Like 'zipWith' but using a monadic zipping function.
7483--
7584-- @since 0.4.0