2-- Module : Serial.NestedStream
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 #-}
12#ifdef __HADDOCK_VERSION__
17{-# LANGUAGE TemplateHaskell #-}
18{-# OPTIONS_GHC -fplugin Test.Inspection.Plugin #-}
21module Serial.NestedStream (benchmarks) where
23import Control.Monad.Trans.Class (lift)
25import qualified Control.Applicative as AP
28import GHC.Types (SPEC(..))
31import qualified Streamly.Internal.Data.Stream.StreamD as D
34import qualified Streamly.Prelude as S
35import qualified Streamly.Internal.Data.Stream.IsStream as Internal
36import qualified Streamly.Internal.Data.Unfold as UF
39import Streamly.Prelude (SerialT, fromSerial, serial)
40import Streamly.Benchmark.Common
41import Streamly.Benchmark.Prelude
42import Prelude hiding (concatMap)
44-------------------------------------------------------------------------------
45-- Iteration/looping utilities
46-------------------------------------------------------------------------------
48{-# INLINE iterateN #-}
49iterateN :: (Int -> a -> a) -> a -> Int -> a
50iterateN g initial count = f count initial
55 f i x = f (i - 1) (g i x)
57-- Iterate a transformation over a singleton stream
58{-# INLINE iterateSingleton #-}
59iterateSingleton :: S.MonadAsync m
60 => (Int -> SerialT m Int -> SerialT m Int)
64iterateSingleton g count n = iterateN g (return n) count
66-- XXX need to check why this is slower than the explicit recursion above, even
67-- if the above code is written in a foldr like head recursive way. We also
68-- need to try this with foldlM' once #150 is fixed.
69-- However, it is perhaps best to keep the iteration benchmarks independent of
70-- foldrM and any related fusion issues.
71{-# INLINE _iterateSingleton #-}
74 => (Int -> SerialT m Int -> SerialT m Int)
78_iterateSingleton g value n = S.foldrM g (return n) $ sourceIntFromTo value n
80-------------------------------------------------------------------------------
82-------------------------------------------------------------------------------
84-------------------------------------------------------------------------------
86-------------------------------------------------------------------------------
89serial2 :: Int -> Int -> IO ()
92 S.serial (sourceUnfoldrM count n) (sourceUnfoldrM count (n + 1))
95serial4 :: Int -> Int -> IO ()
99 (S.serial (sourceUnfoldrM count n) (sourceUnfoldrM count (n + 1)))
101 (sourceUnfoldrM count (n + 2))
102 (sourceUnfoldrM count (n + 3)))
104{-# INLINE append2 #-}
105append2 :: Int -> Int -> IO ()
108 Internal.append (sourceUnfoldrM count n) (sourceUnfoldrM count (n + 1))
110{-# INLINE append4 #-}
111append4 :: Int -> Int -> IO ()
116 (sourceUnfoldrM count n)
117 (sourceUnfoldrM count (n + 1)))
119 (sourceUnfoldrM count (n + 2))
120 (sourceUnfoldrM count (n + 3)))
123inspect $ hasNoTypeClasses 'append2
124inspect $ 'append2 `hasNoType` ''SPEC
125inspect $ 'append2 `hasNoType` ''D.AppendState
128o_1_space_joining :: Int -> [Benchmark]
129o_1_space_joining value =
131 [ benchIOSrc1 "serial (2,x/2)" (serial2 (value `div` 2))
132 , benchIOSrc1 "append (2,x/2)" (append2 (value `div` 2))
133 , benchIOSrc1 "serial (2,2,x/4)" (serial4 (value `div` 4))
134 , benchIOSrc1 "append (2,2,x/4)" (append4 (value `div` 4))
138-------------------------------------------------------------------------------
139-- Concat Foldable containers
140-------------------------------------------------------------------------------
142o_1_space_concatFoldable :: Int -> [Benchmark]
143o_1_space_concatFoldable value =
144 [ bgroup "concat-foldable"
145 [ benchIOSrc fromSerial "foldMapWith (<>) (List)"
146 (sourceFoldMapWith value)
147 , benchIOSrc fromSerial "foldMapWith (<>) (Stream)"
148 (sourceFoldMapWithStream value)
149 , benchIOSrc fromSerial "foldMapWithM (<>) (List)"
150 (sourceFoldMapWithM value)
151 , benchIOSrc fromSerial "S.concatFoldableWith (<>) (List)"
152 (concatFoldableWith value)
153 , benchIOSrc fromSerial "S.concatForFoldableWith (<>) (List)"
154 (concatForFoldableWith value)
155 , benchIOSrc fromSerial "foldMapM (List)" (sourceFoldMapM value)
159-------------------------------------------------------------------------------
161-------------------------------------------------------------------------------
163-- concatMap unfoldrM/unfoldrM
165{-# INLINE concatMap #-}
166concatMap :: Int -> Int -> Int -> IO ()
167concatMap outer inner n =
168 S.drain $ S.concatMap
169 (\_ -> sourceUnfoldrM inner n)
170 (sourceUnfoldrM outer n)
172{-# INLINE concatMapM #-}
173concatMapM :: Int -> Int -> Int -> IO ()
174concatMapM outer inner n =
175 S.drain $ S.concatMapM
176 (\_ -> return $ sourceUnfoldrM inner n)
177 (sourceUnfoldrM outer n)
180inspect $ hasNoTypeClasses 'concatMap
181inspect $ 'concatMap `hasNoType` ''SPEC
184-- concatMap unfoldr/unfoldr
186{-# INLINE concatMapPure #-}
187concatMapPure :: Int -> Int -> Int -> IO ()
188concatMapPure outer inner n =
189 S.drain $ S.concatMap
190 (\_ -> sourceUnfoldr inner n)
191 (sourceUnfoldr outer n)
194inspect $ hasNoTypeClasses 'concatMapPure
195inspect $ 'concatMapPure `hasNoType` ''SPEC
198-- concatMap replicate/unfoldrM
200{-# INLINE concatMapRepl #-}
201concatMapRepl :: Int -> Int -> Int -> IO ()
202concatMapRepl outer inner n =
203 S.drain $ S.concatMap (S.replicate inner) (sourceUnfoldrM outer n)
206inspect $ hasNoTypeClasses 'concatMapRepl
207inspect $ 'concatMapRepl `hasNoType` ''SPEC
212{-# INLINE concatMapWithSerial #-}
213concatMapWithSerial :: Int -> Int -> Int -> IO ()
214concatMapWithSerial = concatStreamsWith S.serial
217inspect $ hasNoTypeClasses 'concatMapWithSerial
218inspect $ 'concatMapWithSerial `hasNoType` ''SPEC
221{-# INLINE concatMapWithAppend #-}
222concatMapWithAppend :: Int -> Int -> Int -> IO ()
223concatMapWithAppend = concatStreamsWith Internal.append
226inspect $ hasNoTypeClasses 'concatMapWithAppend
227inspect $ 'concatMapWithAppend `hasNoType` ''SPEC
232{-# INLINE concatPairWithSerial #-}
233concatPairWithSerial :: Int -> Int -> Int -> IO ()
234concatPairWithSerial = concatPairsWith Internal.serial
236{-# INLINE concatPairWithAppend #-}
237concatPairWithAppend :: Int -> Int -> Int -> IO ()
238concatPairWithAppend = concatPairsWith Internal.append
242-- unfoldMany replicate/unfoldrM
244{-# INLINE unfoldManyRepl #-}
245unfoldManyRepl :: Int -> Int -> Int -> IO ()
246unfoldManyRepl outer inner n =
249 (UF.lmap return (UF.replicateM inner))
250 (sourceUnfoldrM outer n)
253inspect $ hasNoTypeClasses 'unfoldManyRepl
254inspect $ 'unfoldManyRepl `hasNoType` ''D.ConcatMapUState
255inspect $ 'unfoldManyRepl `hasNoType` ''SPEC
258o_1_space_concat :: Int -> [Benchmark]
259o_1_space_concat value = sqrtVal `seq`
261 [ benchIOSrc1 "concatMapPure (n of 1)"
262 (concatMapPure value 1)
263 , benchIOSrc1 "concatMapPure (sqrt n of sqrt n)"
264 (concatMapPure sqrtVal sqrtVal)
265 , benchIOSrc1 "concatMapPure (1 of n)"
266 (concatMapPure 1 value)
268 -- This is for comparison with foldMapWith
269 , benchIOSrc fromSerial "concatMapId (n of 1) (fromFoldable)"
270 (S.concatMap id . sourceConcatMapId value)
272 , benchIOSrc1 "concatMap (n of 1)"
274 , benchIOSrc1 "concatMap (sqrt n of sqrt n)"
275 (concatMap sqrtVal sqrtVal)
276 , benchIOSrc1 "concatMap (1 of n)"
279 , benchIOSrc1 "concatMapM (n of 1)"
281 , benchIOSrc1 "concatMapM (sqrt n of sqrt n)"
282 (concatMapM sqrtVal sqrtVal)
283 , benchIOSrc1 "concatMapM (1 of n)"
286 -- This is for comparison with foldMapWith
287 , benchIOSrc fromSerial "concatMapWithId (n of 1) (fromFoldable)"
288 (S.concatMapWith serial id . sourceConcatMapId value)
290 , benchIOSrc1 "concatMapWith (n of 1)"
291 (concatMapWithSerial value 1)
292 , benchIOSrc1 "concatMapWith (sqrt n of sqrt n)"
293 (concatMapWithSerial sqrtVal sqrtVal)
294 , benchIOSrc1 "concatMapWith (1 of n)"
295 (concatMapWithSerial 1 value)
297 -- quadratic with number of outer streams
298 , benchIOSrc1 "concatMapWithAppend (2 of n/2)"
299 (concatMapWithAppend 2 (value `div` 2))
301 -- concatMap vs unfoldMany
302 , benchIOSrc1 "concatMapRepl (sqrt n of sqrt n)"
303 (concatMapRepl sqrtVal sqrtVal)
304 , benchIOSrc1 "unfoldManyRepl (sqrt n of sqrt n)"
305 (unfoldManyRepl sqrtVal sqrtVal)
311 sqrtVal = round $ sqrt (fromIntegral value :: Double)
313o_n_space_concat :: Int -> [Benchmark]
314o_n_space_concat value = sqrtVal `seq`
317 -------------------concatPairsWith-----------------
319 -- Use large number of streams to check scalability
321 benchIOSrc1 "concatPairWithSerial (n of 1)"
322 (concatPairWithSerial value 1)
323 , benchIOSrc1 "concatPairWithSerial (sqrtVal of sqrtVal)"
324 (concatPairWithSerial sqrtVal sqrtVal)
325 , benchIOSrc1 "concatPairWithSerial (2 of n/2)"
326 (concatPairWithSerial 2 (value `div` 2))
328 , benchIOSrc1 "concatPairWithAppend (n of 1)"
329 (concatPairWithAppend value 1)
330 , benchIOSrc1 "concatPairWithAppend (sqrtVal of sqrtVal)"
331 (concatPairWithAppend sqrtVal sqrtVal)
337 sqrtVal = round $ sqrt (fromIntegral value :: Double)
339-------------------------------------------------------------------------------
341-------------------------------------------------------------------------------
343o_1_space_applicative :: Int -> [Benchmark]
344o_1_space_applicative value =
345 [ bgroup "Applicative"
346 [ benchIO "(*>) (sqrt n x sqrt n)" $ apDiscardFst value fromSerial
347 , benchIO "(<*) (sqrt n x sqrt n)" $ apDiscardSnd value fromSerial
348 , benchIO "(<*>) (sqrt n x sqrt n)" $ toNullAp value fromSerial
349 , benchIO "liftA2 (sqrt n x sqrt n)" $ apLiftA2 value fromSerial
353o_n_space_applicative :: Int -> [Benchmark]
354o_n_space_applicative value =
355 [ bgroup "Applicative"
356 [ benchIOSrc fromSerial "(*>) (n times)" $
357 iterateSingleton ((*>) . pure) value
358 , benchIOSrc fromSerial "(<*) (n times)" $
359 iterateSingleton (\x xs -> xs <* pure x) value
360 , benchIOSrc fromSerial "(<*>) (n times)" $
361 iterateSingleton (\x xs -> pure (+ x) <*> xs) value
362 , benchIOSrc fromSerial "liftA2 (n times)" $
363 iterateSingleton (AP.liftA2 (+) . pure) value
367-------------------------------------------------------------------------------
369-------------------------------------------------------------------------------
371o_1_space_monad :: Int -> [Benchmark]
372o_1_space_monad value =
374 [ benchIO "(>>) (sqrt n x sqrt n)" $ monadThen value fromSerial
375 , benchIO "(>>=) (sqrt n x sqrt n)" $ toNullM value fromSerial
376 , benchIO "(>>=) (sqrt n x sqrt n) (filterAllOut)" $
377 filterAllOutM value fromSerial
378 , benchIO "(>>=) (sqrt n x sqrt n) (filterAllIn)" $
379 filterAllInM value fromSerial
380 , benchIO "(>>=) (sqrt n x sqrt n) (filterSome)" $
381 filterSome value fromSerial
382 , benchIO "(>>=) (sqrt n x sqrt n) (breakAfterSome)" $
383 breakAfterSome value fromSerial
384 , benchIO "(>>=) (cubert n x cubert n x cubert n)" $
385 toNullM3 value fromSerial
389-- This is a good benchmark but inefficient way to compute primes. As we see a
390-- new prime we keep appending a division filter for all the future numbers.
392sieve :: Monad m => SerialT m Int -> SerialT m Int
394 r <- lift $ S.uncons s
396 Just (prime, rest) ->
397 prime `S.cons` sieve (S.filter (\n -> n `mod` prime /= 0) rest)
400o_n_space_monad :: Int -> [Benchmark]
401o_n_space_monad value =
403 [ benchIOSrc fromSerial "(>>) (n times)" $
404 iterateSingleton ((>>) . pure) value
405 , benchIOSrc fromSerial "(>>=) (n times)" $
406 iterateSingleton (\x xs -> xs >>= \y -> return (x + y)) value
407 , benchIO "(>>=) (sqrt n x sqrt n) (toList)" $
408 toListM value fromSerial
409 , benchIO "(>>=) (sqrt n x sqrt n) (toListSome)" $
410 toListSome value fromSerial
411 , benchIO "naive prime sieve (n/4)"
412 (\n -> S.sum $ sieve $ S.enumerateFromTo 2 (value `div` 4 + n))
416-------------------------------------------------------------------------------
418-------------------------------------------------------------------------------
420toKv :: Int -> (Int, Int)
423{-# INLINE joinWith #-}
424joinWith :: (S.MonadAsync m) =>
425 ((Int -> Int -> Bool) -> SerialT m Int -> SerialT m Int -> SerialT m b)
430 S.drain $ j (==) (sourceUnfoldrM val i) (sourceUnfoldrM val (val `div` 2))
432{-# INLINE joinMapWith #-}
433joinMapWith :: (S.MonadAsync m) =>
434 (SerialT m (Int, Int) -> SerialT m (Int, Int) -> SerialT m b)
441 (fmap toKv (sourceUnfoldrM val i))
442 (fmap toKv (sourceUnfoldrM val (val `div` 2)))
444o_n_heap_buffering :: Int -> [Benchmark]
445o_n_heap_buffering value =
448 benchIOSrc1 "joinInner (sqrtVal)"
449 $ joinWith Internal.joinInner sqrtVal
450 , benchIOSrc1 "joinInnerMap"
451 $ joinMapWith Internal.joinInnerMap halfVal
452 , benchIOSrc1 "joinLeft (sqrtVal)"
453 $ joinWith Internal.joinLeft sqrtVal
454 , benchIOSrc1 "joinLeftMap "
455 $ joinMapWith Internal.joinLeftMap halfVal
456 , benchIOSrc1 "joinOuter (sqrtVal)"
457 $ joinWith Internal.joinOuter sqrtVal
458 , benchIOSrc1 "joinOuterMap"
459 $ joinMapWith Internal.joinOuterMap halfVal
460 , benchIOSrc1 "intersectBy (sqrtVal)"
461 $ joinWith Internal.intersectBy sqrtVal
462 , benchIOSrc1 "intersectBySorted"
463 $ joinMapWith (Internal.intersectBySorted compare) halfVal
469 halfVal = value `div` 2
470 sqrtVal = round $ sqrt (fromIntegral value :: Double)
472-------------------------------------------------------------------------------
474-------------------------------------------------------------------------------
476-- In addition to gauge options, the number of elements in the stream can be
477-- passed using the --stream-size option.
479benchmarks :: String -> Int -> [Benchmark]
480benchmarks moduleName size =
481 [ bgroup (o_1_space_prefix moduleName) $ Prelude.concat
484 o_1_space_joining size
485 , o_1_space_concatFoldable size
486 , o_1_space_concat size
488 , o_1_space_applicative size
489 , o_1_space_monad size
492 , bgroup (o_n_space_prefix moduleName) $ Prelude.concat
495 o_n_space_applicative size
496 , o_n_space_monad size
497 , o_n_space_concat size
499 , bgroup (o_n_heap_prefix moduleName) $
501 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