@@ -1,14 +1,1573 @@
2-- Module : Main
2-- Module : Main
3−-- Copyright : (c) 2018 Harendra Kumar
3+-- Copyright : (c) 2018 Composewell Technologies
5-- License : BSD3
5-- License : BSD3
6-- Maintainer : streamly@composewell.com
6-- Maintainer : streamly@composewell.com
9+{-# LANGUAGE FlexibleContexts #-}
10+{-# LANGUAGE ScopedTypeVariables #-}
11+{-# LANGUAGE DeriveAnyClass #-}
12+{-# LANGUAGE DeriveGeneric #-}
13+{-# LANGUAGE RankNTypes #-}
14+{-# LANGUAGE TupleSections #-}
16+#ifdef __HADDOCK_VERSION__
21+{-# LANGUAGE TemplateHaskell #-}
22+{-# OPTIONS_GHC -fplugin Test.Inspection.Plugin #-}
25+import Control.DeepSeq (NFData(..))
26+import Control.Monad (when)
27+import Control.Monad.IO.Class (MonadIO(..))
28+import Control.Monad.State.Strict (StateT, get, put)
29+import Data.Functor.Identity (Identity, runIdentity)
30+import Data.IORef (newIORef, modifyIORef')
31+import GHC.Generics (Generic)
32+import System.Random (randomRIO)
33+import Prelude hiding (concatMap, mapM_, init, last, elem, notElem, all, any,
34+ and, or, length, sum, product, maximum, minimum, reverse, fmap, map,
35+ sequence, mapM, tail)
37+import qualified Prelude as P
38+import qualified Data.Foldable as F
39+import qualified GHC.Exts as GHC
42+import GHC.Types (SPEC(..))
43+import Test.Inspection
45+import qualified Streamly.Internal.Data.Stream.StreamD as D
48+import qualified Streamly as S
49+import qualified Streamly.Prelude as S
50+import qualified Streamly.Internal.Prelude as Internal
51+import qualified Streamly.Internal.Data.Fold as FL
52+import qualified Streamly.Internal.Data.Unfold as UF
8import Streamly.Benchmark.Common
56import Streamly.Benchmark.Common
9import Streamly.Benchmark.Prelude
57import Streamly.Benchmark.Prelude
58+import Streamly.Internal.Data.Time.Units
11−import Gauge
60+moduleName :: String
61+moduleName = "Prelude.Serial"
63+-------------------------------------------------------------------------------
65+-------------------------------------------------------------------------------
67+-------------------------------------------------------------------------------
69+-------------------------------------------------------------------------------
71+{-# INLINE sourceIsList #-}
72+sourceIsList :: Int -> Int -> SerialT Identity Int
73+sourceIsList value n = GHC.fromList [n..n+value]
75+{-# INLINE sourceIsString #-}
76+sourceIsString :: Int -> Int -> SerialT Identity P.Char
77+sourceIsString value n = GHC.fromString (P.replicate (n + value) 'a')
79+{-# INLINE readInstance #-}
80+readInstance :: P.String -> SerialT Identity Int
85+ _ -> P.error "readInstance: no parse"
88+{-# INLINE readInstanceList #-}
89+readInstanceList :: P.String -> [Int]
90+readInstanceList str =
94+ _ -> P.error "readInstance: no parse"
96+o_1_space_generation :: Int -> [Benchmark]
97+o_1_space_generation value =
98+ [ bgroup "generation"
99+ [ benchIOSrc serially "unfoldr" (sourceUnfoldr value)
100+ , benchIOSrc serially "unfoldrM" (sourceUnfoldrM value)
101+ , benchIOSrc serially "intFromTo" (sourceIntFromTo value)
102+ , benchIOSrc serially "intFromThenTo" (sourceIntFromThenTo value)
103+ , benchIOSrc serially "integerFromStep" (sourceIntegerFromStep value)
104+ , benchIOSrc serially "fracFromThenTo" (sourceFracFromThenTo value)
105+ , benchIOSrc serially "fracFromTo" (sourceFracFromTo value)
106+ , benchIOSrc serially "fromList" (sourceFromList value)
107+ , benchPureSrc "IsList.fromList" (sourceIsList value)
108+ , benchPureSrc "IsString.fromString" (sourceIsString value)
109+ , benchIOSrc serially "fromListM" (sourceFromListM value)
111+ -- These essentially test cons and consM
112+ , benchIOSrc serially "fromFoldable" (sourceFromFoldable value)
113+ , benchIOSrc serially "fromFoldableM" (sourceFromFoldableM value)
115+ , benchIOSrc serially "absTimes" $ absTimes value
119+o_n_heap_generation :: Int -> [Benchmark]
120+o_n_heap_generation value =
122+ -- Buffers the output of show/read.
123+ -- XXX can the outputs be streaming? Can we have special read/show
124+ -- style type classes, readM/showM supporting streaming effects?
125+ [ bench "readsPrec pure streams" $
126+ nf readInstance (mkString value)
127+ , bench "readsPrec Haskell lists" $
128+ nf readInstanceList (mkListString value)
132+-------------------------------------------------------------------------------
134+-------------------------------------------------------------------------------
136+-------------------------------------------------------------------------------
138+-------------------------------------------------------------------------------
140+{-# INLINE foldableFoldl' #-}
141+foldableFoldl' :: Int -> Int -> Int
142+foldableFoldl' value n =
143+ F.foldl' (+) 0 (sourceUnfoldr value n :: S.SerialT Identity Int)
145+{-# INLINE foldableFoldrElem #-}
146+foldableFoldrElem :: Int -> Int -> Bool
147+foldableFoldrElem value n =
148+ F.foldr (\x xs -> x == value || xs)
150+ (sourceUnfoldr value n :: S.SerialT Identity Int)
152+{-# INLINE foldableSum #-}
153+foldableSum :: Int -> Int -> Int
154+foldableSum value n =
155+ P.sum (sourceUnfoldr value n :: S.SerialT Identity Int)
157+{-# INLINE foldableProduct #-}
158+foldableProduct :: Int -> Int -> Int
159+foldableProduct value n =
160+ P.product (sourceUnfoldr value n :: S.SerialT Identity Int)
162+{-# INLINE _foldableNull #-}
163+_foldableNull :: Int -> Int -> Bool
164+_foldableNull value n =
165+ P.null (sourceUnfoldr value n :: S.SerialT Identity Int)
167+{-# INLINE foldableElem #-}
168+foldableElem :: Int -> Int -> Bool
169+foldableElem value n =
170+ value `P.elem` (sourceUnfoldr value n :: S.SerialT Identity Int)
172+{-# INLINE foldableNotElem #-}
173+foldableNotElem :: Int -> Int -> Bool
174+foldableNotElem value n =
175+ value `P.notElem` (sourceUnfoldr value n :: S.SerialT Identity Int)
177+{-# INLINE foldableFind #-}
178+foldableFind :: Int -> Int -> Maybe Int
179+foldableFind value n =
180+ F.find (== (value + 1)) (sourceUnfoldr value n :: S.SerialT Identity Int)
182+{-# INLINE foldableAll #-}
183+foldableAll :: Int -> Int -> Bool
184+foldableAll value n =
185+ P.all (<= (value + 1)) (sourceUnfoldr value n :: S.SerialT Identity Int)
187+{-# INLINE foldableAny #-}
188+foldableAny :: Int -> Int -> Bool
189+foldableAny value n =
190+ P.any (> (value + 1)) (sourceUnfoldr value n :: S.SerialT Identity Int)
192+{-# INLINE foldableAnd #-}
193+foldableAnd :: Int -> Int -> Bool
194+foldableAnd value n =
196+ (<= (value + 1)) (sourceUnfoldr value n :: S.SerialT Identity Int)
198+{-# INLINE foldableOr #-}
199+foldableOr :: Int -> Int -> Bool
202+ (> (value + 1)) (sourceUnfoldr value n :: S.SerialT Identity Int)
204+{-# INLINE foldableLength #-}
205+foldableLength :: Int -> Int -> Int
206+foldableLength value n =
207+ P.length (sourceUnfoldr value n :: S.SerialT Identity Int)
209+{-# INLINE foldableMin #-}
210+foldableMin :: Int -> Int -> Int
211+foldableMin value n =
212+ P.minimum (sourceUnfoldr value n :: S.SerialT Identity Int)
214+{-# INLINE ordInstanceMin #-}
215+ordInstanceMin :: SerialT Identity Int -> SerialT Identity Int
216+ordInstanceMin src = P.min src src
218+{-# INLINE foldableMax #-}
219+foldableMax :: Int -> Int -> Int
220+foldableMax value n =
221+ P.maximum (sourceUnfoldr value n :: S.SerialT Identity Int)
223+{-# INLINE foldableMinBy #-}
224+foldableMinBy :: Int -> Int -> Int
225+foldableMinBy value n =
226+ F.minimumBy compare (sourceUnfoldr value n :: S.SerialT Identity Int)
228+{-# INLINE foldableListMinBy #-}
229+foldableListMinBy :: Int -> Int -> Int
230+foldableListMinBy value n = F.minimumBy compare [1..value+n]
232+{-# INLINE foldableMaxBy #-}
233+foldableMaxBy :: Int -> Int -> Int
234+foldableMaxBy value n =
235+ F.maximumBy compare (sourceUnfoldr value n :: S.SerialT Identity Int)
237+{-# INLINE foldableToList #-}
238+foldableToList :: Int -> Int -> [Int]
239+foldableToList value n =
240+ F.toList (sourceUnfoldr value n :: S.SerialT Identity Int)
242+{-# INLINE foldableMapM_ #-}
243+foldableMapM_ :: Monad m => Int -> Int -> m ()
244+foldableMapM_ value n =
245+ F.mapM_ (\_ -> return ()) (sourceUnfoldr value n :: S.SerialT Identity Int)
247+{-# INLINE foldableSequence_ #-}
248+foldableSequence_ :: Int -> Int -> IO ()
249+foldableSequence_ value n =
250+ F.sequence_ (sourceUnfoldrAction value n :: S.SerialT Identity (IO Int))
252+{-# INLINE foldableMsum #-}
253+foldableMsum :: Int -> Int -> IO Int
254+foldableMsum value n =
255+ F.msum (sourceUnfoldrAction value n :: S.SerialT Identity (IO Int))
257+{-# INLINE showInstance #-}
258+showInstance :: SerialT Identity Int -> P.String
259+showInstance = P.show
261+o_1_space_elimination_foldable :: Int -> [Benchmark]
262+o_1_space_elimination_foldable value =
264+ -- Foldable instance
265+ [ bench "foldl'" $ nf (foldableFoldl' value) 1
266+ , bench "foldrElem" $ nf (foldableFoldrElem value) 1
267+ -- , bench "null" $ nf (_foldableNull value) 1
268+ , bench "elem" $ nf (foldableElem value) 1
269+ , bench "length" $ nf (foldableLength value) 1
270+ , bench "sum" $ nf (foldableSum value) 1
271+ , bench "product" $ nf (foldableProduct value) 1
272+ , bench "minimum" $ nf (foldableMin value) 1
273+ , benchPureSink value "min (ord)" ordInstanceMin
274+ , bench "maximum" $ nf (foldableMax value) 1
275+ , bench "length . toList" $ nf (P.length . foldableToList value) 1
276+ , bench "notElem" $ nf (foldableNotElem value) 1
277+ , bench "find" $ nf (foldableFind value) 1
278+ , bench "all" $ nf (foldableAll value) 1
279+ , bench "any" $ nf (foldableAny value) 1
280+ , bench "and" $ nf (foldableAnd value) 1
281+ , bench "or" $ nf (foldableOr value) 1
283+ -- Applicative and Traversable operations
285+ , benchIOSink1 "mapM_" (foldableMapM_ value)
288+ , benchIOSink1 "sequence_" (foldableSequence_ value)
291+ , benchIOSink1 "msum" (foldableMsum value)
295+o_n_space_elimination_foldable :: Int -> [Benchmark]
296+o_n_space_elimination_foldable value =
297+ -- Head recursive strict right folds.
299+ -- XXX the definitions of minimumBy and maximumBy in Data.Foldable use
300+ -- foldl1 which does not work in constant memory for our
301+ -- implementation. It works in constant memory for lists but even for
302+ -- lists it takes 15x more time compared to our foldl' based
304+ [ bench "minimumBy" $ nf (`foldableMinBy` 1) value
305+ , bench "maximumBy" $ nf (`foldableMaxBy` 1) value
306+ , bench "minimumByList" $ nf (`foldableListMinBy` 1) value
310+-------------------------------------------------------------------------------
312+-------------------------------------------------------------------------------
314+{-# INLINE benchPureSink #-}
315+benchPureSink :: NFData b
316+ => Int -> String -> (SerialT Identity Int -> b) -> Benchmark
317+benchPureSink value name = benchPure name (sourceUnfoldr value)
319+{-# INLINE benchHoistSink #-}
321+ :: (IsStream t, NFData b)
322+ => Int -> String -> (t Identity Int -> IO b) -> Benchmark
323+benchHoistSink value name f =
324+ bench name $ nfIO $ randomRIO (1,1) >>= f . sourceUnfoldr value
326+-- XXX We should be using sourceUnfoldrM for fair comparison with IO monad, but
327+-- we can't use it as it requires MonadAsync constraint.
328+{-# INLINE benchIdentitySink #-}
330+ :: (IsStream t, NFData b)
331+ => Int -> String -> (t Identity Int -> Identity b) -> Benchmark
332+benchIdentitySink value name f = bench name $ nf (f . sourceUnfoldr value) 1
334+-------------------------------------------------------------------------------
336+-------------------------------------------------------------------------------
338+{-# INLINE uncons #-}
339+uncons :: Monad m => SerialT m Int -> m ()
343+ Nothing -> return ()
344+ Just (_, t) -> uncons t
347+init :: Monad m => SerialT m a -> m ()
348+init s = S.init s >>= P.mapM_ S.drain
351+mapM_ :: Monad m => SerialT m Int -> m ()
352+mapM_ = S.mapM_ (\_ -> return ())
354+{-# INLINE foldrMElem #-}
355+foldrMElem :: Monad m => Int -> SerialT m Int -> m Bool
364+{-# INLINE foldrMToStream #-}
365+foldrMToStream :: Monad m => SerialT m Int -> m (SerialT Identity Int)
366+foldrMToStream = S.foldr S.cons S.nil
368+{-# INLINE foldrMBuild #-}
369+foldrMBuild :: Monad m => SerialT m Int -> m [Int]
370+foldrMBuild = S.foldrM (\x xs -> (x :) <$> xs) (return [])
372+{-# INLINE foldl'Reduce #-}
373+foldl'Reduce :: Monad m => SerialT m Int -> m Int
374+foldl'Reduce = S.foldl' (+) 0
376+{-# INLINE foldl1'Reduce #-}
377+foldl1'Reduce :: Monad m => SerialT m Int -> m (Maybe Int)
378+foldl1'Reduce = S.foldl1' (+)
380+{-# INLINE foldlM'Reduce #-}
381+foldlM'Reduce :: Monad m => SerialT m Int -> m Int
382+foldlM'Reduce = S.foldlM' (\xs a -> return $ a + xs) 0
385+last :: Monad m => SerialT m Int -> m (Maybe Int)
389+_head :: Monad m => SerialT m Int -> m (Maybe Int)
393+elem :: Monad m => Int -> SerialT m Int -> m Bool
394+elem value = S.elem (value + 1)
396+{-# INLINE notElem #-}
397+notElem :: Monad m => Int -> SerialT m Int -> m Bool
398+notElem value = S.notElem (value + 1)
400+{-# INLINE length #-}
401+length :: Monad m => SerialT m Int -> m Int
405+all :: Monad m => Int -> SerialT m Int -> m Bool
406+all value = S.all (<= (value + 1))
409+any :: Monad m => Int -> SerialT m Int -> m Bool
410+any value = S.any (> (value + 1))
413+and :: Monad m => Int -> SerialT m Int -> m Bool
414+and value = S.and . S.map (<= (value + 1))
417+or :: Monad m => Int -> SerialT m Int -> m Bool
418+or value = S.or . S.map (> (value + 1))
421+find :: Monad m => Int -> SerialT m Int -> m (Maybe Int)
422+find value = S.find (== (value + 1))
424+{-# INLINE findIndex #-}
425+findIndex :: Monad m => Int -> SerialT m Int -> m (Maybe Int)
426+findIndex value = S.findIndex (== (value + 1))
428+{-# INLINE elemIndex #-}
429+elemIndex :: Monad m => Int -> SerialT m Int -> m (Maybe Int)
430+elemIndex value = S.elemIndex (value + 1)
432+{-# INLINE maximum #-}
433+maximum :: Monad m => SerialT m Int -> m (Maybe Int)
436+{-# INLINE minimum #-}
437+minimum :: Monad m => SerialT m Int -> m (Maybe Int)
441+sum :: Monad m => SerialT m Int -> m Int
444+{-# INLINE product #-}
445+product :: Monad m => SerialT m Int -> m Int
448+{-# INLINE minimumBy #-}
449+minimumBy :: Monad m => SerialT m Int -> m (Maybe Int)
450+minimumBy = S.minimumBy compare
452+{-# INLINE maximumBy #-}
453+maximumBy :: Monad m => SerialT m Int -> m (Maybe Int)
454+maximumBy = S.maximumBy compare
456+o_1_space_elimination_folds :: Int -> [Benchmark]
457+o_1_space_elimination_folds value =
458+ [ bgroup "elimination"
463+ [ benchIOSink value "foldl'" foldl'Reduce
464+ , benchIOSink value "foldl1'" foldl1'Reduce
465+ , benchIOSink value "foldlM'" foldlM'Reduce
469+ [ benchIdentitySink value "foldl'" foldl'Reduce
470+ , benchIdentitySink value "foldl1'" foldl1'Reduce
471+ , benchIdentitySink value "foldlM'" foldlM'Reduce
476+ [ benchIOSink value "foldrMElem" (foldrMElem value)
479+ [ benchIdentitySink value "foldrMElem" (foldrMElem value)
480+ , benchIdentitySink value "foldrMToStreamLength"
481+ (S.length . runIdentity . foldrMToStream)
482+ , benchPureSink value "foldrMToListLength"
483+ (P.length . runIdentity . foldrMBuild)
488+ , benchIOSink value "uncons" uncons
489+ , benchIOSink value "init" init
492+ , benchIOSink value "drain" $ toNull serially
493+ , benchPureSink value "drain (pure)" P.id
494+ , benchIOSink value "mapM_" mapM_
496+ -- this is too fast, causes all benchmarks reported in ns
497+ -- , benchIOSink value "head" head
498+ , benchIOSink value "last" last
499+ , benchIOSink value "length" length
500+ , benchHoistSink value "length . generally"
501+ (length . Internal.generally)
502+ , benchIOSink value "sum" sum
503+ , benchIOSink value "product" product
504+ , benchIOSink value "maximumBy" maximumBy
505+ , benchIOSink value "maximum" maximum
506+ , benchIOSink value "minimumBy" minimumBy
507+ , benchIOSink value "minimum" minimum
509+ , benchIOSink value "find" (find value)
510+ -- , benchIOSink value "lookup" lookup
511+ , benchIOSink value "findIndex" (findIndex value)
512+ , benchIOSink value "elemIndex" (elemIndex value)
513+ -- this is too fast, causes all benchmarks reported in ns
514+ -- , benchIOSink value "null" S.null
515+ , benchIOSink value "elem" (elem value)
516+ , benchIOSink value "notElem" (notElem value)
517+ , benchIOSink value "all" (all value)
518+ , benchIOSink value "any" (any value)
519+ , benchIOSink value "and" (and value)
520+ , benchIOSink value "or" (or value)
522+ , benchPureSink value "showsPrec pure streams" showInstance
523+ -- length is used to check for foldr/build fusion
524+ , benchPureSink value "length . IsList.toList" (P.length . GHC.toList)
528+-------------------------------------------------------------------------------
529+-- Buffered Transformations by fold
530+-------------------------------------------------------------------------------
532+{-# INLINE foldl'Build #-}
533+foldl'Build :: Monad m => SerialT m Int -> m [Int]
534+foldl'Build = S.foldl' (flip (:)) []
536+{-# INLINE foldlM'Build #-}
537+foldlM'Build :: Monad m => SerialT m Int -> m [Int]
538+foldlM'Build = S.foldlM' (\xs x -> return $ x : xs) []
540+o_n_heap_elimination_foldl :: Int -> [Benchmark]
541+o_n_heap_elimination_foldl value =
543+ -- Left folds for building a structure are inherently non-streaming
544+ -- as the structure cannot be lazily consumed until fully built.
545+ [ benchIOSink value "foldl'/build/IO" foldl'Build
546+ , benchIdentitySink value "foldl'/build/Identity" foldl'Build
547+ , benchIOSink value "foldlM'/build/IO" foldlM'Build
548+ , benchIdentitySink value "foldlM'/build/Identity" foldlM'Build
553+{-# INLINE showInstanceList #-}
554+showInstanceList :: [Int] -> P.String
555+showInstanceList = P.show
557+o_n_heap_elimination_buffered :: Int -> [Benchmark]
558+o_n_heap_elimination_buffered value =
560+ -- Buffers the output of show/read.
561+ -- XXX can the outputs be streaming? Can we have special read/show
562+ -- style type classes, readM/showM supporting streaming effects?
563+ [ bench "showPrec Haskell lists" $ nf showInstanceList (mkList value)
567+{-# INLINE foldrMReduce #-}
568+foldrMReduce :: Monad m => SerialT m Int -> m Int
569+foldrMReduce = S.foldrM (\x xs -> (x +) <$> xs) (return 0)
571+o_n_space_elimination_foldr :: Int -> [Benchmark]
572+o_n_space_elimination_foldr value =
573+ -- Head recursive strict right folds.
575+ -- accumulation due to strictness of IO monad
576+ [ benchIOSink value "foldrM/build/IO (toList)" foldrMBuild
577+ -- Right folds for reducing are inherently non-streaming as the
578+ -- expression needs to be fully built before it can be reduced.
579+ , benchIdentitySink value "foldrM/reduce/Identity (sum)" foldrMReduce
580+ , benchIOSink value "foldrM/reduce/IO (sum)" foldrMReduce
584+o_n_heap_elimination_toList :: Int -> [Benchmark]
585+o_n_heap_elimination_toList value =
587+ -- Converting the stream to a list or pure stream in a strict monad
588+ [ benchIOSink value "toListRev" Internal.toListRev
589+ , benchIOSink value "toPureRev" Internal.toPureRev
593+o_n_space_elimination_toList :: Int -> [Benchmark]
594+o_n_space_elimination_toList value =
596+ -- Converting the stream to a list or pure stream in a strict monad
597+ [ benchIOSink value "toList" S.toList
598+ , benchIOSink value "toPure" Internal.toPure
602+-------------------------------------------------------------------------------
603+-- Multi-stream folds
604+-------------------------------------------------------------------------------
606+-------------------------------------------------------------------------------
608+-------------------------------------------------------------------------------
611+eqBy' :: (Monad m, P.Eq a) => SerialT m a -> m P.Bool
612+eqBy' src = S.eqBy (==) src src
614+{-# INLINE eqByPure #-}
615+eqByPure :: Int -> Int -> Identity Bool
616+eqByPure value n = eqBy' (sourceUnfoldr value n)
619+inspect $ hasNoTypeClasses 'eqByPure
620+inspect $ 'eqByPure `hasNoType` ''SPEC
621+inspect $ 'eqByPure `hasNoType` ''D.Step
624+{-# INLINE eqInstance #-}
625+eqInstance :: SerialT Identity Int -> Bool
626+eqInstance src = src == src
628+{-# INLINE eqInstanceNotEq #-}
629+eqInstanceNotEq :: SerialT Identity Int -> Bool
630+eqInstanceNotEq src = src P./= src
632+{-# INLINE cmpBy' #-}
633+cmpBy' :: (Monad m, P.Ord a) => SerialT m a -> m P.Ordering
634+cmpBy' src = S.cmpBy P.compare src src
636+{-# INLINE cmpByPure #-}
637+cmpByPure :: Int -> Int -> Identity P.Ordering
638+cmpByPure value n = cmpBy' (sourceUnfoldr value n)
641+inspect $ hasNoTypeClasses 'cmpByPure
642+inspect $ 'cmpByPure `hasNoType` ''SPEC
643+inspect $ 'cmpByPure `hasNoType` ''D.Step
646+{-# INLINE ordInstance #-}
647+ordInstance :: SerialT Identity Int -> Bool
648+ordInstance src = src P.< src
650+o_1_space_elimination_multi_stream_pure :: Int -> [Benchmark]
651+o_1_space_elimination_multi_stream_pure value =
652+ [ bgroup "multi-stream-pure"
653+ [ benchPureSink1 "eqBy" (eqByPure value)
654+ , benchPureSink value "==" eqInstance
655+ , benchPureSink value "/=" eqInstanceNotEq
656+ , benchPureSink1 "cmpBy" (cmpByPure value)
657+ , benchPureSink value "<" ordInstance
661+{-# INLINE isPrefixOf #-}
662+isPrefixOf :: Monad m => SerialT m Int -> m Bool
663+isPrefixOf src = S.isPrefixOf src src
665+{-# INLINE isSubsequenceOf #-}
666+isSubsequenceOf :: Monad m => SerialT m Int -> m Bool
667+isSubsequenceOf src = S.isSubsequenceOf src src
669+{-# INLINE stripPrefix #-}
670+stripPrefix :: Monad m => SerialT m Int -> m ()
672+ _ <- S.stripPrefix src src
676+eqBy :: Int -> Int -> IO Bool
677+eqBy value n = eqBy' (sourceUnfoldrM value n)
680+inspect $ hasNoTypeClasses 'eqBy
681+inspect $ 'eqBy `hasNoType` ''SPEC
682+inspect $ 'eqBy `hasNoType` ''D.Step
686+cmpBy :: Int -> Int -> IO P.Ordering
687+cmpBy value n = cmpBy' (sourceUnfoldrM value n)
690+inspect $ hasNoTypeClasses 'cmpBy
691+inspect $ 'cmpBy `hasNoType` ''SPEC
692+inspect $ 'cmpBy `hasNoType` ''D.Step
695+o_1_space_elimination_multi_stream :: Int -> [Benchmark]
696+o_1_space_elimination_multi_stream value =
697+ [ bgroup "multi-stream"
698+ [ benchIOSink1 "eqBy" (eqBy value)
699+ , benchIOSink1 "cmpBy" (cmpBy value)
700+ , benchIOSink value "isPrefixOf" isPrefixOf
701+ , benchIOSink value "isSubsequenceOf" isSubsequenceOf
702+ , benchIOSink value "stripPrefix" stripPrefix
706+-------------------------------------------------------------------------------
707+-- Pipelines (stream-to-stream transformations)
708+-------------------------------------------------------------------------------
710+-------------------------------------------------------------------------------
711+-- one-to-one transformations
712+-------------------------------------------------------------------------------
714+-------------------------------------------------------------------------------
715+-- Traversable Instance
716+-------------------------------------------------------------------------------
718+{-# INLINE traversableTraverse #-}
719+traversableTraverse :: SerialT Identity Int -> IO (SerialT Identity Int)
720+traversableTraverse = P.traverse return
722+{-# INLINE traversableSequenceA #-}
723+traversableSequenceA :: SerialT Identity Int -> IO (SerialT Identity Int)
724+traversableSequenceA = P.sequenceA . P.fmap return
726+{-# INLINE traversableMapM #-}
727+traversableMapM :: SerialT Identity Int -> IO (SerialT Identity Int)
728+traversableMapM = P.mapM return
730+{-# INLINE traversableSequence #-}
731+traversableSequence :: SerialT Identity Int -> IO (SerialT Identity Int)
732+traversableSequence = P.sequence . P.fmap return
734+{-# INLINE benchPureSinkIO #-}
737+ => Int -> String -> (SerialT Identity Int -> IO b) -> Benchmark
738+benchPureSinkIO value name f =
739+ bench name $ nfIO $ randomRIO (1, 1) >>= f . sourceUnfoldr value
741+o_n_space_traversable :: Int -> [Benchmark]
742+o_n_space_traversable value =
743+ -- Buffering operations using heap proportional to number of elements.
744+ [ bgroup "traversable"
745+ -- Traversable instance
746+ [ benchPureSinkIO value "traverse" traversableTraverse
747+ , benchPureSinkIO value "sequenceA" traversableSequenceA
748+ , benchPureSinkIO value "mapM" traversableMapM
749+ , benchPureSinkIO value "sequence" traversableSequence
753+-------------------------------------------------------------------------------
755+-------------------------------------------------------------------------------
758+scan :: MonadIO m => Int -> SerialT m Int -> m ()
759+scan n = composeN n $ S.scanl' (+) 0
761+{-# INLINE scanl1' #-}
762+scanl1' :: MonadIO m => Int -> SerialT m Int -> m ()
763+scanl1' n = composeN n $ S.scanl1' (+)
765+{-# INLINE sequence #-}
767+ (S.IsStream t, S.MonadAsync m)
768+ => (t m Int -> S.SerialT m Int)
771+sequence t = S.drain . t . S.sequence
774+tap :: MonadIO m => Int -> SerialT m Int -> m ()
775+tap n = composeN n $ S.tap FL.sum
777+{-# INLINE tapRate #-}
778+tapRate :: Int -> SerialT IO Int -> IO ()
781+ composeN n (Internal.tapRate 1 (\c -> modifyIORef' cref (c +))) str
783+{-# INLINE pollCounts #-}
784+pollCounts :: Int -> SerialT IO Int -> IO ()
786+ composeN n (Internal.pollCounts (P.const P.True) f FL.drain)
790+ f = Internal.rollingMap (P.-) . Internal.delayPost 1
792+{-# INLINE timestamped #-}
793+timestamped :: (S.MonadAsync m) => SerialT m Int -> m ()
794+timestamped = S.drain . Internal.timestamped
796+{-# INLINE foldrS #-}
797+foldrS :: MonadIO m => Int -> SerialT m Int -> m ()
798+foldrS n = composeN n $ Internal.foldrS S.cons S.nil
800+{-# INLINE foldrSMap #-}
801+foldrSMap :: MonadIO m => Int -> SerialT m Int -> m ()
802+foldrSMap n = composeN n $ Internal.foldrS (\x xs -> x + 1 `S.cons` xs) S.nil
804+{-# INLINE foldrT #-}
805+foldrT :: MonadIO m => Int -> SerialT m Int -> m ()
806+foldrT n = composeN n $ Internal.foldrT S.cons S.nil
808+{-# INLINE foldrTMap #-}
809+foldrTMap :: MonadIO m => Int -> SerialT m Int -> m ()
810+foldrTMap n = composeN n $ Internal.foldrT (\x xs -> x + 1 `S.cons` xs) S.nil
812+o_1_space_mapping :: Int -> [Benchmark]
813+o_1_space_mapping value =
818+ benchIOSink value "foldrS" (foldrS 1)
819+ , benchIOSink value "foldrSMap" (foldrSMap 1)
820+ , benchIOSink value "foldrT" (foldrT 1)
821+ , benchIOSink value "foldrTMap" (foldrTMap 1)
824+ , benchIOSink value "map" (mapN serially 1)
825+ , benchIOSink value "fmap" (fmapN serially 1)
826+ , bench "sequence" $ nfIO $ randomRIO (1, 1000) >>= \n ->
827+ sequence serially (sourceUnfoldrAction value n)
828+ , benchIOSink value "mapM" (mapM serially 1)
829+ , benchIOSink value "tap" (tap 1)
830+ , benchIOSink value "tapRate 1 second" (tapRate 1)
831+ , benchIOSink value "pollCounts 1 second" (pollCounts 1)
832+ , benchIOSink value "timestamped" timestamped
835+ , benchIOSink value "scanl'" (scan 1)
836+ , benchIOSink value "scanl1'" (scanl1' 1)
841+o_1_space_mappingX4 :: Int -> [Benchmark]
842+o_1_space_mappingX4 value =
843+ [ bgroup "mappingX4"
844+ [ benchIOSink value "map" (mapN serially 4)
845+ , benchIOSink value "fmap" (fmapN serially 4)
846+ , benchIOSink value "mapM" (mapM serially 4)
848+ , benchIOSink value "scan" (scan 4)
849+ , benchIOSink value "scanl1'" (scanl1' 4)
854+-------------------------------------------------------------------------------
855+-- Size reducing transformations (filtering)
856+-------------------------------------------------------------------------------
858+{-# INLINE filterEven #-}
859+filterEven :: MonadIO m => Int -> SerialT m Int -> m ()
860+filterEven n = composeN n $ S.filter even
862+{-# INLINE filterAllOut #-}
863+filterAllOut :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
864+filterAllOut value n = composeN n $ S.filter (> (value + 1))
866+{-# INLINE filterAllIn #-}
867+filterAllIn :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
868+filterAllIn value n = composeN n $ S.filter (<= (value + 1))
870+{-# INLINE _takeOne #-}
871+_takeOne :: MonadIO m => Int -> SerialT m Int -> m ()
872+_takeOne n = composeN n $ S.take 1
874+{-# INLINE takeAll #-}
875+takeAll :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
876+takeAll value n = composeN n $ S.take (value + 1)
878+{-# INLINE takeWhileTrue #-}
879+takeWhileTrue :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
880+takeWhileTrue value n = composeN n $ S.takeWhile (<= (value + 1))
882+{-# INLINE _takeWhileMTrue #-}
883+_takeWhileMTrue :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
884+_takeWhileMTrue value n = composeN n $ S.takeWhileM (return . (<= (value + 1)))
886+{-# INLINE takeByTime #-}
887+takeByTime :: NanoSecond64 -> Int -> SerialT IO Int -> IO ()
888+takeByTime i n = composeN n (Internal.takeByTime i)
891+-- inspect $ hasNoType 'takeByTime ''SPEC
892+inspect $ hasNoTypeClasses 'takeByTime
893+-- inspect $ 'takeByTime `hasNoType` ''D.Step
896+{-# INLINE dropOne #-}
897+dropOne :: MonadIO m => Int -> SerialT m Int -> m ()
898+dropOne n = composeN n $ S.drop 1
900+{-# INLINE dropAll #-}
901+dropAll :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
902+dropAll value n = composeN n $ S.drop (value + 1)
904+{-# INLINE dropWhileTrue #-}
905+dropWhileTrue :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
906+dropWhileTrue value n = composeN n $ S.dropWhile (<= (value + 1))
908+{-# INLINE _dropWhileMTrue #-}
909+_dropWhileMTrue :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
910+_dropWhileMTrue value n = composeN n $ S.dropWhileM (return . (<= (value + 1)))
912+{-# INLINE dropWhileFalse #-}
913+dropWhileFalse :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
914+dropWhileFalse value n = composeN n $ S.dropWhile (> (value + 1))
916+{-# INLINE dropByTime #-}
917+dropByTime :: NanoSecond64 -> Int -> SerialT IO Int -> IO ()
918+dropByTime i n = composeN n (Internal.dropByTime i)
921+inspect $ hasNoTypeClasses 'dropByTime
922+-- inspect $ 'dropByTime `hasNoType` ''D.Step
925+{-# INLINE findIndices #-}
926+findIndices :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
927+findIndices value n = composeN n $ S.findIndices (== (value + 1))
929+{-# INLINE elemIndices #-}
930+elemIndices :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
931+elemIndices value n = composeN n $ S.elemIndices (value + 1)
933+{-# INLINE deleteBy #-}
934+deleteBy :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
935+deleteBy value n = composeN n $ S.deleteBy (>=) (value + 1)
937+{-# INLINE mapMaybe #-}
938+mapMaybe :: MonadIO m => Int -> SerialT m Int -> m ()
947+{-# INLINE mapMaybeM #-}
948+mapMaybeM :: S.MonadAsync m => Int -> SerialT m Int -> m ()
955+ else return $ Just x)
957+o_1_space_filtering :: Int -> [Benchmark]
958+o_1_space_filtering value =
959+ [ bgroup "filtering"
960+ [ benchIOSink value "filter-even" (filterEven 1)
961+ , benchIOSink value "filter-all-out" (filterAllOut value 1)
962+ , benchIOSink value "filter-all-in" (filterAllIn value 1)
965+ , benchIOSink value "take-all" (takeAll value 1)
969+ (takeByTime (NanoSecond64 maxBound) 1)
970+ , benchIOSink value "takeWhile-true" (takeWhileTrue value 1)
971+ -- , benchIOSink value "takeWhileM-true" (_takeWhileMTrue value 1)
972+ , benchIOSink value "drop-one" (dropOne 1)
973+ , benchIOSink value "drop-all" (dropAll value 1)
977+ (dropByTime (NanoSecond64 maxBound) 1)
978+ , benchIOSink value "dropWhile-true" (dropWhileTrue value 1)
979+ -- , benchIOSink value "dropWhileM-true" (_dropWhileMTrue value 1)
983+ (dropWhileFalse value 1)
984+ , benchIOSink value "deleteBy" (deleteBy value 1)
987+ , benchIOSink value "mapMaybe" (mapMaybe 1)
988+ , benchIOSink value "mapMaybeM" (mapMaybeM 1)
990+ -- Searching (stateful map and filter)
991+ , benchIOSink value "findIndices" (findIndices value 1)
992+ , benchIOSink value "elemIndices" (elemIndices value 1)
996+o_1_space_filteringX4 :: Int -> [Benchmark]
997+o_1_space_filteringX4 value =
998+ [ bgroup "filteringX4"
999+ [ benchIOSink value "filter-even" (filterEven 4)
1000+ , benchIOSink value "filter-all-out" (filterAllOut value 4)
1001+ , benchIOSink value "filter-all-in" (filterAllIn value 4)
1004+ , benchIOSink value "take-all" (takeAll value 4)
1005+ , benchIOSink value "takeWhile-true" (takeWhileTrue value 4)
1006+ -- , benchIOSink value "takeWhileM-true" (_takeWhileMTrue value 4)
1007+ , benchIOSink value "drop-one" (dropOne 4)
1008+ , benchIOSink value "drop-all" (dropAll value 4)
1009+ , benchIOSink value "dropWhile-true" (dropWhileTrue value 4)
1010+ -- , benchIOSink value "dropWhileM-true" (_dropWhileMTrue value 4)
1014+ (dropWhileFalse value 4)
1015+ , benchIOSink value "deleteBy" (deleteBy value 4)
1018+ , benchIOSink value "mapMaybe" (mapMaybe 4)
1019+ , benchIOSink value "mapMaybeM" (mapMaybeM 4)
1022+ , benchIOSink value "findIndices" (findIndices value 4)
1023+ , benchIOSink value "elemIndices" (elemIndices value 4)
1027+-------------------------------------------------------------------------------
1028+-- Size increasing transformations (insertions)
1029+-------------------------------------------------------------------------------
1031+{-# INLINE intersperse #-}
1032+intersperse :: S.MonadAsync m => Int -> Int -> SerialT m Int -> m ()
1033+intersperse value n = composeN n $ S.intersperse (value + 1)
1035+{-# INLINE insertBy #-}
1036+insertBy :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1037+insertBy value n = composeN n $ S.insertBy compare (value + 1)
1039+o_1_space_inserting :: Int -> [Benchmark]
1040+o_1_space_inserting value =
1041+ [ bgroup "filtering"
1042+ [ benchIOSink value "intersperse" (intersperse value 1)
1043+ , benchIOSink value "insertBy" (insertBy value 1)
1047+o_1_space_insertingX4 :: Int -> [Benchmark]
1048+o_1_space_insertingX4 value =
1049+ [ bgroup "insertingX4"
1050+ [ benchIOSink value "intersperse" (intersperse value 4)
1051+ , benchIOSink value "insertBy" (insertBy value 4)
1055+-------------------------------------------------------------------------------
1057+-------------------------------------------------------------------------------
1059+{-# INLINE reverse #-}
1060+reverse :: MonadIO m => Int -> SerialT m Int -> m ()
1061+reverse n = composeN n S.reverse
1063+{-# INLINE reverse' #-}
1064+reverse' :: MonadIO m => Int -> SerialT m Int -> m ()
1065+reverse' n = composeN n Internal.reverse'
1067+o_n_heap_reordering :: Int -> [Benchmark]
1068+o_n_heap_reordering value =
1069+ [ bgroup "buffered"
1071+ -- Reversing/sorting a stream
1072+ benchIOSink value "reverse" (reverse 1)
1073+ , benchIOSink value "reverse'" (reverse' 1)
1077+-------------------------------------------------------------------------------
1078+-- Grouping/Splitting
1079+-------------------------------------------------------------------------------
1081+{-# INLINE classifySessionsOf #-}
1082+classifySessionsOf :: (S.MonadAsync m) => SerialT m Int -> m ()
1083+classifySessionsOf =
1085+ . Internal.classifySessionsOf
1086+ 3 (const (return False)) (P.fmap Right FL.drain)
1087+ . S.map (\(ts,(k,a)) -> (k, a, ts))
1088+ . Internal.timestamped
1089+ . S.concatMap (\x -> S.map (x,) (S.enumerateFromTo 1 (10 :: Int)))
1091+o_n_space_grouping :: Int -> [Benchmark]
1092+o_n_space_grouping value =
1093+ -- Buffering operations using heap proportional to group/window sizes.
1094+ [ bgroup "grouping"
1095+ [ benchIOSink (value `div` 10) "classifySessionsOf" classifySessionsOf
1099+-------------------------------------------------------------------------------
1100+-- Mixed Transformation
1101+-------------------------------------------------------------------------------
1103+{-# INLINE scanMap #-}
1104+scanMap :: MonadIO m => Int -> SerialT m Int -> m ()
1105+scanMap n = composeN n $ S.map (subtract 1) . S.scanl' (+) 0
1107+{-# INLINE dropMap #-}
1108+dropMap :: MonadIO m => Int -> SerialT m Int -> m ()
1109+dropMap n = composeN n $ S.map (subtract 1) . S.drop 1
1111+{-# INLINE dropScan #-}
1112+dropScan :: MonadIO m => Int -> SerialT m Int -> m ()
1113+dropScan n = composeN n $ S.scanl' (+) 0 . S.drop 1
1115+{-# INLINE takeDrop #-}
1116+takeDrop :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1117+takeDrop value n = composeN n $ S.drop 1 . S.take (value + 1)
1119+{-# INLINE takeScan #-}
1120+takeScan :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1121+takeScan value n = composeN n $ S.scanl' (+) 0 . S.take (value + 1)
1123+{-# INLINE takeMap #-}
1124+takeMap :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1125+takeMap value n = composeN n $ S.map (subtract 1) . S.take (value + 1)
1127+{-# INLINE filterDrop #-}
1128+filterDrop :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1129+filterDrop value n = composeN n $ S.drop 1 . S.filter (<= (value + 1))
1131+{-# INLINE filterTake #-}
1132+filterTake :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1133+filterTake value n = composeN n $ S.take (value + 1) . S.filter (<= (value + 1))
1135+{-# INLINE filterScan #-}
1136+filterScan :: MonadIO m => Int -> SerialT m Int -> m ()
1137+filterScan n = composeN n $ S.scanl' (+) 0 . S.filter (<= maxBound)
1139+{-# INLINE filterScanl1 #-}
1140+filterScanl1 :: MonadIO m => Int -> SerialT m Int -> m ()
1141+filterScanl1 n = composeN n $ S.scanl1' (+) . S.filter (<= maxBound)
1143+{-# INLINE filterMap #-}
1144+filterMap :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1145+filterMap value n = composeN n $ S.map (subtract 1) . S.filter (<= (value + 1))
1147+-------------------------------------------------------------------------------
1149+-------------------------------------------------------------------------------
1153+ deriving (Generic, NFData)
1155+{-# INLINE sumProductFold #-}
1156+sumProductFold :: Monad m => SerialT m Int -> m (Int, Int)
1157+sumProductFold = S.foldl' (\(s, p) x -> (s + x, p P.* x)) (0, 1)
1159+{-# INLINE sumProductScan #-}
1160+sumProductScan :: Monad m => SerialT m Int -> m (Pair Int Int)
1162+ S.foldl' (\(Pair _ p) (s0, x) -> Pair s0 (p P.* x)) (Pair 0 1) .
1163+ S.scanl' (\(s, _) x -> (s + x, x)) (0, 0)
1165+{-# INLINE foldl'ReduceMap #-}
1166+foldl'ReduceMap :: Monad m => SerialT m Int -> m Int
1167+foldl'ReduceMap = P.fmap (+ 1) . S.foldl' (+) 0
1169+o_1_space_transformations_mixed :: Int -> [Benchmark]
1170+o_1_space_transformations_mixed value =
1171+ -- scanl-map and foldl-map are equivalent to the scan and fold in the foldl
1172+ -- library. If scan/fold followed by a map is efficient enough we may not
1173+ -- need monolithic implementations of these.
1175+ [ benchIOSink value "scanl-map" (scanMap 1)
1176+ , benchIOSink value "foldl-map" foldl'ReduceMap
1177+ , benchIOSink value "sum-product-fold" sumProductFold
1178+ , benchIOSink value "sum-product-scan" sumProductScan
1182+o_1_space_transformations_mixedX4 :: Int -> [Benchmark]
1183+o_1_space_transformations_mixedX4 value =
1185+ [ benchIOSink value "scan-map" (scanMap 4)
1186+ , benchIOSink value "drop-map" (dropMap 4)
1187+ , benchIOSink value "drop-scan" (dropScan 4)
1188+ , benchIOSink value "take-drop" (takeDrop value 4)
1189+ , benchIOSink value "take-scan" (takeScan value 4)
1190+ , benchIOSink value "take-map" (takeMap value 4)
1191+ , benchIOSink value "filter-drop" (filterDrop value 4)
1192+ , benchIOSink value "filter-take" (filterTake value 4)
1193+ , benchIOSink value "filter-scan" (filterScan 4)
1194+ , benchIOSink value "filter-scanl1" (filterScanl1 4)
1195+ , benchIOSink value "filter-map" (filterMap value 4)
1199+-------------------------------------------------------------------------------
1200+-- Iterating a transformation over and over again
1201+-------------------------------------------------------------------------------
1203+{-# INLINE iterStreamLen #-}
1204+iterStreamLen :: Int
1207+{-# INLINE maxIters #-}
1211+{-# INLINE iterateSource #-}
1214+ => (SerialT m Int -> SerialT m Int)
1218+iterateSource g i n = f i (sourceUnfoldrMN iterStreamLen n)
1220+ f (0 :: Int) m = g m
1221+ f x m = g (f (x P.- 1) m)
1223+-- this is quadratic
1224+{-# INLINE iterateScan #-}
1225+iterateScan :: S.MonadAsync m => Int -> SerialT m Int
1226+iterateScan = iterateSource (S.scanl' (+) 0) (maxIters `div` 10)
1228+-- this is quadratic
1229+{-# INLINE iterateScanl1 #-}
1230+iterateScanl1 :: S.MonadAsync m => Int -> SerialT m Int
1231+iterateScanl1 = iterateSource (S.scanl1' (+)) (maxIters `div` 10)
1233+{-# INLINE iterateMapM #-}
1234+iterateMapM :: S.MonadAsync m => Int -> SerialT m Int
1235+iterateMapM = iterateSource (S.mapM return) maxIters
1237+{-# INLINE iterateFilterEven #-}
1238+iterateFilterEven :: S.MonadAsync m => Int -> SerialT m Int
1239+iterateFilterEven = iterateSource (S.filter even) maxIters
1241+{-# INLINE iterateTakeAll #-}
1242+iterateTakeAll :: S.MonadAsync m => Int -> Int -> SerialT m Int
1243+iterateTakeAll value = iterateSource (S.take (value + 1)) maxIters
1245+{-# INLINE iterateDropOne #-}
1246+iterateDropOne :: S.MonadAsync m => Int -> SerialT m Int
1247+iterateDropOne = iterateSource (S.drop 1) maxIters
1249+{-# INLINE iterateDropWhileFalse #-}
1250+iterateDropWhileFalse :: S.MonadAsync m => Int -> Int -> SerialT m Int
1251+iterateDropWhileFalse value =
1252+ iterateSource (S.dropWhile (> (value + 1))) maxIters
1254+{-# INLINE iterateDropWhileTrue #-}
1255+iterateDropWhileTrue :: S.MonadAsync m => Int -> Int -> SerialT m Int
1256+iterateDropWhileTrue value =
1257+ iterateSource (S.dropWhile (<= (value + 1))) maxIters
1260+tail :: Monad m => SerialT m a -> m ()
1261+tail s = S.tail s >>= P.mapM_ tail
1263+{-# INLINE nullHeadTail #-}
1264+nullHeadTail :: Monad m => SerialT m Int -> m ()
1269+ S.tail s >>= P.mapM_ nullHeadTail
1271+-- Head recursive operations.
1272+o_n_stack_iterated :: Int -> [Benchmark]
1273+o_n_stack_iterated value =
1274+ [ bgroup "iterated"
1275+ [ benchIOSrc serially "mapMx10K" iterateMapM
1276+ , benchIOSrc serially "scanx100" iterateScan
1277+ , benchIOSrc serially "scanl1x100" iterateScanl1
1278+ , benchIOSrc serially "filterEvenx10K" iterateFilterEven
1279+ , benchIOSrc serially "takeAllx10K" (iterateTakeAll value)
1280+ , benchIOSrc serially "dropOnex10K" iterateDropOne
1281+ , benchIOSrc serially "dropWhileFalsex10K"
1282+ (iterateDropWhileFalse value)
1283+ , benchIOSrc serially "dropWhileTruex10K"
1284+ (iterateDropWhileTrue value)
1285+ , benchIOSink value "tail" tail
1286+ , benchIOSink value "nullHeadTail" nullHeadTail
1290+-------------------------------------------------------------------------------
1292+-------------------------------------------------------------------------------
1294+o_1_space_pipes :: Int -> [Benchmark]
1295+o_1_space_pipes value =
1297+ [ benchIOSink value "mapM" (transformMapM serially 1)
1298+ , benchIOSink value "compose" (transformComposeMapM serially 1)
1299+ , benchIOSink value "tee" (transformTeeMapM serially 1)
1300+ , benchIOSink value "zip" (transformZipMapM serially 1)
1304+o_1_space_pipesX4 :: Int -> [Benchmark]
1305+o_1_space_pipesX4 value =
1307+ [ benchIOSink value "mapM" (transformMapM serially 4)
1308+ , benchIOSink value "compose" (transformComposeMapM serially 4)
1309+ , benchIOSink value "tee" (transformTeeMapM serially 4)
1310+ , benchIOSink value "zip" (transformZipMapM serially 4)
1314+-------------------------------------------------------------------------------
1316+-------------------------------------------------------------------------------
1318+-------------------------------------------------------------------------------
1320+-------------------------------------------------------------------------------
1322+{-# INLINE serial2 #-}
1323+serial2 :: Int -> Int -> IO ()
1326+ S.serial (sourceUnfoldrMN count n) (sourceUnfoldrMN count (n + 1))
1328+{-# INLINE serial4 #-}
1329+serial4 :: Int -> Int -> IO ()
1333+ (S.serial (sourceUnfoldrMN count n) (sourceUnfoldrMN count (n + 1)))
1335+ (sourceUnfoldrMN count (n + 2))
1336+ (sourceUnfoldrMN count (n + 3)))
1338+{-# INLINE append2 #-}
1339+append2 :: Int -> Int -> IO ()
1342+ Internal.append (sourceUnfoldrMN count n) (sourceUnfoldrMN count (n + 1))
1344+{-# INLINE append4 #-}
1345+append4 :: Int -> Int -> IO ()
1350+ (sourceUnfoldrMN count n)
1351+ (sourceUnfoldrMN count (n + 1)))
1353+ (sourceUnfoldrMN count (n + 2))
1354+ (sourceUnfoldrMN count (n + 3)))
1357+inspect $ hasNoTypeClasses 'append2
1358+inspect $ 'append2 `hasNoType` ''SPEC
1359+inspect $ 'append2 `hasNoType` ''D.AppendState
1362+-------------------------------------------------------------------------------
1364+-------------------------------------------------------------------------------
1366+{-# INLINE mergeBy #-}
1367+mergeBy :: Int -> Int -> IO ()
1372+ (sourceUnfoldrMN count n)
1373+ (sourceUnfoldrMN count (n + 1))
1376+inspect $ hasNoTypeClasses 'mergeBy
1377+inspect $ 'mergeBy `hasNoType` ''SPEC
1378+inspect $ 'mergeBy `hasNoType` ''D.Step
1381+o_1_space_joining :: Int -> [Benchmark]
1382+o_1_space_joining value =
1384+ [ benchIOSrc1 "serial (2,x/2)" (serial2 (value `div` 2))
1385+ , benchIOSrc1 "append (2,x/2)" (append2 (value `div` 2))
1386+ , benchIOSrc1 "serial (2,2,x/4)" (serial4 (value `div` 4))
1387+ , benchIOSrc1 "append (2,2,x/4)" (append4 (value `div` 4))
1388+ , benchIOSrc1 "mergeBy (2,x/2)" (mergeBy (value `div` 2))
1392+-------------------------------------------------------------------------------
1393+-- Concat Foldable containers
1394+-------------------------------------------------------------------------------
1396+o_1_space_concatFoldable :: Int -> [Benchmark]
1397+o_1_space_concatFoldable value =
1398+ [ bgroup "concat-foldable"
1399+ [ benchIOSrc serially "foldMapWith" (sourceFoldMapWith value)
1400+ , benchIOSrc serially "foldMapWithM" (sourceFoldMapWithM value)
1401+ , benchIOSrc serially "foldMapM" (sourceFoldMapM value)
1402+ , benchIOSrc serially "foldWithConcatMapId" (sourceConcatMapId value)
1406+-------------------------------------------------------------------------------
1408+-------------------------------------------------------------------------------
1410+-- concatMap unfoldrM/unfoldrM
1412+{-# INLINE concatMap #-}
1413+concatMap :: Int -> Int -> Int -> IO ()
1414+concatMap outer inner n =
1415+ S.drain $ S.concatMap
1416+ (\_ -> sourceUnfoldrMN inner n)
1417+ (sourceUnfoldrMN outer n)
1420+inspect $ hasNoTypeClasses 'concatMap
1421+inspect $ 'concatMap `hasNoType` ''SPEC
1424+-- concatMap unfoldr/unfoldr
1426+{-# INLINE concatMapPure #-}
1427+concatMapPure :: Int -> Int -> Int -> IO ()
1428+concatMapPure outer inner n =
1429+ S.drain $ S.concatMap
1430+ (\_ -> sourceUnfoldrN inner n)
1431+ (sourceUnfoldrN outer n)
1434+inspect $ hasNoTypeClasses 'concatMapPure
1435+inspect $ 'concatMapPure `hasNoType` ''SPEC
1438+-- concatMap replicate/unfoldrM
1440+{-# INLINE concatMapRepl4xN #-}
1441+concatMapRepl4xN :: Int -> Int -> IO ()
1442+concatMapRepl4xN value n = S.drain $ S.concatMap (S.replicate 4)
1443+ (sourceUnfoldrMN (value `div` 4) n)
1446+inspect $ hasNoTypeClasses 'concatMapRepl4xN
1447+inspect $ 'concatMapRepl4xN `hasNoType` ''SPEC
1452+{-# INLINE concatMapWithSerial #-}
1453+concatMapWithSerial :: Int -> Int -> Int -> IO ()
1454+concatMapWithSerial = concatStreamsWith S.serial
1457+inspect $ hasNoTypeClasses 'concatMapWithSerial
1458+inspect $ 'concatMapWithSerial `hasNoType` ''SPEC
1461+{-# INLINE concatMapWithAppend #-}
1462+concatMapWithAppend :: Int -> Int -> Int -> IO ()
1463+concatMapWithAppend = concatStreamsWith Internal.append
1466+inspect $ hasNoTypeClasses 'concatMapWithAppend
1467+inspect $ 'concatMapWithAppend `hasNoType` ''SPEC
1472+-- concatUnfold replicate/unfoldrM
1474+{-# INLINE concatUnfoldRepl4xN #-}
1475+concatUnfoldRepl4xN :: Int -> Int -> IO ()
1476+concatUnfoldRepl4xN value n =
1477+ S.drain $ S.concatUnfold
1479+ (sourceUnfoldrMN (value `div` 4) n)
1482+inspect $ hasNoTypeClasses 'concatUnfoldRepl4xN
1483+inspect $ 'concatUnfoldRepl4xN `hasNoType` ''D.ConcatMapUState
1484+inspect $ 'concatUnfoldRepl4xN `hasNoType` ''SPEC
1487+o_1_space_concat :: Int -> [Benchmark]
1488+o_1_space_concat value =
1490+ [ benchIOSrc1 "concatMapPure (2,x/2)"
1491+ (concatMapPure 2 (value `div` 2))
1492+ , benchIOSrc1 "concatMap (2,x/2)"
1493+ (concatMap 2 (value `div` 2))
1494+ , benchIOSrc1 "concatMap (x/2,2)"
1495+ (concatMap (value `div` 2) 2)
1496+ , benchIOSrc1 "concatMapRepl (x/4,4)"
1497+ (concatMapRepl4xN value)
1498+ , benchIOSrc1 "concatUnfoldRepl (x/4,4)"
1499+ (concatUnfoldRepl4xN value)
1500+ , benchIOSrc1 "concatMapWithSerial (2,x/2)"
1501+ (concatMapWithSerial 2 (value `div` 2))
1502+ , benchIOSrc1 "concatMapWithSerial (x/2,2)"
1503+ (concatMapWithSerial (value `div` 2) 2)
1504+ , benchIOSrc1 "concatMapWithAppend (2,x/2)"
1505+ (concatMapWithAppend 2 (value `div` 2))
1509+-------------------------------------------------------------------------------
1511+-------------------------------------------------------------------------------
1513+o_1_space_outerProduct :: Int -> [Benchmark]
1514+o_1_space_outerProduct value =
1515+ [ bgroup "monad-outer-product"
1516+ [ benchIO "toNullAp" $ toNullAp value serially
1517+ , benchIO "toNull" $ toNullM value serially
1518+ , benchIO "toNull3" $ toNullM3 value serially
1519+ , benchIO "filterAllOut" $ filterAllOutM value serially
1520+ , benchIO "filterAllIn" $ filterAllInM value serially
1521+ , benchIO "filterSome" $ filterSome value serially
1522+ , benchIO "breakAfterSome" $ breakAfterSome value serially
1526+o_n_space_outerProduct :: Int -> [Benchmark]
1527+o_n_space_outerProduct value =
1528+ [ bgroup "monad-outer-product"
1529+ [ benchIO "toList" $ toListM value serially
1530+ , benchIO "toListSome" $ toListSome value serially
1534+-------------------------------------------------------------------------------
1535+-- Monad transformation (hoisting etc.)
1536+-------------------------------------------------------------------------------
1538+{-# INLINE sourceUnfoldrState #-}
1539+sourceUnfoldrState :: (S.IsStream t, S.MonadAsync m)
1540+ => Int -> Int -> t (StateT Int m) Int
1541+sourceUnfoldrState value n = S.unfoldrM step n
1545+ then return Nothing
1549+ return (Just (s, cnt + 1))
1551+{-# INLINE evalStateT #-}
1552+evalStateT :: S.MonadAsync m => Int -> Int -> SerialT m Int
1553+evalStateT value n = Internal.evalStateT 0 (sourceUnfoldrState value n)
1555+{-# INLINE withState #-}
1556+withState :: S.MonadAsync m => Int -> Int -> SerialT m Int
1558+ Internal.evalStateT (0 :: Int) (Internal.liftInner (sourceUnfoldrM value n))
1560+o_1_space_hoisting :: Int -> [Benchmark]
1561+o_1_space_hoisting value =
1562+ [ bgroup "hoisting"
1563+ [ benchIOSrc serially "evalState" (evalStateT value)
1564+ , benchIOSrc serially "withState" (withState value)
1568+-------------------------------------------------------------------------------
1570+-------------------------------------------------------------------------------
13-- In addition to gauge options, the number of elements in the stream can be
1572-- In addition to gauge options, the number of elements in the stream can be
14-- passed using the --stream-size option.
1573-- passed using the --stream-size option.
@@ -16,49 +1575,63 @@ import Gauge
16main :: IO ()
1575main :: IO ()
18 (value, cfg, benches) <- parseCLIOpts defaultStreamSize
1577 (value, cfg, benches) <- parseCLIOpts defaultStreamSize
19− size <- limitStreamSize value
20− size `seq` runMode (mode cfg) cfg benches (allBenchmarks size)
1578+ value `seq` runMode (mode cfg) cfg benches (allBenchmarks value)
22 allBenchmarks size =
1582 allBenchmarks size =
23− [ bgroup "o-n-stack" $ o_n_stack_serial_iterated size
1583+ [ bgroup (o_1_space_prefix moduleName) $ Prelude.concat
24− , bgroup "o-1-space" $
1584+ [ o_1_space_generation size
25− concat [serial size, wSerial size, zipSerial size]
1585+
26− , bgroup "o-n-heap" $
1586+ -- elimination
27− concat [o_n_heap_serial_foldl size, o_n_heap_serial_buffering size]
1587+ , o_1_space_elimination_foldable size
28− , bgroup "o-n-space" $
1588+ , o_1_space_elimination_folds size
29− concat
1589+ , o_1_space_elimination_multi_stream_pure size
30− [ o_n_space_serial_toList size -- < 2MB
1590+ , o_1_space_elimination_multi_stream size
31− , o_n_space_serial_outerProductStreams size
1591+
32− , o_n_space_wSerial_outerProductStreams size
1592+ -- transformation
33− , o_n_space_serial size -- < 2MB
1593+ , o_1_space_mapping size
34− , o_n_space_serial_foldr size
1594+ , o_1_space_mappingX4 size
35− ]
1595+ , o_1_space_filtering size
36− ]
1596+ , o_1_space_filteringX4 size
37− serial size =
1597+ , o_1_space_inserting size
38− concat
1598+ , o_1_space_insertingX4 size
39− [ o_1_space_serial_pure size
1599+ , o_1_space_transformations_mixed size
40− , o_1_space_serial_foldable size
1600+ , o_1_space_transformations_mixedX4 size
41− , o_1_space_serial_generation size
1601+
42− , o_1_space_serial_elimination size
1602+ -- pipes
43− , o_1_space_serial_foldMultiStream size
1603+ , o_1_space_pipes size
44− , o_1_space_serial_pipes size
1604+ , o_1_space_pipesX4 size
45− , o_1_space_serial_pipesX4 size
1605+
46− , o_1_space_serial_transformer size
1606+ -- multi-stream
47− , o_1_space_serial_transformation size
1607+ , o_1_space_joining size
48− , o_1_space_serial_transformationX4 size
1608+ , o_1_space_concatFoldable size
49− , o_1_space_serial_filtering size
1609+ , o_1_space_concat size
50− , o_1_space_serial_filteringX4 size
1610+ , o_1_space_outerProduct size
51− , o_1_space_serial_joining size
1611+
52− , o_1_space_serial_concatFoldable size
1612+ -- Monad transformation
53− , o_1_space_serial_concatSerial size
1613+ , o_1_space_hoisting size
54− , o_1_space_serial_outerProductStreams size
55− , o_1_space_serial_mixed size
56− , o_1_space_serial_mixedX4 size
58− wSerial size =
1615+ , bgroup (o_n_stack_prefix moduleName) (o_n_stack_iterated size)
59− concat
1616+ , bgroup (o_n_heap_prefix moduleName) $ Prelude.concat
60− [ o_1_space_wSerial_transformation size
1617+ [ o_n_heap_generation size
61− , o_1_space_wSerial_concatMap size
1618+ , o_n_heap_elimination_foldl size
62− , o_1_space_wSerial_outerProduct size
1619+ , o_n_heap_elimination_toList size
1620+ , o_n_heap_elimination_buffered size
1623+ , o_n_heap_reordering size
64− zipSerial size = concat [o_1_space_zipSerial_transformation size]
1625+ , bgroup (o_n_space_prefix moduleName) $ Prelude.concat
1626+ [ o_n_space_elimination_foldable size
1627+ , o_n_space_elimination_toList size
1628+ , o_n_space_elimination_foldr size
1631+ , o_n_space_traversable size
1632+ , o_n_space_grouping size
1635+ , o_n_space_outerProduct size