2-- Module : Stream.Expand
3-- Copyright : (c) 2018 Composewell Technologies
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
8{-# LANGUAGE FlexibleContexts #-}
9{-# LANGUAGE ScopedTypeVariables #-}
10{-# LANGUAGE RankNTypes #-}
11{-# LANGUAGE TupleSections #-}
14{-# OPTIONS_GHC -Wno-deprecations #-}
17#ifdef __HADDOCK_VERSION__
22{-# LANGUAGE TemplateHaskell #-}
23{-# OPTIONS_GHC -fplugin Test.Inspection.Plugin #-}
26module Stream.Expand (benchmarks) where
29import GHC.Types (SPEC(..))
32import qualified Streamly.Internal.Data.Stream as D
35import qualified Stream.Common as Common
36import qualified Streamly.Internal.Data.Unfold as UF
39import qualified Streamly.Internal.Data.Stream.IsStream as S
40import qualified Streamly.Internal.Data.Stream.IsStream as StreamK
41import Streamly.Benchmark.Prelude
42 ( sourceFoldMapM, sourceFoldMapWith, sourceFoldMapWithM
43 , sourceFoldMapWithStream, concatFoldableWith, concatForFoldableWith)
45import qualified Streamly.Internal.Data.Stream as S
47import Streamly.Internal.Data.Stream (Stream)
48import Streamly.Internal.Data.StreamK (StreamK, CrossStreamK)
49import qualified Control.Applicative as AP
50import qualified Streamly.Internal.Data.Fold as Fold
51import qualified Streamly.Internal.Data.StreamK as StreamK
53import qualified Streamly.Internal.Data.Stream as StreamK
59import Streamly.Benchmark.Common
60import Prelude hiding (concatMap)
62-------------------------------------------------------------------------------
63-- Iteration/looping utilities
64-------------------------------------------------------------------------------
67{-# INLINE iterateN #-}
68iterateN :: (Int -> a -> a) -> a -> Int -> a
69iterateN g initial count = f count initial
74 f i x = f (i - 1) (g i x)
76-- Iterate a transformation over a singleton stream
77{-# INLINE iterateSingleton #-}
78iterateSingleton :: Applicative m =>
79 (Int -> CrossStreamK m Int -> CrossStreamK m Int)
83iterateSingleton g count n =
86 $ iterateN g (StreamK.mkCross (StreamK.fromPure n)) count
90-- XXX need to check why this is slower than the explicit recursion above, even
91-- if the above code is written in a foldr like head recursive way. We also
92-- need to try this with foldlM' once #150 is fixed.
93-- However, it is perhaps best to keep the iteration benchmarks independent of
94-- foldrM and any related fusion issues.
95{-# INLINE _iterateSingleton #-}
98 => (Int -> Stream m Int -> Stream m Int)
102_iterateSingleton g value n = S.foldrM g (return n) $ sourceIntFromTo value n
105-------------------------------------------------------------------------------
107-------------------------------------------------------------------------------
109-------------------------------------------------------------------------------
111-------------------------------------------------------------------------------
113{-# INLINE serial2 #-}
114serial2 :: Int -> Int -> IO ()
118 (fromStream $ sourceUnfoldrM count n)
119 (fromStream $ sourceUnfoldrM count (n + 1))
121{-# INLINE serial4 #-}
122serial4 :: Int -> Int -> IO ()
127 (fromStream $ sourceUnfoldrM count n)
128 (fromStream $ sourceUnfoldrM count (n + 1)))
130 (fromStream $ sourceUnfoldrM count (n + 2))
131 (fromStream $ sourceUnfoldrM count (n + 3)))
133o_1_space_joining :: Int -> [Benchmark]
134o_1_space_joining value =
136 [ benchIOSrc1 "serial (2,x/2)" (serial2 (value `div` 2))
137 , benchIOSrc1 "serial (2,2,x/4)" (serial4 (value `div` 4))
141-------------------------------------------------------------------------------
142-- Concat Foldable containers
143-------------------------------------------------------------------------------
146o_1_space_concatFoldable :: Int -> [Benchmark]
147o_1_space_concatFoldable value =
148 [ bgroup "concat-foldable"
149 [ benchIOSrc "foldMapWith (<>) (List)"
150 (sourceFoldMapWith value)
151 , benchIOSrc "foldMapWith (<>) (Stream)"
152 (sourceFoldMapWithStream value)
153 , benchIOSrc "foldMapWithM (<>) (List)"
154 (sourceFoldMapWithM value)
155 , benchIOSrc "S.concatFoldableWith (<>) (List)"
156 (concatFoldableWith value)
157 , benchIOSrc "S.concatForFoldableWith (<>) (List)"
158 (concatForFoldableWith value)
159 , benchIOSrc "foldMapM (List)" (sourceFoldMapM value)
164-------------------------------------------------------------------------------
166-------------------------------------------------------------------------------
168-- concatMap unfoldrM/unfoldrM
170{-# INLINE concatMap #-}
171concatMap :: Int -> Int -> Int -> IO ()
172concatMap outer inner n =
173 drain $ toStream $ StreamK.concatMap
174 (\_ -> fromStream $ sourceUnfoldrM inner n)
175 (fromStream $ sourceUnfoldrM outer n)
178{-# INLINE concatMapM #-}
179concatMapM :: Int -> Int -> Int -> IO ()
180concatMapM outer inner n =
182 (\_ -> return $ sourceUnfoldrM inner n)
183 (sourceUnfoldrM outer n)
186inspect $ hasNoTypeClasses 'concatMap
187inspect $ 'concatMap `hasNoType` ''SPEC
191-- concatMap unfoldr/unfoldr
193{-# INLINE concatMapPure #-}
194concatMapPure :: Int -> Int -> Int -> IO ()
195concatMapPure outer inner n =
196 drain $ toStream $ StreamK.concatMap
197 (\_ -> fromStream $ sourceUnfoldr inner n)
198 (fromStream $ sourceUnfoldr outer n)
201#if __GLASGOW_HASKELL__ >= 906
202inspect $ hasNoTypeClassesExcept 'concatMapPure [''Applicative]
204inspect $ hasNoTypeClasses 'concatMapPure
206inspect $ 'concatMapPure `hasNoType` ''SPEC
209-- concatMap replicate/unfoldrM
211{-# INLINE concatMapRepl #-}
212concatMapRepl :: Int -> Int -> Int -> IO ()
213concatMapRepl outer inner n =
214 drain $ toStream $ StreamK.concatMap
215 (fromStream . S.replicate inner) (fromStream $ sourceUnfoldrM outer n)
218#if __GLASGOW_HASKELL__ >= 906
219inspect $ hasNoTypeClassesExcept 'concatMapRepl [''Applicative]
221inspect $ hasNoTypeClasses 'concatMapRepl
223inspect $ 'concatMapRepl `hasNoType` ''SPEC
229{-# INLINE concatMapWithSerial #-}
230concatMapWithSerial :: Int -> Int -> Int -> IO ()
231concatMapWithSerial = concatStreamsWith Common.append
234inspect $ hasNoTypeClasses 'concatMapWithSerial
235inspect $ 'concatMapWithSerial `hasNoType` ''SPEC
239{-# INLINE concatMapWithAppend #-}
240concatMapWithAppend :: Int -> Int -> Int -> IO ()
241concatMapWithAppend = concatStreamsWith Common.append2
244inspect $ hasNoTypeClasses 'concatMapWithAppend
245inspect $ 'concatMapWithAppend `hasNoType` ''SPEC
251{-# INLINE mergeMapWithSerial #-}
252mergeMapWithSerial :: Int -> Int -> Int -> IO ()
253mergeMapWithSerial = mergeMapWith Common.append
256{-# INLINE mergeMapWithAppend #-}
257mergeMapWithAppend :: Int -> Int -> Int -> IO ()
258mergeMapWithAppend = mergeMapWith Common.append2
264-- unfoldMany replicate/unfoldrM
266{-# INLINE unfoldManyRepl #-}
267unfoldManyRepl :: Int -> Int -> Int -> IO ()
268unfoldManyRepl outer inner n =
272 (fmap ((inner,) . return) (sourceUnfoldrM outer n))
276inspect $ hasNoTypeClasses 'unfoldManyRepl
277inspect $ 'unfoldManyRepl `hasNoType` ''D.ConcatMapUState
278inspect $ 'unfoldManyRepl `hasNoType` ''SPEC
281o_1_space_concat :: Int -> [Benchmark]
282o_1_space_concat value = sqrtVal `seq`
284 [ benchIOSrc1 "concatMapPure (n of 1)"
285 (concatMapPure value 1)
286 , benchIOSrc1 "concatMapPure (sqrt n of sqrt n)"
287 (concatMapPure sqrtVal sqrtVal)
288 , benchIOSrc1 "concatMapPure (1 of n)"
289 (concatMapPure 1 value)
291 , benchIOSrc1 "concatMap (n of 1)"
293 , benchIOSrc1 "concatMap (sqrt n of sqrt n)"
294 (concatMap sqrtVal sqrtVal)
295 , benchIOSrc1 "concatMap (1 of n)"
299 -- This is for comparison with foldMapWith
300 , benchIOSrc "concatMapId (n of 1) (fromFoldable)"
301 (S.concatMap id . sourceConcatMapId value)
303 , benchIOSrc1 "concatMapM (n of 1)"
305 , benchIOSrc1 "concatMapM (sqrt n of sqrt n)"
306 (concatMapM sqrtVal sqrtVal)
307 , benchIOSrc1 "concatMapM (1 of n)"
313 -- This is for comparison with foldMapWith
314 , benchIOSrc "concatMapWithId (n of 1) (fromFoldable)"
315 (toStream . S.concatMapWith Common.append id . sourceConcatMapId value)
318 , benchIOSrc1 "concatMapWith (n of 1)"
319 (concatMapWithSerial value 1)
320 , benchIOSrc1 "concatMapWith (sqrt n of sqrt n)"
321 (concatMapWithSerial sqrtVal sqrtVal)
322 , benchIOSrc1 "concatMapWith (1 of n)"
323 (concatMapWithSerial 1 value)
326 -- quadratic with number of outer streams
327 , benchIOSrc1 "concatMapWithAppend (2 of n/2)"
328 (concatMapWithAppend 2 (value `div` 2))
332 -- concatMap vs unfoldMany
333 , benchIOSrc1 "concatMapRepl (sqrt n of sqrt n)"
334 (concatMapRepl sqrtVal sqrtVal)
335 , benchIOSrc1 "unfoldManyRepl (sqrt n of sqrt n)"
336 (unfoldManyRepl sqrtVal sqrtVal)
342 sqrtVal = round $ sqrt (fromIntegral value :: Double)
345o_n_space_merge :: Int -> [Benchmark]
346o_n_space_merge value = sqrtVal `seq`
349 -------------------mergeMapWith-----------------
351 -- Use large number of streams to check scalability
353 benchIOSrc1 "mergeMapWithSerial (n of 1)"
354 (mergeMapWithSerial value 1)
355 , benchIOSrc1 "mergeMapWithSerial (sqrtVal of sqrtVal)"
356 (mergeMapWithSerial sqrtVal sqrtVal)
357 , benchIOSrc1 "mergeMapWithSerial (2 of n/2)"
358 (mergeMapWithSerial 2 (value `div` 2))
361 , benchIOSrc1 "mergeMapWithAppend (n of 1)"
362 (mergeMapWithAppend value 1)
363 , benchIOSrc1 "mergeMapWithAppend (sqrtVal of sqrtVal)"
364 (mergeMapWithAppend sqrtVal sqrtVal)
371 sqrtVal = round $ sqrt (fromIntegral value :: Double)
374-------------------------------------------------------------------------------
376-------------------------------------------------------------------------------
378o_1_space_applicative :: Int -> [Benchmark]
379o_1_space_applicative value =
380 [ bgroup "Applicative"
381 [ benchIO "(*>) (sqrt n x sqrt n)" $ apDiscardFst value
382 , benchIO "(<*) (sqrt n x sqrt n)" $ apDiscardSnd value
383 , benchIO "(<*>) (sqrt n x sqrt n)" $ toNullAp value
384 , benchIO "liftA2 (sqrt n x sqrt n)" $ apLiftA2 value
389o_n_space_applicative :: Int -> [Benchmark]
390o_n_space_applicative value =
392 [ benchIOSrc "(*>) (n times)" $
393 iterateSingleton ((*>) . pure) value
394 , benchIOSrc "(<*) (n times)" $
395 iterateSingleton (\x xs -> xs <* pure x) value
396 , benchIOSrc "(<*>) (n times)" $
397 iterateSingleton (\x xs -> pure (+ x) <*> xs) value
398 , benchIOSrc "liftA2 (n times)" $
399 iterateSingleton (AP.liftA2 (+) . pure) value
404-------------------------------------------------------------------------------
406-------------------------------------------------------------------------------
408o_1_space_monad :: Int -> [Benchmark]
409o_1_space_monad value =
411 [ benchIO "(>>) (sqrt n x sqrt n)" $ monadThen value
412 , benchIO "(>>=) (sqrt n x sqrt n)" $ toNullM value
413 , benchIO "(>>=) (sqrt n x sqrt n) (filterAllOut)" $
415 , benchIO "(>>=) (sqrt n x sqrt n) (filterAllIn)" $
417 , benchIO "(>>=) (sqrt n x sqrt n) (filterSome)" $
419 , benchIO "(>>=) (sqrt n x sqrt n) (breakAfterSome)" $
421 , benchIO "(>>=) (cubert n x cubert n x cubert n)" $
427-- This is a good benchmark but inefficient way to compute primes. As we see a
428-- new prime we keep appending a division filter for all the future numbers.
430sieve :: Monad m => StreamK m Int -> StreamK m Int
431sieve s = StreamK.concatEffect $ do
432 r <- StreamK.uncons s
434 Just (prime, rest) ->
435 -- XXX Use K.filter or rewrite to K.filter
436 let f = S.filter (\n -> n `mod` prime /= 0)
437 in pure $ prime `StreamK.cons` sieve (fromStream $ f $ toStream rest)
438 Nothing -> pure StreamK.nil
440o_n_space_iterated :: Int -> [Benchmark]
441o_n_space_iterated value =
444 benchIO "concatEffect prime sieve (n/4)"
450 $ S.enumerateFromTo 2 (value `div` 4 + n))
451 , benchIOSrc "(>>) (n times)" $
452 iterateSingleton ((>>) . pure) value
453 , benchIOSrc "(>>=) (n times)" $
454 iterateSingleton (\x xs -> xs >>= \y -> return (x + y)) value
459o_n_space_monad :: Int -> [Benchmark]
460o_n_space_monad value =
462 [ benchIO "(>>=) (sqrt n x sqrt n) (toList)" $
464 , benchIO "(>>=) (sqrt n x sqrt n) (toListSome)" $
469-------------------------------------------------------------------------------
471-------------------------------------------------------------------------------
474toKv :: Int -> (Int, Int)
477{-# INLINE joinWith #-}
478joinWith :: Common.MonadAsync m =>
479 ((Int -> Int -> Bool) -> Stream m Int -> Stream m Int -> Stream m b)
484 drain $ j (==) (sourceUnfoldrM val i) (sourceUnfoldrM val (val `div` 2))
486{-# INLINE joinMapWith #-}
487joinMapWith :: Common.MonadAsync m =>
488 (Stream m (Int, Int) -> Stream m (Int, Int) -> Stream m b)
495 (fmap toKv (sourceUnfoldrM val i))
496 (fmap toKv (sourceUnfoldrM val (val `div` 2)))
498o_n_heap_buffering :: Int -> [Benchmark]
499o_n_heap_buffering value =
502 benchIOSrc1 "joinInnerGeneric (sqrtVal)"
503 $ joinWith S.joinInnerGeneric sqrtVal
504 , benchIOSrc1 "joinInner"
505 $ joinMapWith S.joinInner halfVal
506 , benchIOSrc1 "joinLeftGeneric (sqrtVal)"
507 $ joinWith S.joinLeftGeneric sqrtVal
508 , benchIOSrc1 "joinLeft "
509 $ joinMapWith S.joinLeft halfVal
510 , benchIOSrc1 "joinOuterGeneric (sqrtVal)"
511 $ joinWith S.joinOuterGeneric sqrtVal
512 , benchIOSrc1 "joinOuter"
513 $ joinMapWith S.joinOuter halfVal
514 , benchIOSrc1 "filterInStreamGenericBy (sqrtVal)"
515 $ joinWith S.filterInStreamGenericBy sqrtVal
516 , benchIOSrc1 "filterInStreamAscBy"
517 $ joinMapWith (S.filterInStreamAscBy compare) halfVal
523 halfVal = value `div` 2
524 sqrtVal = round $ sqrt (fromIntegral value :: Double)
527-------------------------------------------------------------------------------
529-------------------------------------------------------------------------------
531-- In addition to gauge options, the number of elements in the stream can be
532-- passed using the --stream-size option.
534{-# ANN benchmarks "HLint: ignore" #-}
535benchmarks :: String -> Int -> [Benchmark]
536benchmarks moduleName size =
537 [ bgroup (o_1_space_prefix moduleName) $ Prelude.concat
540 o_1_space_joining size
542 , o_1_space_concatFoldable size
544 , o_1_space_concat size
545 , o_1_space_applicative size
546 , o_1_space_monad size
548 , bgroup (o_n_space_prefix moduleName) $ Prelude.concat
553 , o_n_space_merge size
554 , o_n_space_iterated size
555 , o_n_space_applicative size
559 , bgroup (o_n_heap_prefix moduleName) $
561 o_n_heap_buffering size
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list