2-- Module : Streamly.Benchmark.Data.Stream.StreamD
3-- Copyright : (c) 2018 Composewell Technologies
6-- Maintainer : streamly@composewell.com
9{-# LANGUAGE FlexibleContexts #-}
10{-# LANGUAGE ScopedTypeVariables #-}
11{-# LANGUAGE TupleSections #-}
12{-# OPTIONS_GHC -Wno-orphans #-}
14#ifdef __HADDOCK_VERSION__
19{-# LANGUAGE TemplateHaskell #-}
20{-# OPTIONS_GHC -fplugin Test.Inspection.Plugin #-}
23module Main (main) where
25import Control.Applicative (liftA2)
26import Control.Monad (when)
27import Data.Maybe (isJust)
28import Gauge (bench, nfIO, bgroup, Benchmark, defaultMain)
29import System.Random (randomRIO)
30import Prelude hiding (tail, mapM_, foldl, last, map, mapM, concatMap, zip)
32import qualified Prelude as P
33import qualified Streamly.Internal.Data.Stream as S
34import qualified Streamly.Internal.Data.Unfold as UF
35import qualified Streamly.Internal.Data.Fold as FL
37import Streamly.Benchmark.Common
40import GHC.Types (SPEC(..))
44-- We try to keep the total number of iterations same irrespective of nesting
45-- of the loops so that the overhead is easy to compare.
46value, value2, value3, value16, maxValue :: Int
48value2 = round (P.fromIntegral value**(1/2::P.Double)) -- double nested loop
49value3 = round (P.fromIntegral value**(1/3::P.Double)) -- triple nested loop
50value16 = round (P.fromIntegral value**(1/16::P.Double)) -- triple nested loop
53-------------------------------------------------------------------------------
54-- Stream generation and elimination
55-------------------------------------------------------------------------------
57type Stream m a = S.Stream m a
59{-# INLINE sourceUnfoldr #-}
60sourceUnfoldr :: Monad m => Int -> Stream m Int
61sourceUnfoldr n = S.unfoldr step n
66 else Just (cnt, cnt + 1)
68{-# INLINE sourceUnfoldrN #-}
69sourceUnfoldrN :: Monad m => Int -> Int -> Stream m Int
70sourceUnfoldrN m n = S.unfoldr step n
75 else Just (cnt, cnt + 1)
77{-# INLINE sourceUnfoldrMN #-}
78sourceUnfoldrMN :: Monad m => Int -> Int -> Stream m Int
79sourceUnfoldrMN m n = S.unfoldrM step n
84 else return (Just (cnt, cnt + 1))
86{-# INLINE sourceUnfoldrM #-}
87sourceUnfoldrM :: Monad m => Int -> Stream m Int
88sourceUnfoldrM n = S.unfoldrM step n
93 else return (Just (cnt, cnt + 1))
95{-# INLINE sourceIntFromTo #-}
96sourceIntFromTo :: Monad m => Int -> Stream m Int
97sourceIntFromTo n = S.enumerateFromToIntegral n (n + value)
99{-# INLINE sourceFromList #-}
100sourceFromList :: Monad m => Int -> Stream m Int
101sourceFromList n = S.fromList [n..n+value]
103-------------------------------------------------------------------------------
105-------------------------------------------------------------------------------
107{-# INLINE runStream #-}
108runStream :: Monad m => Stream m a -> m ()
112mapM_ :: Monad m => Stream m a -> m ()
113mapM_ = S.mapM_ (\_ -> return ())
116toNull :: Monad m => Stream m Int -> m ()
120{-# INLINE nullTail #-}
121{-# INLINE headTail #-}
122uncons, nullTail, headTail
124 => Stream m Int -> m ()
130 Just (_, t) -> uncons t
132{-# INLINE foldBreak #-}
133foldBreak :: Monad m => Stream m Int -> m ()
135 (r, s1) <- S.foldBreak (FL.take 1 FL.length) s
136 when (r /= 0) $ foldBreak s1
139tail :: Monad m => Stream m a -> m ()
140tail s = S.tail s >>= P.mapM_ tail
144 when (not r) $ S.tail s >>= P.mapM_ nullTail
148 when (isJust h) $ S.tail s >>= P.mapM_ headTail
151toList :: Monad m => Stream m Int -> m [Int]
155foldl :: Monad m => Stream m Int -> m Int
156foldl = S.foldl' (+) 0
159last :: Monad m => Stream m Int -> m (Maybe Int)
162-------------------------------------------------------------------------------
164-------------------------------------------------------------------------------
166{-# INLINE transform #-}
167transform :: Monad m => Stream m a -> m ()
170{-# INLINE composeN #-}
173 => Int -> (Stream m Int -> Stream m Int) -> Stream m Int -> m ()
177 2 -> transform . f . f
178 3 -> transform . f . f . f
179 4 -> transform . f . f . f . f
186{-# INLINE mapMaybe #-}
187{-# INLINE mapMaybeM #-}
188{-# INLINE filterEven #-}
189{-# INLINE filterAllOut #-}
190{-# INLINE filterAllIn #-}
191{-# INLINE _takeOne #-}
192{-# INLINE takeAll #-}
193{-# INLINE takeWhileTrue #-}
194{-# INLINE _takeWhileMTrue #-}
195{-# INLINE dropOne #-}
196{-# INLINE dropAll #-}
197{-# INLINE dropWhileTrue #-}
198{-# INLINE _dropWhileMTrue #-}
199{-# INLINE dropWhileFalse #-}
200{-# INLINE _foldrS #-}
201{-# INLINE _foldlS #-}
202{-# INLINE intersperse #-}
203scan, map, fmapD, mapM, mapMaybe, mapMaybeM, filterEven, filterAllOut,
204 filterAllIn, _takeOne, takeAll, takeWhileTrue, _takeWhileMTrue, dropOne,
205 dropAll, dropWhileTrue, _dropWhileMTrue, dropWhileFalse, _foldrS, _foldlS,
208 => Int -> Stream m Int -> m ()
210scan n = composeN n $ S.scanl' (+) 0
211fmapD n = composeN n $ Prelude.fmap (+1)
212map n = composeN n $ S.map (+1)
213mapM n = composeN n $ S.mapM return
214mapMaybe n = composeN n $ S.mapMaybe
215 (\x -> if Prelude.odd x then Nothing else Just x)
216mapMaybeM n = composeN n $ S.mapMaybeM
217 (\x -> if Prelude.odd x then return Nothing else return $ Just x)
218filterEven n = composeN n $ S.filter even
219filterAllOut n = composeN n $ S.filter (> maxValue)
220filterAllIn n = composeN n $ S.filter (<= maxValue)
221_takeOne n = composeN n $ S.take 1
222takeAll n = composeN n $ S.take maxValue
223takeWhileTrue n = composeN n $ S.takeWhile (<= maxValue)
224_takeWhileMTrue n = composeN n $ S.takeWhileM (return . (<= maxValue))
225dropOne n = composeN n $ S.drop 1
226dropAll n = composeN n $ S.drop maxValue
227dropWhileTrue n = composeN n $ S.dropWhile (<= maxValue)
228_dropWhileMTrue n = composeN n $ S.dropWhileM (return . (<= maxValue))
229dropWhileFalse n = composeN n $ S.dropWhile (> maxValue)
230_foldrS n = composeN n $ S.foldrS S.cons S.nil
231_foldlS n = composeN n $ S.foldlS (flip S.cons) S.nil
232intersperse n = composeN n $ S.intersperse maxValue
234-------------------------------------------------------------------------------
236-------------------------------------------------------------------------------
238iterStreamLen, maxIters :: Int
242{-# INLINE iterateSource #-}
245 => (Stream m Int -> Stream m Int) -> Int -> Int -> Stream m Int
246iterateSource g i n = f i (sourceUnfoldrMN iterStreamLen n)
249 f x m = g (f (x P.- 1) m)
251{-# INLINE iterateMapM #-}
252{-# INLINE iterateScan #-}
253{-# INLINE iterateFilterEven #-}
254{-# INLINE iterateTakeAll #-}
255{-# INLINE iterateDropOne #-}
256{-# INLINE iterateDropWhileFalse #-}
257{-# INLINE iterateDropWhileTrue #-}
258iterateMapM, iterateScan, iterateFilterEven, iterateTakeAll, iterateDropOne,
259 iterateDropWhileFalse, iterateDropWhileTrue
261 => Int -> Stream m Int
264iterateScan = iterateSource (S.scanl' (+) 0) (maxIters `div` 10)
265iterateDropWhileFalse = iterateSource (S.dropWhile (> maxValue))
268iterateMapM = iterateSource (S.mapM return) maxIters
269iterateFilterEven = iterateSource (S.filter even) maxIters
270iterateTakeAll = iterateSource (S.take maxValue) maxIters
271iterateDropOne = iterateSource (S.drop 1) maxIters
272iterateDropWhileTrue = iterateSource (S.dropWhile (<= maxValue)) maxIters
274{-# INLINE iterateM #-}
275iterateM :: Monad m => Int -> Stream m Int
276iterateM i = S.take maxIters (S.iterateM (\x -> return (x + 1)) (return i))
278-------------------------------------------------------------------------------
280-------------------------------------------------------------------------------
283eqBy :: (Monad m, P.Eq a) => S.Stream m a -> m P.Bool
284eqBy src = S.eqBy (==) src src
287cmpBy :: (Monad m, P.Ord a) => S.Stream m a -> m P.Ordering
288cmpBy src = S.cmpBy P.compare src src
291zip :: Monad m => Stream m Int -> m ()
292zip src = transform $ S.zipWith (,) src src
294-------------------------------------------------------------------------------
296-------------------------------------------------------------------------------
298{-# INLINE scanMap #-}
299{-# INLINE dropMap #-}
300{-# INLINE dropScan #-}
301{-# INLINE takeDrop #-}
302{-# INLINE takeScan #-}
303{-# INLINE takeMap #-}
304{-# INLINE filterDrop #-}
305{-# INLINE filterTake #-}
306{-# INLINE filterScan #-}
307{-# INLINE filterMap #-}
308scanMap, dropMap, dropScan, takeDrop, takeScan, takeMap, filterDrop,
309 filterTake, filterScan, filterMap
310 :: Monad m => Int -> Stream m Int -> m ()
312scanMap n = composeN n $ S.map (subtract 1) . S.scanl' (+) 0
313dropMap n = composeN n $ S.map (subtract 1) . S.drop 1
314dropScan n = composeN n $ S.scanl' (+) 0 . S.drop 1
315takeDrop n = composeN n $ S.drop 1 . S.take maxValue
316takeScan n = composeN n $ S.scanl' (+) 0 . S.take maxValue
317takeMap n = composeN n $ S.map (subtract 1) . S.take maxValue
318filterDrop n = composeN n $ S.drop 1 . S.filter (<= maxValue)
319filterTake n = composeN n $ S.take maxValue . S.filter (<= maxValue)
320filterScan n = composeN n $ S.scanl' (+) 0 . S.filter (<= maxBound)
321filterMap n = composeN n $ S.map (subtract 1) . S.filter (<= maxValue)
323-------------------------------------------------------------------------------
325-------------------------------------------------------------------------------
327instance Monad f => Applicative (S.Stream f) where
334 {-# INLINE liftA2 #-}
335 liftA2 f x = (<*>) (fmap f x)
338 (*>) = S.crossApplySnd
341 (<*) = S.crossApplyFst
343-- NOTE: even though concatMap for StreamD is 4x faster compared to StreamK,
344-- the monad instance does not seem to be significantly faster.
345instance Monad m => Monad (S.Stream m) where
346 {-# INLINE return #-}
350 (>>=) = flip S.concatMap
355{-# INLINE toNullApNested #-}
356toNullApNested :: Monad m => Stream m Int -> m ()
357toNullApNested s = runStream $ do
360{-# INLINE toNullNested #-}
361toNullNested :: Monad m => Stream m Int -> m ()
362toNullNested s = runStream $ do
367{-# INLINE toNullNested3 #-}
368toNullNested3 :: Monad m => Stream m Int -> m ()
369toNullNested3 s = runStream $ do
375{-# INLINE filterAllOutNested #-}
378 => Stream m Int -> m ()
379filterAllOutNested str = runStream $ do
387{-# INLINE filterAllInNested #-}
390 => Stream m Int -> m ()
391filterAllInNested str = runStream $ do
399-------------------------------------------------------------------------------
401-------------------------------------------------------------------------------
403-- concatMap unfoldrM/unfoldrM
405{-# INLINE concatMap #-}
406concatMap :: Int -> Int -> Int -> IO ()
407concatMap outer inner n =
408 S.drain $ S.concatMap
409 (\_ -> sourceUnfoldrMN inner n)
410 (sourceUnfoldrMN outer n)
413inspect $ hasNoTypeClasses 'concatMap
414inspect $ 'concatMap `hasNoType` ''SPEC
417-- concatMap unfoldr/unfoldr
419{-# INLINE concatMapPure #-}
420concatMapPure :: Int -> Int -> Int -> IO ()
421concatMapPure outer inner n =
422 S.drain $ S.concatMap
423 (\_ -> sourceUnfoldrN inner n)
424 (sourceUnfoldrN outer n)
427inspect $ hasNoTypeClasses 'concatMapPure
428inspect $ 'concatMapPure `hasNoType` ''SPEC
431-- concatMap replicate/unfoldrM
433{-# INLINE concatMapRepl #-}
434concatMapRepl :: Int -> Int -> Int -> IO ()
435concatMapRepl outer inner n =
436 S.drain $ S.concatMap (S.replicate inner) (sourceUnfoldrMN outer n)
439inspect $ hasNoTypeClasses 'concatMapRepl
440inspect $ 'concatMapRepl `hasNoType` ''SPEC
443-- unfoldMany replicate/unfoldrM
445{-# INLINE unfoldManyRepl #-}
446unfoldManyRepl :: Int -> Int -> Int -> IO ()
447unfoldManyRepl outer inner n =
451 (S.map ((inner,) . return) (sourceUnfoldrMN outer n))
454inspect $ hasNoTypeClasses 'unfoldManyRepl
455inspect $ 'unfoldManyRepl `hasNoType` ''S.ConcatMapUState
456inspect $ 'unfoldManyRepl `hasNoType` ''SPEC
459-------------------------------------------------------------------------------
461-------------------------------------------------------------------------------
464moduleName = "Data.StreamD"
466o_1_space :: [Benchmark]
468 [ bgroup (o_1_space_prefix moduleName)
469 [ bgroup "generation"
470 [ benchFold "unfoldr" toNull sourceUnfoldr
471 , benchFold "unfoldrM" toNull sourceUnfoldrM
472 , benchFold "intFromTo" toNull sourceIntFromTo
474 , benchFold "fromList" toNull sourceFromList
476 , bgroup "elimination"
477 [ benchFold "toNull" toNull sourceUnfoldrM
478 , benchFold "mapM_" mapM_ sourceUnfoldrM
479 , benchFold "uncons" uncons sourceUnfoldrM
480 , benchFold "foldBreak" foldBreak sourceUnfoldrM
481 , benchFold "foldl'" foldl sourceUnfoldrM
482 , benchFold "last" last sourceUnfoldrM
485 [ benchFold "toNullAp" toNullApNested (sourceUnfoldrMN value2)
486 , benchFold "toNull" toNullNested (sourceUnfoldrMN value2)
487 , benchFold "toNull3" toNullNested3 (sourceUnfoldrMN value3)
488 , benchFold "filterAllIn" filterAllInNested (sourceUnfoldrMN value2)
489 , benchFold "filterAllOut" filterAllOutNested (sourceUnfoldrMN value2)
490 , benchFold "toNullApPure" toNullApNested (sourceUnfoldrN value2)
491 , benchFold "toNullPure" toNullNested (sourceUnfoldrN value2)
492 , benchFold "toNull3Pure" toNullNested3 (sourceUnfoldrN value3)
493 , benchFold "filterAllInPure" filterAllInNested (sourceUnfoldrN value2)
494 , benchFold "filterAllOutPure" filterAllOutNested (sourceUnfoldrN value2)
496 , bgroup "transformation"
497 [ benchFold "scan" (scan 1) sourceUnfoldrM
498 , benchFold "map" (map 1) sourceUnfoldrM
499 , benchFold "fmap" (fmapD 1) sourceUnfoldrM
500 , benchFold "mapM" (mapM 1) sourceUnfoldrM
501 , benchFold "mapMaybe" (mapMaybe 1) sourceUnfoldrM
502 , benchFold "mapMaybeM" (mapMaybeM 1) sourceUnfoldrM
504 , bgroup "transformationX4"
505 [ benchFold "scan" (scan 4) sourceUnfoldrM
506 , benchFold "map" (map 4) sourceUnfoldrM
507 , benchFold "fmap" (fmapD 4) sourceUnfoldrM
508 , benchFold "mapM" (mapM 4) sourceUnfoldrM
509 , benchFold "mapMaybe" (mapMaybe 4) sourceUnfoldrM
510 , benchFold "mapMaybeM" (mapMaybeM 4) sourceUnfoldrM
511 -- XXX this is horribly slow
512 -- , benchFold "concatMap" (concatMap 4) (sourceUnfoldrMN value16)
516 [ benchIOSrc1 "concatMapPure (n of 1)"
517 (concatMapPure value 1)
518 , benchIOSrc1 "concatMapPure (sqrt n of sqrt n)"
519 (concatMapPure value2 value2)
520 , benchIOSrc1 "concatMapPure (1 of n)"
521 (concatMapPure 1 value)
523 , benchIOSrc1 "concatMap (n of 1)"
525 , benchIOSrc1 "concatMap (sqrt n of sqrt n)"
526 (concatMap value2 value2)
527 , benchIOSrc1 "concatMap (1 of n)"
530 -- concatMap vs unfoldMany
531 , benchIOSrc1 "concatMapRepl (sqrt n of sqrt n)"
532 (concatMapRepl value2 value2)
533 , benchIOSrc1 "unfoldManyRepl (sqrt n of sqrt n)"
534 (unfoldManyRepl value2 value2)
537 [ benchFold "filter-even" (filterEven 1) sourceUnfoldrM
538 , benchFold "filter-all-out" (filterAllOut 1) sourceUnfoldrM
539 , benchFold "filter-all-in" (filterAllIn 1) sourceUnfoldrM
540 , benchFold "take-all" (takeAll 1) sourceUnfoldrM
541 , benchFold "takeWhile-true" (takeWhileTrue 1) sourceUnfoldrM
542 , benchFold "drop-one" (dropOne 1) sourceUnfoldrM
543 , benchFold "drop-all" (dropAll 1) sourceUnfoldrM
544 , benchFold "dropWhile-true" (dropWhileTrue 1) sourceUnfoldrM
545 , benchFold "dropWhile-false" (dropWhileFalse 1) sourceUnfoldrM
547 , bgroup "filteringX4"
548 [ benchFold "filter-even" (filterEven 4) sourceUnfoldrM
549 , benchFold "filter-all-out" (filterAllOut 4) sourceUnfoldrM
550 , benchFold "filter-all-in" (filterAllIn 4) sourceUnfoldrM
551 , benchFold "take-all" (takeAll 4) sourceUnfoldrM
552 , benchFold "takeWhile-true" (takeWhileTrue 4) sourceUnfoldrM
553 , benchFold "drop-one" (dropOne 4) sourceUnfoldrM
554 , benchFold "drop-all" (dropAll 4) sourceUnfoldrM
555 , benchFold "dropWhile-true" (dropWhileTrue 4) sourceUnfoldrM
556 , benchFold "dropWhile-false" (dropWhileFalse 4) sourceUnfoldrM
559 [ benchFold "eqBy" eqBy sourceUnfoldrM
560 , benchFold "cmpBy" cmpBy sourceUnfoldrM
561 , benchFold "zip" zip sourceUnfoldrM
564 [ benchFold "scan-map" (scanMap 1) sourceUnfoldrM
565 , benchFold "drop-map" (dropMap 1) sourceUnfoldrM
566 , benchFold "drop-scan" (dropScan 1) sourceUnfoldrM
567 , benchFold "take-drop" (takeDrop 1) sourceUnfoldrM
568 , benchFold "take-scan" (takeScan 1) sourceUnfoldrM
569 , benchFold "take-map" (takeMap 1) sourceUnfoldrM
570 , benchFold "filter-drop" (filterDrop 1) sourceUnfoldrM
571 , benchFold "filter-take" (filterTake 1) sourceUnfoldrM
572 , benchFold "filter-scan" (filterScan 1) sourceUnfoldrM
573 , benchFold "filter-map" (filterMap 1) sourceUnfoldrM
576 [ benchFold "scan-map" (scanMap 2) sourceUnfoldrM
577 , benchFold "drop-map" (dropMap 2) sourceUnfoldrM
578 , benchFold "drop-scan" (dropScan 2) sourceUnfoldrM
579 , benchFold "take-drop" (takeDrop 2) sourceUnfoldrM
580 , benchFold "take-scan" (takeScan 2) sourceUnfoldrM
581 , benchFold "take-map" (takeMap 2) sourceUnfoldrM
582 , benchFold "filter-drop" (filterDrop 2) sourceUnfoldrM
583 , benchFold "filter-take" (filterTake 2) sourceUnfoldrM
584 , benchFold "filter-scan" (filterScan 2) sourceUnfoldrM
585 , benchFold "filter-map" (filterMap 2) sourceUnfoldrM
588 [ benchFold "scan-map" (scanMap 4) sourceUnfoldrM
589 , benchFold "drop-map" (dropMap 4) sourceUnfoldrM
590 , benchFold "drop-scan" (dropScan 4) sourceUnfoldrM
591 , benchFold "take-drop" (takeDrop 4) sourceUnfoldrM
592 , benchFold "take-scan" (takeScan 4) sourceUnfoldrM
593 , benchFold "take-map" (takeMap 4) sourceUnfoldrM
594 , benchFold "filter-drop" (filterDrop 4) sourceUnfoldrM
595 , benchFold "filter-take" (filterTake 4) sourceUnfoldrM
596 , benchFold "filter-scan" (filterScan 4) sourceUnfoldrM
597 , benchFold "filter-map" (filterMap 4) sourceUnfoldrM
602-- | Takes a source, and uses it with a default drain/fold method.
604benchD :: P.String -> (Int -> Stream P.IO Int) -> Benchmark
605benchD name f = bench name $ nfIO $ randomRIO (1,1) >>= toNull . f
607o_n_stack :: [Benchmark]
609 [ bgroup (o_n_stack_prefix moduleName)
610 [ bgroup "elimination"
611 [ benchFold "tail" tail sourceUnfoldrM
612 , benchFold "nullTail" nullTail sourceUnfoldrM
613 , benchFold "headTail" headTail sourceUnfoldrM
615 , bgroup "transformation"
617 -- this is horribly slow
618 -- benchFold "foldrS" (_foldrS 1) sourceUnfoldrM
619 -- XXX why do these need so much stack
620 benchFold "intersperse" (intersperse 1) (sourceUnfoldrMN value2)
621 , benchFold "interspersePure" (intersperse 1) (sourceUnfoldrN value2)
623 , bgroup "transformationX4"
625 benchFold "intersperse" (intersperse 4) (sourceUnfoldrMN value16)
628 [ benchD "mapM" iterateMapM
629 , benchD "scan(1/10)" iterateScan
630 , benchD "filterEven" iterateFilterEven
631 , benchD "takeAll" iterateTakeAll
632 , benchD "dropOne" iterateDropOne
633 , benchD "dropWhileFalse(1/10)" iterateDropWhileFalse
634 , benchD "dropWhileTrue" iterateDropWhileTrue
635 , benchD "iterateM" iterateM
640o_n_space :: [Benchmark]
642 [ bgroup (o_n_space_prefix moduleName)
643 [ bgroup "elimination"
644 [ benchFold "toList" toList sourceUnfoldrM
646 , bgroup "transformation"
649 -- This is horribly slow, never finishes
650 -- benchFold "foldlS" (_foldlS 1) sourceUnfoldrM
656main = defaultMain $ concat [o_1_space, o_n_stack, o_n_space]
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