on master help sign in
Add benchmarks for Foldable and Traversable
b50e1a5Harendra Kumar
benchmark/Linear.hs+51−2
@@ -152,7 +152,42 @@ main = do
152 , benchPureSrc "IsString.fromString" (Ops.sourceIsString value)
152 , benchPureSrc "IsString.fromString" (Ops.sourceIsString value)
153 , benchPureSink value "showsPrec pure streams" Ops.showInstance
153 , benchPureSink value "showsPrec pure streams" Ops.showInstance
154 , benchPureSink value "foldl'" Ops.pureFoldl'
154 , benchPureSink value "foldl'" Ops.pureFoldl'
155+ ]
156+ , bgroup "foldable"
157+ [ -- Foldable instance
158+ -- type class operations
155− , benchPureSink value "foldable/foldl'" Ops.foldableFoldl'
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)
156 ]
191 ]
157 , bgroup "generation"
192 , bgroup "generation"
158 [ -- Most basic, barely stream continuations running
193 [ -- Most basic, barely stream continuations running
@@ -502,6 +537,14 @@ main = do
502 , benchIOSink bufValue "reverse" (Ops.reverse 1)
537 , benchIOSink bufValue "reverse" (Ops.reverse 1)
503 , benchIOSink bufValue "reverse'" (Ops.reverse' 1)
538 , benchIOSink bufValue "reverse'" (Ops.reverse' 1)
504
539
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+
505 -- XXX can these be streaming? Can we have special read/show style type
548 -- XXX can these be streaming? Can we have special read/show style type
506 -- classes supporting streaming?
549 -- classes supporting streaming?
507 , mkString bufValue `deepseq` (bench "readsPrec pure streams" $
550 , mkString bufValue `deepseq` (bench "readsPrec pure streams" $
@@ -514,7 +557,6 @@ main = do
514 -- XXX streaming operations that can potentially be fixed
557 -- XXX streaming operations that can potentially be fixed
515
558
516 -- XXX These consume a lot of stack, fix or segregate
559 -- XXX These consume a lot of stack, fix or segregate
517− , benchPureSink bufValue "foldable/sum" Ops.foldableSum
518 , benchIOSink bufValue "tail" Ops.tail
560 , benchIOSink bufValue "tail" Ops.tail
519 , benchIOSink bufValue "nullHeadTail" Ops.nullHeadTail
561 , benchIOSink bufValue "nullHeadTail" Ops.nullHeadTail
520
562
@@ -523,4 +565,11 @@ main = do
523 , benchIOSrc1 "concatUnfoldRoundrobinRepl (x/4,4)"
565 , benchIOSrc1 "concatUnfoldRoundrobinRepl (x/4,4)"
524 (Ops.concatUnfoldRoundrobinRepl4xN bufValue)
566 (Ops.concatUnfoldRoundrobinRepl4xN bufValue)
525 ]
567 ]
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+ ]
526 ]
575 ]
benchmark/Streamly/Benchmark/Prelude.hs+146−4
@@ -136,6 +136,16 @@ sourceUnfoldrMAction value n = S.serially $ S.unfoldrM step n
136 then return Nothing
136 then return Nothing
137 else return (Just (return cnt, cnt + 1))
137 else return (Just (return cnt, cnt + 1))
138
138
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+
139-- fromIndices
149-- fromIndices
140
150
141{-# INLINE sourceFromIndices #-}
151{-# INLINE sourceFromIndices #-}
@@ -275,6 +285,15 @@ toList = S.toList
275toListRev :: Monad m => Stream m Int -> m [Int]
285toListRev :: Monad m => Stream m Int -> m [Int]
276toListRev = Internal.toListRev
286toListRev = Internal.toListRev
277
287
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+
278foldrMBuild = S.foldrM (\x xs -> xs >>= return . (x :)) (return [])
297foldrMBuild = S.foldrM (\x xs -> xs >>= return . (x :)) (return [])
279foldl'Build = S.foldl' (flip (:)) []
298foldl'Build = S.foldl' (flip (:)) []
280foldlM'Build = S.foldlM' (\xs x -> return $ x : xs) []
299foldlM'Build = S.foldlM' (\xs x -> return $ x : xs) []
@@ -1047,14 +1066,137 @@ readInstanceList str =
1047pureFoldl' :: Stream Identity Int -> Int
1066pureFoldl' :: Stream Identity Int -> Int
1048pureFoldl' = runIdentity . S.foldl' (+) 0
1067pureFoldl' = runIdentity . S.foldl' (+) 0
1049
1068
1069+-------------------------------------------------------------------------------
1070+-- Foldable Instance
1071+-------------------------------------------------------------------------------
1072+
1050{-# INLINE foldableFoldl' #-}
1073{-# INLINE foldableFoldl' #-}
1051−foldableFoldl' :: Stream Identity Int -> Int
1074+foldableFoldl' :: Int -> Int -> Int
1052−foldableFoldl' = F.foldl' (+) 0
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)
1053
1084
1054{-# INLINE foldableSum #-}
1085{-# INLINE foldableSum #-}
1055−foldableSum :: Stream Identity Int -> Int
1086+foldableSum :: Int -> Int -> Int
1056−foldableSum = P.sum
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
1057
1195
1058{-# INLINE traversableMapM #-}
1196{-# INLINE traversableMapM #-}
1059traversableMapM :: Stream Identity Int -> IO (Stream Identity Int)
1197traversableMapM :: Stream Identity Int -> IO (Stream Identity Int)
1060traversableMapM = P.mapM return
1198traversableMapM = 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
52 , eqBy
52 , eqBy
53 , cmpBy
53 , cmpBy
54
54
55+ -- * Foldable instance
56+ , minimum
57+ , maximum
58+
55 -- * Nesting
59 -- * Nesting
56 , K.concatMapBy
60 , K.concatMapBy
57 , K.concatMap
61 , K.concatMap
@@ -64,7 +68,7 @@ module Streamly.Internal.Data.Stream.Prelude
64where
68where
65
69
66import Control.Monad.Trans (MonadTrans(..))
70import Control.Monad.Trans (MonadTrans(..))
67−import Prelude hiding (foldr)
71+import Prelude hiding (foldr, minimum, maximum)
68import qualified Prelude
72import qualified Prelude
69
73
70import Streamly.Internal.Data.Fold.Types (Fold (..))
74import Streamly.Internal.Data.Fold.Types (Fold (..))
@@ -250,6 +254,14 @@ cmpBy
250 => (a -> b -> Ordering) -> t m a -> t m b -> m Ordering
254 => (a -> b -> Ordering) -> t m a -> t m b -> m Ordering
251cmpBy f m1 m2 = D.cmpBy f (D.toStreamD m1) (D.toStreamD m2)
255cmpBy f m1 m2 = D.cmpBy f (D.toStreamD m1) (D.toStreamD m2)
252
256
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+
253------------------------------------------------------------------------------
265------------------------------------------------------------------------------
254-- Fold Utilities
266-- Fold Utilities
255------------------------------------------------------------------------------
267------------------------------------------------------------------------------
src/Streamly/Internal/Prelude.hs+1−1
@@ -1328,7 +1328,7 @@ minimumBy cmp m = S.minimumBy cmp (toStreamS m)
1328-- @since 0.1.0
1328-- @since 0.1.0
1329{-# INLINE maximum #-}
1329{-# INLINE maximum #-}
1330maximum :: (Monad m, Ord a) => SerialT m a -> m (Maybe a)
1330maximum :: (Monad m, Ord a) => SerialT m a -> m (Maybe a)
1331−maximum m = S.maximum (toStreamS m)
1331+maximum = P.maximum
1332
1332
1333-- | Determine the maximum element in a stream using the supplied comparison
1333-- | Determine the maximum element in a stream using the supplied comparison
1334-- function.
1334-- function.