on master help sign in
Add benchmarks for Foldable and Traversable
commitb50e1a52850f8fdd1e70797815047cc7333f743d authorHarendra Kumar <harendra.kumar@gmail.com> dateMon 17 Feb 2020, 20:37 (7 years ago) parent233ea77
4 files changed+211−8
benchmark/Linear.hs+51−2 benchmark/Streamly/Benchmark/Prelude.hs+146−4 src/Streamly/Internal/Data/Stream/Prelude.hs+13−1 src/Streamly/Internal/Prelude.hs+1−1
benchmark/Linear.hs+51−2
@@ -152,7 +152,42 @@ main = do
152152 , benchPureSrc "IsString.fromString" (Ops.sourceIsString value)
153153 , benchPureSink value "showsPrec pure streams" Ops.showInstance
154154 , benchPureSink value "foldl'" Ops.pureFoldl'
155− , benchPureSink value "foldable/foldl'" Ops.foldableFoldl'
155+ ]
156+ , bgroup "foldable"
157+ [ -- Foldable instance
158+ -- type class operations
159+ bench "foldl'" $ nf (Ops.foldableFoldl' value) 1
160+ , bench "foldrElem" $ nf (Ops.foldableFoldrElem value) 1
161+ -- , bench "null" $ nf (Ops.foldableNull value) 1
162+ , bench "elem" $ nf (Ops.foldableElem value) 1
163+ , bench "length" $ nf (Ops.foldableLength value) 1
164+ , bench "sum" $ nf (Ops.foldableSum value) 1
165+ , bench "product" $ nf (Ops.foldableProduct value) 1
166+ , bench "minimum" $ nf (Ops.foldableMin value) 1
167+ , bench "maximum" $ nf (Ops.foldableMax value) 1
168+ , bench "length . toList" $
169+ nf (length . Ops.foldableToList value) 1
170+
171+ -- folds
172+ , bench "notElem" $ nf (Ops.foldableNotElem value) 1
173+ , bench "find" $ nf (Ops.foldableFind value) 1
174+ , bench "all" $ nf (Ops.foldableAll value) 1
175+ , bench "any" $ nf (Ops.foldableAny value) 1
176+ , bench "and" $ nf (Ops.foldableAnd value) 1
177+ , bench "or" $ nf (Ops.foldableOr value) 1
178+
179+ -- Note: minimumBy/maximumBy do not work in constant memory they are in
180+ -- the O(n) group of benchmarks down below in this file.
181+
182+ -- Applicative and Traversable operations
183+ -- TBD: traverse_
184+ , benchIOSink1 "mapM_" (Ops.foldableMapM_ value)
185+ -- TBD: for_
186+ -- TBD: forM_
187+ , benchIOSink1 "sequence_" (Ops.foldableSequence_ value)
188+ -- TBD: sequenceA_
189+ -- TBD: asum
190+ -- , benchIOSink1 "msum" (Ops.foldableMsum value)
156191 ]
157192 , bgroup "generation"
158193 [ -- Most basic, barely stream continuations running
@@ -502,6 +537,14 @@ main = do
502537 , benchIOSink bufValue "reverse" (Ops.reverse 1)
503538 , benchIOSink bufValue "reverse'" (Ops.reverse' 1)
504539
540+ -- XXX the definitions of minimumBy and maximumBy in Data.Foldable use
541+ -- foldl1 which does not work in constant memory for our implementation.
542+ -- It works in constant memory for lists but even for lists it takes 15x
543+ -- more time compared to our foldl' based implementation.
544+ , bench "minimumBy" $ nf (flip Ops.foldableMinBy 1) value
545+ , bench "maximumBy" $ nf (flip Ops.foldableMaxBy 1) value
546+ , bench "minimumByList" $ nf (flip Ops.foldableListMinBy 1) value
547+
505548 -- XXX can these be streaming? Can we have special read/show style type
506549 -- classes supporting streaming?
507550 , mkString bufValue `deepseq` (bench "readsPrec pure streams" $
@@ -514,7 +557,6 @@ main = do
514557 -- XXX streaming operations that can potentially be fixed
515558
516559 -- XXX These consume a lot of stack, fix or segregate
517− , benchPureSink bufValue "foldable/sum" Ops.foldableSum
518560 , benchIOSink bufValue "tail" Ops.tail
519561 , benchIOSink bufValue "nullHeadTail" Ops.nullHeadTail
520562
@@ -523,4 +565,11 @@ main = do
523565 , benchIOSrc1 "concatUnfoldRoundrobinRepl (x/4,4)"
524566 (Ops.concatUnfoldRoundrobinRepl4xN bufValue)
525567 ]
568+ , bgroup "traversable"
569+ [ -- Traversable instance
570+ benchPureSinkIO bufValue "traverse" Ops.traversableTraverse
571+ , benchPureSinkIO bufValue "sequenceA" Ops.traversableSequenceA
572+ , benchPureSinkIO bufValue "mapM" Ops.traversableMapM
573+ , benchPureSinkIO bufValue "sequence" Ops.traversableSequence
574+ ]
526575 ]
benchmark/Streamly/Benchmark/Prelude.hs+146−4
@@ -136,6 +136,16 @@ sourceUnfoldrMAction value n = S.serially $ S.unfoldrM step n
136136 then return Nothing
137137 else return (Just (return cnt, cnt + 1))
138138
139+{-# INLINE sourceUnfoldrAction #-}
140+sourceUnfoldrAction :: (S.IsStream t, Monad m, Monad m1)
141+ => Int -> Int -> t m (m1 Int)
142+sourceUnfoldrAction value n = S.serially $ S.unfoldr step n
143+ where
144+ step cnt =
145+ if cnt > n + value
146+ then Nothing
147+ else (Just (return cnt, cnt + 1))
148+
139149-- fromIndices
140150
141151{-# INLINE sourceFromIndices #-}
@@ -275,6 +285,15 @@ toList = S.toList
275285toListRev :: Monad m => Stream m Int -> m [Int]
276286toListRev = Internal.toListRev
277287
288+{-# INLINE foldrMElem #-}
289+foldrMElem :: Monad m => Int -> Stream m Int -> m Bool
290+foldrMElem e m = S.foldrM (\x xs -> if x == e then return P.True else xs)
291+ (return P.False) m
292+
293+{-# INLINE foldrMToStream #-}
294+foldrMToStream :: Monad m => Stream m Int -> m (Stream Identity Int)
295+foldrMToStream = S.foldr S.cons S.nil
296+
278297foldrMBuild = S.foldrM (\x xs -> xs >>= return . (x :)) (return [])
279298foldl'Build = S.foldl' (flip (:)) []
280299foldlM'Build = S.foldlM' (\xs x -> return $ x : xs) []
@@ -1047,14 +1066,137 @@ readInstanceList str =
10471066pureFoldl' :: Stream Identity Int -> Int
10481067pureFoldl' = runIdentity . S.foldl' (+) 0
10491068
1069+-------------------------------------------------------------------------------
1070+-- Foldable Instance
1071+-------------------------------------------------------------------------------
1072+
10501073{-# INLINE foldableFoldl' #-}
1051−foldableFoldl' :: Stream Identity Int -> Int
1052−foldableFoldl' = F.foldl' (+) 0
1074+foldableFoldl' :: Int -> Int -> Int
1075+foldableFoldl' value n =
1076+ F.foldl' (+) 0 (sourceUnfoldr value n :: S.SerialT Identity Int)
1077+
1078+{-# INLINE foldableFoldrElem #-}
1079+foldableFoldrElem :: Int -> Int -> Bool
1080+foldableFoldrElem value n =
1081+ F.foldr (\x xs -> if x == value then P.True else xs)
1082+ (P.False)
1083+ (sourceUnfoldr value n :: S.SerialT Identity Int)
10531084
10541085{-# INLINE foldableSum #-}
1055−foldableSum :: Stream Identity Int -> Int
1056−foldableSum = P.sum
1086+foldableSum :: Int -> Int -> Int
1087+foldableSum value n =
1088+ P.sum (sourceUnfoldr value n :: S.SerialT Identity Int)
1089+
1090+{-# INLINE foldableProduct #-}
1091+foldableProduct :: Int -> Int -> Int
1092+foldableProduct value n =
1093+ P.product (sourceUnfoldr value n :: S.SerialT Identity Int)
1094+
1095+{-# INLINE foldableNull #-}
1096+foldableNull :: Int -> Int -> Bool
1097+foldableNull value n =
1098+ P.null (sourceUnfoldr value n :: S.SerialT Identity Int)
1099+
1100+{-# INLINE foldableElem #-}
1101+foldableElem :: Int -> Int -> Bool
1102+foldableElem value n =
1103+ P.elem value (sourceUnfoldr value n :: S.SerialT Identity Int)
1104+
1105+{-# INLINE foldableNotElem #-}
1106+foldableNotElem :: Int -> Int -> Bool
1107+foldableNotElem value n =
1108+ P.notElem value (sourceUnfoldr value n :: S.SerialT Identity Int)
1109+
1110+{-# INLINE foldableFind #-}
1111+foldableFind :: Int -> Int -> Maybe Int
1112+foldableFind value n =
1113+ F.find (== (value + 1)) (sourceUnfoldr value n :: S.SerialT Identity Int)
1114+
1115+{-# INLINE foldableAll #-}
1116+foldableAll :: Int -> Int -> Bool
1117+foldableAll value n =
1118+ P.all (<= (value + 1)) (sourceUnfoldr value n :: S.SerialT Identity Int)
1119+
1120+{-# INLINE foldableAny #-}
1121+foldableAny :: Int -> Int -> Bool
1122+foldableAny value n =
1123+ P.any (> (value + 1)) (sourceUnfoldr value n :: S.SerialT Identity Int)
1124+
1125+{-# INLINE foldableAnd #-}
1126+foldableAnd :: Int -> Int -> Bool
1127+foldableAnd value n =
1128+ P.and $ S.map (<= (value + 1)) (sourceUnfoldr value n :: S.SerialT Identity Int)
1129+
1130+{-# INLINE foldableOr #-}
1131+foldableOr :: Int -> Int -> Bool
1132+foldableOr value n =
1133+ P.or $ S.map (> (value + 1)) (sourceUnfoldr value n :: S.SerialT Identity Int)
1134+
1135+{-# INLINE foldableLength #-}
1136+foldableLength :: Int -> Int -> Int
1137+foldableLength value n =
1138+ P.length (sourceUnfoldr value n :: S.SerialT Identity Int)
1139+
1140+{-# INLINE foldableMin #-}
1141+foldableMin :: Int -> Int -> Int
1142+foldableMin value n =
1143+ P.minimum (sourceUnfoldr value n :: S.SerialT Identity Int)
1144+
1145+{-# INLINE foldableMax #-}
1146+foldableMax :: Int -> Int -> Int
1147+foldableMax value n =
1148+ P.maximum (sourceUnfoldr value n :: S.SerialT Identity Int)
1149+
1150+{-# INLINE foldableMinBy #-}
1151+foldableMinBy :: Int -> Int -> Int
1152+foldableMinBy value n =
1153+ F.minimumBy compare (sourceUnfoldr value n :: S.SerialT Identity Int)
1154+
1155+{-# INLINE foldableListMinBy #-}
1156+foldableListMinBy :: Int -> Int -> Int
1157+foldableListMinBy value n = F.minimumBy compare [1..value+n]
1158+
1159+{-# INLINE foldableMaxBy #-}
1160+foldableMaxBy :: Int -> Int -> Int
1161+foldableMaxBy value n =
1162+ F.maximumBy compare (sourceUnfoldr value n :: S.SerialT Identity Int)
1163+
1164+{-# INLINE foldableToList #-}
1165+foldableToList :: Int -> Int -> [Int]
1166+foldableToList value n =
1167+ F.toList (sourceUnfoldr value n :: S.SerialT Identity Int)
1168+
1169+{-# INLINE foldableMapM_ #-}
1170+foldableMapM_ :: Monad m => Int -> Int -> m ()
1171+foldableMapM_ value n =
1172+ F.mapM_ (\_ -> return ()) (sourceUnfoldr value n :: S.SerialT Identity Int)
1173+
1174+{-# INLINE foldableSequence_ #-}
1175+foldableSequence_ :: Int -> Int -> IO ()
1176+foldableSequence_ value n =
1177+ F.sequence_ (sourceUnfoldrAction value n :: S.SerialT Identity (IO Int))
1178+
1179+{-# INLINE foldableMsum #-}
1180+foldableMsum :: Int -> Int -> IO Int
1181+foldableMsum value n =
1182+ F.msum (sourceUnfoldrAction value n :: S.SerialT Identity (IO Int))
1183+
1184+-------------------------------------------------------------------------------
1185+-- Traversable Instance
1186+-------------------------------------------------------------------------------
1187+
1188+{-# INLINE traversableTraverse #-}
1189+traversableTraverse :: Stream Identity Int -> IO (Stream Identity Int)
1190+traversableTraverse = P.traverse return
1191+
1192+{-# INLINE traversableSequenceA #-}
1193+traversableSequenceA :: Stream Identity Int -> IO (Stream Identity Int)
1194+traversableSequenceA = P.sequenceA . P.fmap return
10571195
10581196{-# INLINE traversableMapM #-}
10591197traversableMapM :: Stream Identity Int -> IO (Stream Identity Int)
10601198traversableMapM = P.mapM return
1199+
1200+{-# INLINE traversableSequence #-}
1201+traversableSequence :: Stream Identity Int -> IO (Stream Identity Int)
1202+traversableSequence = P.sequence . P.fmap return
src/Streamly/Internal/Data/Stream/Prelude.hs+13−1
@@ -52,6 +52,10 @@ module Streamly.Internal.Data.Stream.Prelude
5252 , eqBy
5353 , cmpBy
5454
55+ -- * Foldable instance
56+ , minimum
57+ , maximum
58+
5559 -- * Nesting
5660 , K.concatMapBy
5761 , K.concatMap
@@ -64,7 +68,7 @@ module Streamly.Internal.Data.Stream.Prelude
6468where
6569
6670import Control.Monad.Trans (MonadTrans(..))
67−import Prelude hiding (foldr)
71+import Prelude hiding (foldr, minimum, maximum)
6872import qualified Prelude
6973
7074import Streamly.Internal.Data.Fold.Types (Fold (..))
@@ -250,6 +254,14 @@ cmpBy
250254 => (a -> b -> Ordering) -> t m a -> t m b -> m Ordering
251255cmpBy f m1 m2 = D.cmpBy f (D.toStreamD m1) (D.toStreamD m2)
252256
257+{-# INLINE minimum #-}
258+minimum :: (IsStream t, Monad m, Ord a) => t m a -> m (Maybe a)
259+minimum m = S.minimum (toStreamS m)
260+
261+{-# INLINE maximum #-}
262+maximum :: (IsStream t, Monad m, Ord a) => t m a -> m (Maybe a)
263+maximum m = S.maximum (toStreamS m)
264+
253265------------------------------------------------------------------------------
254266-- Fold Utilities
255267------------------------------------------------------------------------------
src/Streamly/Internal/Prelude.hs+1−1
@@ -1328,7 +1328,7 @@ minimumBy cmp m = S.minimumBy cmp (toStreamS m)
13281328-- @since 0.1.0
13291329{-# INLINE maximum #-}
13301330maximum :: (Monad m, Ord a) => SerialT m a -> m (Maybe a)
1331−maximum m = S.maximum (toStreamS m)
1331+maximum = P.maximum
13321332
13331333-- | Determine the maximum element in a stream using the supplied comparison
13341334-- function.