on master help sign in
Break Benchmark.Prelude.Serial into smaller modules
c216af0Harendra Kumar
benchmark/Streamly/Benchmark/Prelude/Serial.hs+16−2122
@@ -1,2075 +1,29 @@
1-- |
1-- |
2−-- Module : Main
2+-- Module : Serial
3-- Copyright : (c) 2018 Composewell Technologies
3-- Copyright : (c) 2018 Composewell Technologies
4−--
5−-- License : BSD3
4+-- License : BSD-3-Clause
6-- Maintainer : streamly@composewell.com
5-- Maintainer : streamly@composewell.com
7
6
8{-# LANGUAGE CPP #-}
7{-# LANGUAGE CPP #-}
9{-# LANGUAGE FlexibleContexts #-}
8{-# LANGUAGE FlexibleContexts #-}
10{-# LANGUAGE ScopedTypeVariables #-}
9{-# LANGUAGE ScopedTypeVariables #-}
11−{-# LANGUAGE DeriveAnyClass #-}
12−{-# LANGUAGE DeriveGeneric #-}
13{-# LANGUAGE RankNTypes #-}
10{-# LANGUAGE RankNTypes #-}
14−{-# LANGUAGE TupleSections #-}
15−
16−#ifdef __HADDOCK_VERSION__
17−#undef INSPECTION
18−#endif
19−
20−#ifdef INSPECTION
21−{-# LANGUAGE TemplateHaskell #-}
22−{-# OPTIONS_GHC -fplugin Test.Inspection.Plugin #-}
23−#endif
24−
25−module Main where
26
11
27−import Control.DeepSeq (NFData(..))
12+module Main (main) where
28−import Control.Monad (when)
29−import Control.Monad.IO.Class (MonadIO(..))
30−import Control.Monad.State.Strict (StateT, get, put, MonadState)
31−import qualified Control.Monad.State.Strict as State
32−import Control.Monad.Trans.Class (lift)
33−import Data.Functor.Identity (Identity, runIdentity)
34−import Data.IORef (newIORef, modifyIORef')
35−import GHC.Generics (Generic)
36−import System.Random (randomRIO)
37−import Prelude hiding
38− ( concatMap, mapM_, init, last, elem, notElem, all, any
39− , and, or, length, sum, product, maximum, minimum, reverse, fmap, map
40− , sequence, mapM, tail, repeat, replicate, iterate, (!!), lookup)
41
13
42−import qualified Control.Applicative as AP
14+import qualified Serial.Generation as Generation
43−import qualified Prelude as P
44−import qualified Data.Foldable as F
45−import qualified GHC.Exts as GHC
15+import qualified Serial.Elimination as Elimination
46−
16+import qualified Serial.Transformation1 as Transformation1
47−#ifdef INSPECTION
17+import qualified Serial.Transformation2 as Transformation2
48−import GHC.Types (SPEC(..))
18+import qualified Serial.Transformation3 as Transformation3
49−import Test.Inspection
50−
51−import qualified Streamly.Internal.Data.Stream.StreamD as D
52−#endif
53−
54−import qualified Streamly.Prelude as S
55−import qualified Streamly.Internal.Data.Stream.IsStream as Internal
56−import qualified Streamly.Internal.Data.Fold as FL
57−import qualified Streamly.Internal.Data.Unfold as UF
19+import qualified Serial.Nested as Nested
58
20
59import Gauge
21import Gauge
60−import Streamly.Prelude (SerialT, IsStream, serially, serial, MonadAsync)
61import Streamly.Benchmark.Common
22import Streamly.Benchmark.Common
62−import Streamly.Benchmark.Prelude
63−import Streamly.Internal.Data.Time.Units
64
23
65moduleName :: String
24moduleName :: String
66moduleName = "Prelude.Serial"
25moduleName = "Prelude.Serial"
67
26
68−-------------------------------------------------------------------------------
69−-- Generation
70−-------------------------------------------------------------------------------
71−
72−-------------------------------------------------------------------------------
73−-- fromList
74−-------------------------------------------------------------------------------
75−
76−{-# INLINE sourceIsList #-}
77−sourceIsList :: Int -> Int -> SerialT Identity Int
78−sourceIsList value n = GHC.fromList [n..n+value]
79−
80−{-# INLINE sourceIsString #-}
81−sourceIsString :: Int -> Int -> SerialT Identity P.Char
82−sourceIsString value n = GHC.fromString (P.replicate (n + value) 'a')
83−
84−{-# INLINE readInstance #-}
85−readInstance :: P.String -> SerialT Identity Int
86−readInstance str =
87− let r = P.reads str
88− in case r of
89− [(x,"")] -> x
90− _ -> P.error "readInstance: no parse"
91−
92−-- For comparisons
93−{-# INLINE readInstanceList #-}
94−readInstanceList :: P.String -> [Int]
95−readInstanceList str =
96− let r = P.reads str
97− in case r of
98− [(x,"")] -> x
99− _ -> P.error "readInstance: no parse"
100−
101−{-# INLINE repeat #-}
102−repeat :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
103−repeat count = S.take count . S.repeat
104−
105−{-# INLINE repeatM #-}
106−repeatM :: (MonadAsync m, S.IsStream t) => Int -> Int -> t m Int
107−repeatM count = S.take count . S.repeatM . return
108−
109−{-# INLINE replicate #-}
110−replicate :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
111−replicate count = S.replicate count
112−
113−{-# INLINE replicateM #-}
114−replicateM :: (MonadAsync m, S.IsStream t) => Int -> Int -> t m Int
115−replicateM count = S.replicateM count . return
116−
117−{-# INLINE enumerateFrom #-}
118−enumerateFrom :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
119−enumerateFrom count = S.take count . S.enumerateFrom
120−
121−{-# INLINE enumerateFromTo #-}
122−enumerateFromTo :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
123−enumerateFromTo = sourceIntFromTo
124−
125−{-# INLINE enumerateFromThen #-}
126−enumerateFromThen :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
127−enumerateFromThen count inp = S.take count $ S.enumerateFromThen inp (inp + 1)
128−
129−{-# INLINE enumerateFromThenTo #-}
130−enumerateFromThenTo :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
131−enumerateFromThenTo = sourceIntFromThenTo
132−
133−-- n ~ 1
134−{-# INLINE enumerate #-}
135−enumerate :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
136−enumerate count n = S.take (count + n) $ S.enumerate
137−
138−-- n ~ 1
139−{-# INLINE enumerateTo #-}
140−enumerateTo :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
141−enumerateTo count n = S.enumerateTo (minBound + count + n)
142−
143−{-# INLINE iterate #-}
144−iterate :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
145−iterate count = S.take count . S.iterate (+1)
146−
147−{-# INLINE iterateM #-}
148−iterateM :: (MonadAsync m, S.IsStream t) => Int -> Int -> t m Int
149−iterateM count = S.take count . S.iterateM (return . (+1)) . return
150−
151−{-# INLINE fromIndices #-}
152−fromIndices :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
153−fromIndices value n = S.take value $ S.fromIndices (+ n)
154−
155−{-# INLINE fromIndicesM #-}
156−fromIndicesM :: (MonadAsync m, S.IsStream t) => Int -> Int -> t m Int
157−fromIndicesM value n = S.take value $ S.fromIndicesM (return <$> (+ n))
158−
159−o_1_space_generation :: Int -> [Benchmark]
160−o_1_space_generation value =
161− [ bgroup "generation"
162− [ benchIOSrc serially "unfoldr" (sourceUnfoldr value)
163− , benchIOSrc serially "unfoldrM" (sourceUnfoldrM value)
164− , benchIOSrc serially "repeat" (repeat value)
165− , benchIOSrc serially "repeatM" (repeatM value)
166− , benchIOSrc serially "replicate" (replicate value)
167− , benchIOSrc serially "replicateM" (replicateM value)
168− , benchIOSrc serially "iterate" (iterate value)
169− , benchIOSrc serially "iterateM" (iterateM value)
170− , benchIOSrc serially "fromIndices" (fromIndices value)
171− , benchIOSrc serially "fromIndicesM" (fromIndicesM value)
172− , benchIOSrc serially "intFromTo" (sourceIntFromTo value)
173− , benchIOSrc serially "intFromThenTo" (sourceIntFromThenTo value)
174− , benchIOSrc serially "integerFromStep" (sourceIntegerFromStep value)
175− , benchIOSrc serially "fracFromThenTo" (sourceFracFromThenTo value)
176− , benchIOSrc serially "fracFromTo" (sourceFracFromTo value)
177− , benchIOSrc serially "fromList" (sourceFromList value)
178− , benchPureSrc "IsList.fromList" (sourceIsList value)
179− , benchPureSrc "IsString.fromString" (sourceIsString value)
180− , benchIOSrc serially "fromListM" (sourceFromListM value)
181− , benchIOSrc serially "enumerateFrom" (enumerateFrom value)
182− , benchIOSrc serially "enumerateFromThen" (enumerateFromThen value)
183− , benchIOSrc serially "enumerate" (enumerate value)
184− , benchIOSrc serially "enumerateTo" (enumerateTo value)
185−
186− -- These essentially test cons and consM
187− , benchIOSrc serially "fromFoldable" (sourceFromFoldable value)
188− , benchIOSrc serially "fromFoldableM" (sourceFromFoldableM value)
189−
190− , benchIOSrc serially "absTimes" $ absTimes value
191− ]
192− ]
193−
194−o_n_heap_generation :: Int -> [Benchmark]
195−o_n_heap_generation value =
196− [ bgroup "buffered"
197− -- Buffers the output of show/read.
198− -- XXX can the outputs be streaming? Can we have special read/show
199− -- style type classes, readM/showM supporting streaming effects?
200− [ bench "readsPrec pure streams" $
201− nf readInstance (mkString value)
202− , bench "readsPrec Haskell lists" $
203− nf readInstanceList (mkListString value)
204− ]
205− ]
206−
207−-------------------------------------------------------------------------------
208−-- Elimination
209−-------------------------------------------------------------------------------
210−
211−-------------------------------------------------------------------------------
212−-- Foldable Instance
213−-------------------------------------------------------------------------------
214−
215−{-# INLINE foldableFoldl' #-}
216−foldableFoldl' :: Int -> Int -> Int
217−foldableFoldl' value n =
218− F.foldl' (+) 0 (sourceUnfoldr value n :: S.SerialT Identity Int)
219−
220−{-# INLINE foldableFoldrElem #-}
221−foldableFoldrElem :: Int -> Int -> Bool
222−foldableFoldrElem value n =
223− F.foldr (\x xs -> x == value || xs)
224− P.False
225− (sourceUnfoldr value n :: S.SerialT Identity Int)
226−
227−{-# INLINE foldableSum #-}
228−foldableSum :: Int -> Int -> Int
229−foldableSum value n =
230− P.sum (sourceUnfoldr value n :: S.SerialT Identity Int)
231−
232−{-# INLINE foldableProduct #-}
233−foldableProduct :: Int -> Int -> Int
234−foldableProduct value n =
235− P.product (sourceUnfoldr value n :: S.SerialT Identity Int)
236−
237−{-# INLINE _foldableNull #-}
238−_foldableNull :: Int -> Int -> Bool
239−_foldableNull value n =
240− P.null (sourceUnfoldr value n :: S.SerialT Identity Int)
241−
242−{-# INLINE foldableElem #-}
243−foldableElem :: Int -> Int -> Bool
244−foldableElem value n =
245− value `P.elem` (sourceUnfoldr value n :: S.SerialT Identity Int)
246−
247−{-# INLINE foldableNotElem #-}
248−foldableNotElem :: Int -> Int -> Bool
249−foldableNotElem value n =
250− value `P.notElem` (sourceUnfoldr value n :: S.SerialT Identity Int)
251−
252−{-# INLINE foldableFind #-}
253−foldableFind :: Int -> Int -> Maybe Int
254−foldableFind value n =
255− F.find (== (value + 1)) (sourceUnfoldr value n :: S.SerialT Identity Int)
256−
257−{-# INLINE foldableAll #-}
258−foldableAll :: Int -> Int -> Bool
259−foldableAll value n =
260− P.all (<= (value + 1)) (sourceUnfoldr value n :: S.SerialT Identity Int)
261−
262−{-# INLINE foldableAny #-}
263−foldableAny :: Int -> Int -> Bool
264−foldableAny value n =
265− P.any (> (value + 1)) (sourceUnfoldr value n :: S.SerialT Identity Int)
266−
267−{-# INLINE foldableAnd #-}
268−foldableAnd :: Int -> Int -> Bool
269−foldableAnd value n =
270− P.and $ S.map
271− (<= (value + 1)) (sourceUnfoldr value n :: S.SerialT Identity Int)
272−
273−{-# INLINE foldableOr #-}
274−foldableOr :: Int -> Int -> Bool
275−foldableOr value n =
276− P.or $ S.map
277− (> (value + 1)) (sourceUnfoldr value n :: S.SerialT Identity Int)
278−
279−{-# INLINE foldableLength #-}
280−foldableLength :: Int -> Int -> Int
281−foldableLength value n =
282− P.length (sourceUnfoldr value n :: S.SerialT Identity Int)
283−
284−{-# INLINE foldableMin #-}
285−foldableMin :: Int -> Int -> Int
286−foldableMin value n =
287− P.minimum (sourceUnfoldr value n :: S.SerialT Identity Int)
288−
289−{-# INLINE ordInstanceMin #-}
290−ordInstanceMin :: SerialT Identity Int -> SerialT Identity Int
291−ordInstanceMin src = P.min src src
292−
293−{-# INLINE foldableMax #-}
294−foldableMax :: Int -> Int -> Int
295−foldableMax value n =
296− P.maximum (sourceUnfoldr value n :: S.SerialT Identity Int)
297−
298−{-# INLINE foldableMinBy #-}
299−foldableMinBy :: Int -> Int -> Int
300−foldableMinBy value n =
301− F.minimumBy compare (sourceUnfoldr value n :: S.SerialT Identity Int)
302−
303−{-# INLINE foldableListMinBy #-}
304−foldableListMinBy :: Int -> Int -> Int
305−foldableListMinBy value n = F.minimumBy compare [1..value+n]
306−
307−{-# INLINE foldableMaxBy #-}
308−foldableMaxBy :: Int -> Int -> Int
309−foldableMaxBy value n =
310− F.maximumBy compare (sourceUnfoldr value n :: S.SerialT Identity Int)
311−
312−{-# INLINE foldableToList #-}
313−foldableToList :: Int -> Int -> [Int]
314−foldableToList value n =
315− F.toList (sourceUnfoldr value n :: S.SerialT Identity Int)
316−
317−{-# INLINE foldableMapM_ #-}
318−foldableMapM_ :: Monad m => Int -> Int -> m ()
319−foldableMapM_ value n =
320− F.mapM_ (\_ -> return ()) (sourceUnfoldr value n :: S.SerialT Identity Int)
321−
322−{-# INLINE foldableSequence_ #-}
323−foldableSequence_ :: Int -> Int -> IO ()
324−foldableSequence_ value n =
325− F.sequence_ (sourceUnfoldrAction value n :: S.SerialT Identity (IO Int))
326−
327−{-# INLINE _foldableMsum #-}
328−_foldableMsum :: Int -> Int -> IO Int
329−_foldableMsum value n =
330− F.msum (sourceUnfoldrAction value n :: S.SerialT Identity (IO Int))
331−
332−{-# INLINE showInstance #-}
333−showInstance :: SerialT Identity Int -> P.String
334−showInstance = P.show
335−
336−o_1_space_elimination_foldable :: Int -> [Benchmark]
337−o_1_space_elimination_foldable value =
338− [ bgroup "foldable"
339− -- Foldable instance
340− [ bench "foldl'" $ nf (foldableFoldl' value) 1
341− , bench "foldrElem" $ nf (foldableFoldrElem value) 1
342− -- , bench "null" $ nf (_foldableNull value) 1
343− , bench "elem" $ nf (foldableElem value) 1
344− , bench "length" $ nf (foldableLength value) 1
345− , bench "sum" $ nf (foldableSum value) 1
346− , bench "product" $ nf (foldableProduct value) 1
347− , bench "minimum" $ nf (foldableMin value) 1
348− , benchPureSink value "min (ord)" ordInstanceMin
349− , bench "maximum" $ nf (foldableMax value) 1
350− , bench "length . toList" $ nf (P.length . foldableToList value) 1
351− , bench "notElem" $ nf (foldableNotElem value) 1
352− , bench "find" $ nf (foldableFind value) 1
353− , bench "all" $ nf (foldableAll value) 1
354− , bench "any" $ nf (foldableAny value) 1
355− , bench "and" $ nf (foldableAnd value) 1
356− , bench "or" $ nf (foldableOr value) 1
357−
358− -- Applicative and Traversable operations
359− -- TBD: traverse_
360− , benchIOSink1 "mapM_" (foldableMapM_ value)
361− -- TBD: for_
362− -- TBD: forM_
363− , benchIOSink1 "sequence_" (foldableSequence_ value)
364− -- TBD: sequenceA_
365− -- TBD: asum
366− -- XXX needs to be fixed, results are in ns
367− -- , benchIOSink1 "msum" (foldableMsum value)
368− ]
369− ]
370−
371−o_n_space_elimination_foldable :: Int -> [Benchmark]
372−o_n_space_elimination_foldable value =
373− -- Head recursive strict right folds.
374− [ bgroup "foldl"
375− -- XXX the definitions of minimumBy and maximumBy in Data.Foldable use
376− -- foldl1 which does not work in constant memory for our
377− -- implementation. It works in constant memory for lists but even for
378− -- lists it takes 15x more time compared to our foldl' based
379− -- implementation.
380− [ bench "minimumBy" $ nf (`foldableMinBy` 1) value
381− , bench "maximumBy" $ nf (`foldableMaxBy` 1) value
382− , bench "minimumByList" $ nf (`foldableListMinBy` 1) value
383− ]
384− ]
385−
386−-------------------------------------------------------------------------------
387−-- Stream folds
388−-------------------------------------------------------------------------------
389−
390−{-# INLINE benchPureSink #-}
391−benchPureSink :: NFData b
392− => Int -> String -> (SerialT Identity Int -> b) -> Benchmark
393−benchPureSink value name = benchPure name (sourceUnfoldr value)
394−
395−{-# INLINE benchHoistSink #-}
396−benchHoistSink
397− :: (IsStream t, NFData b)
398− => Int -> String -> (t Identity Int -> IO b) -> Benchmark
399−benchHoistSink value name f =
400− bench name $ nfIO $ randomRIO (1,1) >>= f . sourceUnfoldr value
401−
402−-- XXX We should be using sourceUnfoldrM for fair comparison with IO monad, but
403−-- we can't use it as it requires MonadAsync constraint.
404−{-# INLINE benchIdentitySink #-}
405−benchIdentitySink
406− :: (IsStream t, NFData b)
407− => Int -> String -> (t Identity Int -> Identity b) -> Benchmark
408−benchIdentitySink value name f = bench name $ nf (f . sourceUnfoldr value) 1
409−
410−-------------------------------------------------------------------------------
411−-- Reductions
412−-------------------------------------------------------------------------------
413−
414−{-# INLINE uncons #-}
415−uncons :: Monad m => SerialT m Int -> m ()
416−uncons s = do
417− r <- S.uncons s
418− case r of
419− Nothing -> return ()
420− Just (_, t) -> uncons t
421−
422−{-# INLINE init #-}
423−init :: Monad m => SerialT m a -> m ()
424−init s = S.init s >>= P.mapM_ S.drain
425−
426−{-# INLINE mapM_ #-}
427−mapM_ :: Monad m => SerialT m Int -> m ()
428−mapM_ = S.mapM_ (\_ -> return ())
429−
430−{-# INLINE foldrMElem #-}
431−foldrMElem :: Monad m => Int -> SerialT m Int -> m Bool
432−foldrMElem e =
433− S.foldrM
434− (\x xs ->
435− if x == e
436− then return P.True
437− else xs)
438− (return P.False)
439−
440−{-# INLINE foldrToStream #-}
441−foldrToStream :: Monad m => SerialT m Int -> m (SerialT Identity Int)
442−foldrToStream = S.foldr S.cons S.nil
443−
444−{-# INLINE foldrMBuild #-}
445−foldrMBuild :: Monad m => SerialT m Int -> m [Int]
446−foldrMBuild = S.foldrM (\x xs -> (x :) <$> xs) (return [])
447−
448−{-# INLINE foldl'Reduce #-}
449−foldl'Reduce :: Monad m => SerialT m Int -> m Int
450−foldl'Reduce = S.foldl' (+) 0
451−
452−{-# INLINE foldl1'Reduce #-}
453−foldl1'Reduce :: Monad m => SerialT m Int -> m (Maybe Int)
454−foldl1'Reduce = S.foldl1' (+)
455−
456−{-# INLINE foldlM'Reduce #-}
457−foldlM'Reduce :: Monad m => SerialT m Int -> m Int
458−foldlM'Reduce = S.foldlM' (\xs a -> return $ a + xs) (return 0)
459−
460−{-# INLINE last #-}
461−last :: Monad m => SerialT m Int -> m (Maybe Int)
462−last = S.last
463−
464−{-# INLINE _head #-}
465−_head :: Monad m => SerialT m Int -> m (Maybe Int)
466−_head = S.head
467−
468−{-# INLINE elem #-}
469−elem :: Monad m => Int -> SerialT m Int -> m Bool
470−elem value = S.elem (value + 1)
471−
472−{-# INLINE notElem #-}
473−notElem :: Monad m => Int -> SerialT m Int -> m Bool
474−notElem value = S.notElem (value + 1)
475−
476−{-# INLINE length #-}
477−length :: Monad m => SerialT m Int -> m Int
478−length = S.length
479−
480−{-# INLINE all #-}
481−all :: Monad m => Int -> SerialT m Int -> m Bool
482−all value = S.all (<= (value + 1))
483−
484−{-# INLINE any #-}
485−any :: Monad m => Int -> SerialT m Int -> m Bool
486−any value = S.any (> (value + 1))
487−
488−{-# INLINE and #-}
489−and :: Monad m => Int -> SerialT m Int -> m Bool
490−and value = S.and . S.map (<= (value + 1))
491−
492−{-# INLINE or #-}
493−or :: Monad m => Int -> SerialT m Int -> m Bool
494−or value = S.or . S.map (> (value + 1))
495−
496−{-# INLINE find #-}
497−find :: Monad m => Int -> SerialT m Int -> m (Maybe Int)
498−find value = S.find (== (value + 1))
499−
500−{-# INLINE findM #-}
501−findM :: Monad m => Int -> SerialT m Int -> m (Maybe Int)
502−findM value = S.findM (\z -> return $ z == (value + 1))
503−
504−{-# INLINE findIndex #-}
505−findIndex :: Monad m => Int -> SerialT m Int -> m (Maybe Int)
506−findIndex value = S.findIndex (== (value + 1))
507−
508−{-# INLINE elemIndex #-}
509−elemIndex :: Monad m => Int -> SerialT m Int -> m (Maybe Int)
510−elemIndex value = S.elemIndex (value + 1)
511−
512−{-# INLINE maximum #-}
513−maximum :: Monad m => SerialT m Int -> m (Maybe Int)
514−maximum = S.maximum
515−
516−{-# INLINE minimum #-}
517−minimum :: Monad m => SerialT m Int -> m (Maybe Int)
518−minimum = S.minimum
519−
520−{-# INLINE sum #-}
521−sum :: Monad m => SerialT m Int -> m Int
522−sum = S.sum
523−
524−{-# INLINE product #-}
525−product :: Monad m => SerialT m Int -> m Int
526−product = S.product
527−
528−{-# INLINE minimumBy #-}
529−minimumBy :: Monad m => SerialT m Int -> m (Maybe Int)
530−minimumBy = S.minimumBy compare
531−
532−{-# INLINE maximumBy #-}
533−maximumBy :: Monad m => SerialT m Int -> m (Maybe Int)
534−maximumBy = S.maximumBy compare
535−
536−{-# INLINE the #-}
537−the :: Monad m => SerialT m Int -> m (Maybe Int)
538−the = S.the
539−
540−{-# INLINE drainN #-}
541−drainN :: Monad m => Int -> SerialT m Int -> m ()
542−drainN val = S.drainN val
543−
544−{-# INLINE drainWhile #-}
545−drainWhile :: Monad m => SerialT m Int -> m ()
546−drainWhile = S.drainWhile (const True)
547−
548−{-# INLINE (!!) #-}
549−(!!) :: Monad m => Int -> SerialT m Int -> m (Maybe Int)
550−(!!) val = flip (Internal.!!) val
551−
552−{-# INLINE lookup #-}
553−lookup :: Monad m => Int -> SerialT m Int -> m (Maybe Int)
554−lookup val = S.lookup val . S.map (\x -> (x, x))
555−
556−o_1_space_elimination_folds :: Int -> [Benchmark]
557−o_1_space_elimination_folds value =
558− [ bgroup "elimination"
559− -- Basic folds
560− [ bgroup "reduce"
561− [ bgroup
562− "IO"
563− [ benchIOSink value "foldl'" foldl'Reduce
564− , benchIOSink value "foldl1'" foldl1'Reduce
565− , benchIOSink value "foldlM'" foldlM'Reduce
566− ]
567− , bgroup
568− "Identity"
569− [ benchIdentitySink value "foldl'" foldl'Reduce
570− , benchIdentitySink value "foldl1'" foldl1'Reduce
571− , benchIdentitySink value "foldlM'" foldlM'Reduce
572− ]
573− ]
574− , bgroup "build"
575− [ bgroup "IO"
576− [ benchIOSink value "foldrMElem" (foldrMElem value)
577− ]
578− , bgroup "Identity"
579− [ benchIdentitySink value "foldrMElem" (foldrMElem value)
580− , benchIdentitySink value "foldrToStreamLength"
581− (S.length . runIdentity . foldrToStream)
582− , benchPureSink value "foldrMToListLength"
583− (P.length . runIdentity . foldrMBuild)
584− ]
585− ]
586−
587− -- deconstruction
588− , benchIOSink value "uncons" uncons
589− , benchIOSink value "init" init
590−
591− -- draining
592− , benchIOSink value "drain" $ toNull serially
593− , benchIOSink value "drainN" $ drainN value
594− , benchIOSink value "drainWhile" $ drainWhile
595− , benchPureSink value "drain (pure)" P.id
596− , benchIOSink value "mapM_" mapM_
597−
598− -- this is too fast, causes all benchmarks reported in ns
599− -- , benchIOSink value "head" head
600− , benchIOSink value "last" last
601− , benchIOSink value "length" length
602− , benchHoistSink value "length . generally"
603− (length . Internal.generally)
604− , benchIOSink value "sum" sum
605− , benchIOSink value "product" product
606− , benchIOSink value "maximumBy" maximumBy
607− , benchIOSink value "maximum" maximum
608− , benchIOSink value "minimumBy" minimumBy
609− , benchIOSink value "minimum" minimum
610−
611− , bench "the" $ nfIO $ randomRIO (1,1) >>= the . repeat value
612− , benchIOSink value "find" (find value)
613− , benchIOSink value "findM" (findM value)
614− -- , benchIOSink value "lookupFirst" (lookup 1)
615− , benchIOSink value "lookupNever" (lookup (value + 1))
616− , benchIOSink value "(!!)" ((!!) value)
617− , benchIOSink value "findIndex" (findIndex value)
618− , benchIOSink value "elemIndex" (elemIndex value)
619− -- this is too fast, causes all benchmarks reported in ns
620− -- , benchIOSink value "null" S.null
621− , benchIOSink value "elem" (elem value)
622− , benchIOSink value "notElem" (notElem value)
623− , benchIOSink value "all" (all value)
624− , benchIOSink value "any" (any value)
625− , benchIOSink value "and" (and value)
626− , benchIOSink value "or" (or value)
627−
628− , benchPureSink value "showsPrec pure streams" showInstance
629− -- length is used to check for foldr/build fusion
630− , benchPureSink value "length . IsList.toList" (P.length . GHC.toList)
631− ]
632− ]
633−
634−-------------------------------------------------------------------------------
635−-- Buffered Transformations by fold
636−-------------------------------------------------------------------------------
637−
638−{-# INLINE foldl'Build #-}
639−foldl'Build :: Monad m => SerialT m Int -> m [Int]
640−foldl'Build = S.foldl' (flip (:)) []
641−
642−{-# INLINE foldlM'Build #-}
643−foldlM'Build :: Monad m => SerialT m Int -> m [Int]
644−foldlM'Build = S.foldlM' (\xs x -> return $ x : xs) (return [])
645−
646−o_n_heap_elimination_foldl :: Int -> [Benchmark]
647−o_n_heap_elimination_foldl value =
648− [ bgroup "foldl"
649− -- Left folds for building a structure are inherently non-streaming
650− -- as the structure cannot be lazily consumed until fully built.
651− [ benchIOSink value "foldl'/build/IO" foldl'Build
652− , benchIdentitySink value "foldl'/build/Identity" foldl'Build
653− , benchIOSink value "foldlM'/build/IO" foldlM'Build
654− , benchIdentitySink value "foldlM'/build/Identity" foldlM'Build
655− ]
656− ]
657−
658−-- For comparisons
659−{-# INLINE showInstanceList #-}
660−showInstanceList :: [Int] -> P.String
661−showInstanceList = P.show
662−
663−o_n_heap_elimination_buffered :: Int -> [Benchmark]
664−o_n_heap_elimination_buffered value =
665− [ bgroup "buffered"
666− -- Buffers the output of show/read.
667− -- XXX can the outputs be streaming? Can we have special read/show
668− -- style type classes, readM/showM supporting streaming effects?
669− [ bench "showPrec Haskell lists" $ nf showInstanceList (mkList value)
670− ]
671− ]
672−
673−{-# INLINE foldrMReduce #-}
674−foldrMReduce :: Monad m => SerialT m Int -> m Int
675−foldrMReduce = S.foldrM (\x xs -> (x +) <$> xs) (return 0)
676−
677−o_n_space_elimination_foldr :: Int -> [Benchmark]
678−o_n_space_elimination_foldr value =
679− -- Head recursive strict right folds.
680− [ bgroup "foldr"
681− -- accumulation due to strictness of IO monad
682− [ benchIOSink value "foldrM/build/IO (toList)" foldrMBuild
683− -- Right folds for reducing are inherently non-streaming as the
684− -- expression needs to be fully built before it can be reduced.
685− , benchIdentitySink value "foldrM/reduce/Identity (sum)" foldrMReduce
686− , benchIOSink value "foldrM/reduce/IO (sum)" foldrMReduce
687− ]
688− ]
689−
690−o_n_heap_elimination_toList :: Int -> [Benchmark]
691−o_n_heap_elimination_toList value =
692− [ bgroup "toList"
693− -- Converting the stream to a list or pure stream in a strict monad
694− [ benchIOSink value "toListRev" Internal.toListRev
695− , benchIOSink value "toPureRev" Internal.toPureRev
696− ]
697− ]
698−
699−o_n_space_elimination_toList :: Int -> [Benchmark]
700−o_n_space_elimination_toList value =
701− [ bgroup "toList"
702− -- Converting the stream to a list or pure stream in a strict monad
703− [ benchIOSink value "toList" S.toList
704− , benchIOSink value "toPure" Internal.toPure
705− ]
706− ]
707−
708−-------------------------------------------------------------------------------
709−-- Multi-stream folds
710−-------------------------------------------------------------------------------
711−
712−-------------------------------------------------------------------------------
713−-- Multi-stream pure
714−-------------------------------------------------------------------------------
715−
716−{-# INLINE eqBy' #-}
717−eqBy' :: (Monad m, P.Eq a) => SerialT m a -> m P.Bool
718−eqBy' src = S.eqBy (==) src src
719−
720−{-# INLINE eqByPure #-}
721−eqByPure :: Int -> Int -> Identity Bool
722−eqByPure value n = eqBy' (sourceUnfoldr value n)
723−
724−#ifdef INSPECTION
725−inspect $ hasNoTypeClasses 'eqByPure
726−inspect $ 'eqByPure `hasNoType` ''SPEC
727−inspect $ 'eqByPure `hasNoType` ''D.Step
728−#endif
729−
730−{-# INLINE eqInstance #-}
731−eqInstance :: SerialT Identity Int -> Bool
732−eqInstance src = src == src
733−
734−{-# INLINE eqInstanceNotEq #-}
735−eqInstanceNotEq :: SerialT Identity Int -> Bool
736−eqInstanceNotEq src = src P./= src
737−
738−{-# INLINE cmpBy' #-}
739−cmpBy' :: (Monad m, P.Ord a) => SerialT m a -> m P.Ordering
740−cmpBy' src = S.cmpBy P.compare src src
741−
742−{-# INLINE cmpByPure #-}
743−cmpByPure :: Int -> Int -> Identity P.Ordering
744−cmpByPure value n = cmpBy' (sourceUnfoldr value n)
745−
746−#ifdef INSPECTION
747−inspect $ hasNoTypeClasses 'cmpByPure
748−inspect $ 'cmpByPure `hasNoType` ''SPEC
749−inspect $ 'cmpByPure `hasNoType` ''D.Step
750−#endif
751−
752−{-# INLINE ordInstance #-}
753−ordInstance :: SerialT Identity Int -> Bool
754−ordInstance src = src P.< src
755−
756−o_1_space_elimination_multi_stream_pure :: Int -> [Benchmark]
757−o_1_space_elimination_multi_stream_pure value =
758− [ bgroup "multi-stream-pure"
759− [ benchPureSink1 "eqBy" (eqByPure value)
760− , benchPureSink value "==" eqInstance
761− , benchPureSink value "/=" eqInstanceNotEq
762− , benchPureSink1 "cmpBy" (cmpByPure value)
763− , benchPureSink value "<" ordInstance
764− ]
765− ]
766−
767−{-# INLINE isPrefixOf #-}
768−isPrefixOf :: Monad m => SerialT m Int -> m Bool
769−isPrefixOf src = S.isPrefixOf src src
770−
771−{-# INLINE isSubsequenceOf #-}
772−isSubsequenceOf :: Monad m => SerialT m Int -> m Bool
773−isSubsequenceOf src = S.isSubsequenceOf src src
774−
775−{-# INLINE stripPrefix #-}
776−stripPrefix :: Monad m => SerialT m Int -> m ()
777−stripPrefix src = do
778− _ <- S.stripPrefix src src
779− return ()
780−
781−{-# INLINE eqBy #-}
782−eqBy :: Int -> Int -> IO Bool
783−eqBy value n = eqBy' (sourceUnfoldrM value n)
784−
785−#ifdef INSPECTION
786−inspect $ hasNoTypeClasses 'eqBy
787−inspect $ 'eqBy `hasNoType` ''SPEC
788−inspect $ 'eqBy `hasNoType` ''D.Step
789−#endif
790−
791−{-# INLINE cmpBy #-}
792−cmpBy :: Int -> Int -> IO P.Ordering
793−cmpBy value n = cmpBy' (sourceUnfoldrM value n)
794−
795−#ifdef INSPECTION
796−inspect $ hasNoTypeClasses 'cmpBy
797−inspect $ 'cmpBy `hasNoType` ''SPEC
798−inspect $ 'cmpBy `hasNoType` ''D.Step
799−#endif
800−
801−o_1_space_elimination_multi_stream :: Int -> [Benchmark]
802−o_1_space_elimination_multi_stream value =
803− [ bgroup "multi-stream"
804− [ benchIOSink1 "eqBy" (eqBy value)
805− , benchIOSink1 "cmpBy" (cmpBy value)
806− , benchIOSink value "isPrefixOf" isPrefixOf
807− , benchIOSink value "isSubsequenceOf" isSubsequenceOf
808− , benchIOSink value "stripPrefix" stripPrefix
809− ]
810− ]
811−
812−-------------------------------------------------------------------------------
813−-- Pipelines (stream-to-stream transformations)
814−-------------------------------------------------------------------------------
815−
816−-------------------------------------------------------------------------------
817−-- one-to-one transformations
818−-------------------------------------------------------------------------------
819−
820−-------------------------------------------------------------------------------
821−-- Traversable Instance
822−-------------------------------------------------------------------------------
823−
824−{-# INLINE traversableTraverse #-}
825−traversableTraverse :: SerialT Identity Int -> IO (SerialT Identity Int)
826−traversableTraverse = P.traverse return
827−
828−{-# INLINE traversableSequenceA #-}
829−traversableSequenceA :: SerialT Identity Int -> IO (SerialT Identity Int)
830−traversableSequenceA = P.sequenceA . P.fmap return
831−
832−{-# INLINE traversableMapM #-}
833−traversableMapM :: SerialT Identity Int -> IO (SerialT Identity Int)
834−traversableMapM = P.mapM return
835−
836−{-# INLINE traversableSequence #-}
837−traversableSequence :: SerialT Identity Int -> IO (SerialT Identity Int)
838−traversableSequence = P.sequence . P.fmap return
839−
840−{-# INLINE benchPureSinkIO #-}
841−benchPureSinkIO
842− :: NFData b
843− => Int -> String -> (SerialT Identity Int -> IO b) -> Benchmark
844−benchPureSinkIO value name f =
845− bench name $ nfIO $ randomRIO (1, 1) >>= f . sourceUnfoldr value
846−
847−o_n_space_traversable :: Int -> [Benchmark]
848−o_n_space_traversable value =
849− -- Buffering operations using heap proportional to number of elements.
850− [ bgroup "traversable"
851− -- Traversable instance
852− [ benchPureSinkIO value "traverse" traversableTraverse
853− , benchPureSinkIO value "sequenceA" traversableSequenceA
854− , benchPureSinkIO value "mapM" traversableMapM
855− , benchPureSinkIO value "sequence" traversableSequence
856− ]
857− ]
858−
859−-------------------------------------------------------------------------------
860−-- maps and scans
861−-------------------------------------------------------------------------------
862−
863−{-# INLINE scanl' #-}
864−scanl' :: MonadIO m => Int -> SerialT m Int -> m ()
865−scanl' n = composeN n $ S.scanl' (+) 0
866−
867−{-# INLINE scanlM' #-}
868−scanlM' :: MonadIO m => Int -> SerialT m Int -> m ()
869−scanlM' n = composeN n $ S.scanlM' (\b a -> return $ b + a) (return 0)
870−
871−{-# INLINE scanl1' #-}
872−scanl1' :: MonadIO m => Int -> SerialT m Int -> m ()
873−scanl1' n = composeN n $ S.scanl1' (+)
874−
875−{-# INLINE scanl1M' #-}
876−scanl1M' :: MonadIO m => Int -> SerialT m Int -> m ()
877−scanl1M' n = composeN n $ S.scanl1M' (\b a -> return $ b + a)
878−
879−{-# INLINE scan #-}
880−scan :: MonadIO m => Int -> SerialT m Int -> m ()
881−scan n = composeN n $ S.scan FL.sum
882−
883−{-# INLINE postscanl' #-}
884−postscanl' :: MonadIO m => Int -> SerialT m Int -> m ()
885−postscanl' n = composeN n $ S.postscanl' (+) 0
886−
887−{-# INLINE postscanlM' #-}
888−postscanlM' :: MonadIO m => Int -> SerialT m Int -> m ()
889−postscanlM' n = composeN n $ S.postscanlM' (\b a -> return $ b + a) (return 0)
890−
891−{-# INLINE postscan #-}
892−postscan :: MonadIO m => Int -> SerialT m Int -> m ()
893−postscan n = composeN n $ S.postscan FL.sum
894−
895−{-# INLINE sequence #-}
896−sequence ::
897− (S.IsStream t, S.MonadAsync m)
898− => (t m Int -> S.SerialT m Int)
899− -> t m (m Int)
900− -> m ()
901−sequence t = S.drain . t . S.sequence
902−
903−{-# INLINE tap #-}
904−tap :: MonadIO m => Int -> SerialT m Int -> m ()
905−tap n = composeN n $ S.tap FL.sum
906−
907−{-# INLINE tapRate #-}
908−tapRate :: Int -> SerialT IO Int -> IO ()
909−tapRate n str = do
910− cref <- newIORef 0
911− composeN n (Internal.tapRate 1 (\c -> modifyIORef' cref (c +))) str
912−
913−{-# INLINE pollCounts #-}
914−pollCounts :: Int -> SerialT IO Int -> IO ()
915−pollCounts n =
916− composeN n (Internal.pollCounts (P.const P.True) f FL.drain)
917−
918− where
919−
920− f = Internal.rollingMap (P.-) . Internal.delayPost 1
921−
922−{-# INLINE timestamped #-}
923−timestamped :: (S.MonadAsync m) => SerialT m Int -> m ()
924−timestamped = S.drain . Internal.timestamped
925−
926−{-# INLINE foldrS #-}
927−foldrS :: MonadIO m => Int -> SerialT m Int -> m ()
928−foldrS n = composeN n $ Internal.foldrS S.cons S.nil
929−
930−{-# INLINE foldrSMap #-}
931−foldrSMap :: MonadIO m => Int -> SerialT m Int -> m ()
932−foldrSMap n = composeN n $ Internal.foldrS (\x xs -> x + 1 `S.cons` xs) S.nil
933−
934−{-# INLINE foldrT #-}
935−foldrT :: MonadIO m => Int -> SerialT m Int -> m ()
936−foldrT n = composeN n $ Internal.foldrT S.cons S.nil
937−
938−{-# INLINE foldrTMap #-}
939−foldrTMap :: MonadIO m => Int -> SerialT m Int -> m ()
940−foldrTMap n = composeN n $ Internal.foldrT (\x xs -> x + 1 `S.cons` xs) S.nil
941−
942−
943−{-# INLINE trace #-}
944−trace :: MonadAsync m => Int -> SerialT m Int -> m ()
945−trace n = composeN n $ Internal.trace return
946−
947−o_1_space_mapping :: Int -> [Benchmark]
948−o_1_space_mapping value =
949− [ bgroup
950− "mapping"
951− [
952− -- Right folds
953− benchIOSink value "foldrS" (foldrS 1)
954− , benchIOSink value "foldrSMap" (foldrSMap 1)
955− , benchIOSink value "foldrT" (foldrT 1)
956− , benchIOSink value "foldrTMap" (foldrTMap 1)
957−
958− -- Mapping
959− , benchIOSink value "map" (mapN serially 1)
960− , bench "sequence" $ nfIO $ randomRIO (1, 1000) >>= \n ->
961− sequence serially (sourceUnfoldrAction value n)
962− , benchIOSink value "mapM" (mapM serially 1)
963− , benchIOSink value "tap" (tap 1)
964− , benchIOSink value "tapRate 1 second" (tapRate 1)
965− , benchIOSink value "pollCounts 1 second" (pollCounts 1)
966− , benchIOSink value "timestamped" timestamped
967−
968− -- Scanning
969− , benchIOSink value "scanl'" (scanl' 1)
970− , benchIOSink value "scanl1'" (scanl1' 1)
971− , benchIOSink value "scanlM'" (scanlM' 1)
972− , benchIOSink value "scanl1M'" (scanl1M' 1)
973− , benchIOSink value "postscanl'" (postscanl' 1)
974− , benchIOSink value "postscanlM'" (postscanlM' 1)
975−
976− , benchIOSink value "scan" (scan 1)
977− , benchIOSink value "postscan" (postscan 1)
978− ]
979− ]
980−
981−o_1_space_mappingX4 :: Int -> [Benchmark]
982−o_1_space_mappingX4 value =
983− [ bgroup "mappingX4"
984− [ benchIOSink value "map" (mapN serially 4)
985− , benchIOSink value "mapM" (mapM serially 4)
986− , benchIOSink value "trace" (trace 4)
987−
988− , benchIOSink value "scanl'" (scanl' 4)
989− , benchIOSink value "scanl1'" (scanl1' 4)
990− , benchIOSink value "scanlM'" (scanlM' 4)
991− , benchIOSink value "scanl1M'" (scanl1M' 4)
992− , benchIOSink value "postscanl'" (postscanl' 4)
993− , benchIOSink value "postscanlM'" (postscanlM' 4)
994−
995− ]
996− ]
997−
998−{-# INLINE sieveScan #-}
999−sieveScan :: Monad m => SerialT m Int -> SerialT m Int
1000−sieveScan =
1001− S.mapMaybe snd
1002− . S.scanlM' (\(primes, _) n -> do
1003− return $
1004− let ps = takeWhile (\p -> p * p <= n) primes
1005− in if P.all (\p -> n `mod` p /= 0) ps
1006− then (primes ++ [n], Just n)
1007− else (primes, Nothing)) (return ([2], Just 2))
1008−
1009−o_n_space_mapping :: Int -> [Benchmark]
1010−o_n_space_mapping value =
1011− [ bgroup "mapping"
1012− [ benchIO "naive prime sieve"
1013− (\n -> S.sum $ sieveScan $ S.enumerateFromTo 2 (value + n))
1014− ]
1015− ]
1016−
1017−-------------------------------------------------------------------------------
1018−-- Iteration/looping utilities
1019−-------------------------------------------------------------------------------
1020−
1021−{-# INLINE iterateN #-}
1022−iterateN :: (Int -> a -> a) -> a -> Int -> a
1023−iterateN g initial count = f count initial
1024−
1025− where
1026−
1027− f (0 :: Int) x = x
1028− f i x = f (i - 1) (g i x)
1029−
1030−-- Iterate a transformation over a singleton stream
1031−{-# INLINE iterateSingleton #-}
1032−iterateSingleton :: S.MonadAsync m
1033− => (Int -> SerialT m Int -> SerialT m Int)
1034− -> Int
1035− -> Int
1036− -> SerialT m Int
1037−iterateSingleton g count n = iterateN g (return n) count
1038−
1039−-- XXX need to check why this is slower than the explicit recursion above, even
1040−-- if the above code is written in a foldr like head recursive way. We also
1041−-- need to try this with foldlM' once #150 is fixed.
1042−-- However, it is perhaps best to keep the iteration benchmarks independent of
1043−-- foldrM and any related fusion issues.
1044−{-# INLINE _iterateSingleton #-}
1045−_iterateSingleton ::
1046− S.MonadAsync m
1047− => (Int -> SerialT m Int -> SerialT m Int)
1048− -> Int
1049− -> Int
1050− -> SerialT m Int
1051−_iterateSingleton g value n = S.foldrM g (return n) $ sourceIntFromTo value n
1052−
1053−-- Apply transformation g count times on a stream of length len
1054−{-# INLINE iterateSource #-}
1055−iterateSource ::
1056− S.MonadAsync m
1057− => (SerialT m Int -> SerialT m Int)
1058− -> Int
1059− -> Int
1060− -> Int
1061− -> SerialT m Int
1062−iterateSource g count len n = f count (sourceUnfoldrM len n)
1063−
1064− where
1065−
1066− f (0 :: Int) stream = stream
1067− f i stream = f (i - 1) (g stream)
1068−
1069−-------------------------------------------------------------------------------
1070−-- Functor
1071−-------------------------------------------------------------------------------
1072−
1073−o_1_space_functor :: Int -> [Benchmark]
1074−o_1_space_functor value =
1075− [ bgroup "Functor"
1076− [ benchIOSink value "fmap" (fmapN serially 1)
1077− , benchIOSink value "fmap x 4" (fmapN serially 4)
1078− ]
1079− ]
1080−
1081−o_n_space_functor :: Int -> [Benchmark]
1082−o_n_space_functor value =
1083− [ bgroup "Functor"
1084− [ benchIO "(+) (n times) (baseline)" $ \i0 ->
1085− iterateN (\i acc -> acc >>= \n -> return $ i + n) (return i0) value
1086− , benchIOSrc serially "(<$) (n times)" $
1087− iterateSingleton (<$) value
1088− , benchIOSrc serially "fmap (n times)" $
1089− iterateSingleton (P.fmap . (+)) value
1090− {-
1091− , benchIOSrc serially "_(<$) (n times)" $
1092− _iterateSingleton (<$) value
1093− , benchIOSrc serially "_fmap (n times)" $
1094− _iterateSingleton (P.fmap . (+)) value
1095− -}
1096− ]
1097− ]
1098−
1099−-------------------------------------------------------------------------------
1100−-- Size reducing transformations (filtering)
1101−-------------------------------------------------------------------------------
1102−
1103−{-# INLINE filterEven #-}
1104−filterEven :: MonadIO m => Int -> SerialT m Int -> m ()
1105−filterEven n = composeN n $ S.filter even
1106−
1107−{-# INLINE filterAllOut #-}
1108−filterAllOut :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1109−filterAllOut value n = composeN n $ S.filter (> (value + 1))
1110−
1111−{-# INLINE filterAllIn #-}
1112−filterAllIn :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1113−filterAllIn value n = composeN n $ S.filter (<= (value + 1))
1114−
1115−{-# INLINE filterMEven #-}
1116−filterMEven :: MonadIO m => Int -> SerialT m Int -> m ()
1117−filterMEven n = composeN n $ S.filterM (return . even)
1118−
1119−{-# INLINE filterMAllOut #-}
1120−filterMAllOut :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1121−filterMAllOut value n = composeN n $ S.filterM (\x -> return $ x > (value + 1))
1122−
1123−{-# INLINE filterMAllIn #-}
1124−filterMAllIn :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1125−filterMAllIn value n = composeN n $ S.filterM (\x -> return $ x <= (value + 1))
1126−
1127−{-# INLINE _takeOne #-}
1128−_takeOne :: MonadIO m => Int -> SerialT m Int -> m ()
1129−_takeOne n = composeN n $ S.take 1
1130−
1131−{-# INLINE takeAll #-}
1132−takeAll :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1133−takeAll value n = composeN n $ S.take (value + 1)
1134−
1135−{-# INLINE takeWhileTrue #-}
1136−takeWhileTrue :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1137−takeWhileTrue value n = composeN n $ S.takeWhile (<= (value + 1))
1138−
1139−{-# INLINE takeWhileMTrue #-}
1140−takeWhileMTrue :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1141−takeWhileMTrue value n = composeN n $ S.takeWhileM (return . (<= (value + 1)))
1142−
1143−{-# INLINE takeByTime #-}
1144−takeByTime :: NanoSecond64 -> Int -> SerialT IO Int -> IO ()
1145−takeByTime i n = composeN n (Internal.takeByTime i)
1146−
1147−#ifdef INSPECTION
1148−-- inspect $ hasNoType 'takeByTime ''SPEC
1149−inspect $ hasNoTypeClasses 'takeByTime
1150−-- inspect $ 'takeByTime `hasNoType` ''D.Step
1151−#endif
1152−
1153−{-# INLINE dropOne #-}
1154−dropOne :: MonadIO m => Int -> SerialT m Int -> m ()
1155−dropOne n = composeN n $ S.drop 1
1156−
1157−{-# INLINE dropAll #-}
1158−dropAll :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1159−dropAll value n = composeN n $ S.drop (value + 1)
1160−
1161−{-# INLINE dropWhileTrue #-}
1162−dropWhileTrue :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1163−dropWhileTrue value n = composeN n $ S.dropWhile (<= (value + 1))
1164−
1165−{-# INLINE dropWhileMTrue #-}
1166−dropWhileMTrue :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1167−dropWhileMTrue value n = composeN n $ S.dropWhileM (return . (<= (value + 1)))
1168−
1169−{-# INLINE dropWhileFalse #-}
1170−dropWhileFalse :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1171−dropWhileFalse value n = composeN n $ S.dropWhile (> (value + 1))
1172−
1173−-- XXX Decide on the time interval
1174−{-# INLINE _intervalsOfSum #-}
1175−_intervalsOfSum :: MonadAsync m => Double -> Int -> SerialT m Int -> m ()
1176−_intervalsOfSum i n = composeN n (S.intervalsOf i FL.sum)
1177−
1178−{-# INLINE dropByTime #-}
1179−dropByTime :: NanoSecond64 -> Int -> SerialT IO Int -> IO ()
1180−dropByTime i n = composeN n (Internal.dropByTime i)
1181−
1182−#ifdef INSPECTION
1183−inspect $ hasNoTypeClasses 'dropByTime
1184−-- inspect $ 'dropByTime `hasNoType` ''D.Step
1185−#endif
1186−
1187−{-# INLINE findIndices #-}
1188−findIndices :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1189−findIndices value n = composeN n $ S.findIndices (== (value + 1))
1190−
1191−{-# INLINE elemIndices #-}
1192−elemIndices :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1193−elemIndices value n = composeN n $ S.elemIndices (value + 1)
1194−
1195−{-# INLINE deleteBy #-}
1196−deleteBy :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1197−deleteBy value n = composeN n $ S.deleteBy (>=) (value + 1)
1198−
1199−-- uniq . uniq == uniq, composeN 2 ~ composeN 1
1200−{-# INLINE uniq #-}
1201−uniq :: MonadIO m => Int -> SerialT m Int -> m ()
1202−uniq n = composeN n S.uniq
1203−
1204−{-# INLINE mapMaybe #-}
1205−mapMaybe :: MonadIO m => Int -> SerialT m Int -> m ()
1206−mapMaybe n =
1207− composeN n $
1208− S.mapMaybe
1209− (\x ->
1210− if P.odd x
1211− then Nothing
1212− else Just x)
1213−
1214−{-# INLINE mapMaybeM #-}
1215−mapMaybeM :: S.MonadAsync m => Int -> SerialT m Int -> m ()
1216−mapMaybeM n =
1217− composeN n $
1218− S.mapMaybeM
1219− (\x ->
1220− if P.odd x
1221− then return Nothing
1222− else return $ Just x)
1223−
1224−o_1_space_filtering :: Int -> [Benchmark]
1225−o_1_space_filtering value =
1226− [ bgroup "filtering"
1227− [ benchIOSink value "filter-even" (filterEven 1)
1228− , benchIOSink value "filter-all-out" (filterAllOut value 1)
1229− , benchIOSink value "filter-all-in" (filterAllIn value 1)
1230−
1231− , benchIOSink value "filterM-even" (filterMEven 1)
1232− , benchIOSink value "filterM-all-out" (filterMAllOut value 1)
1233− , benchIOSink value "filterM-all-in" (filterMAllIn value 1)
1234−
1235− -- Trimming
1236− , benchIOSink value "take-all" (takeAll value 1)
1237− , benchIOSink
1238− value
1239− "takeByTime-all"
1240− (takeByTime (NanoSecond64 maxBound) 1)
1241− , benchIOSink value "takeWhile-true" (takeWhileTrue value 1)
1242− -- , benchIOSink value "takeWhileM-true" (_takeWhileMTrue value 1)
1243− , benchIOSink value "drop-one" (dropOne 1)
1244− , benchIOSink value "drop-all" (dropAll value 1)
1245− , benchIOSink
1246− value
1247− "dropByTime-all"
1248− (dropByTime (NanoSecond64 maxBound) 1)
1249− , benchIOSink value "dropWhile-true" (dropWhileTrue value 1)
1250− -- , benchIOSink value "dropWhileM-true" (_dropWhileMTrue value 1)
1251− , benchIOSink
1252− value
1253− "dropWhile-false"
1254− (dropWhileFalse value 1)
1255− , benchIOSink value "deleteBy" (deleteBy value 1)
1256−
1257− , benchIOSink value "uniq" (uniq 1)
1258−
1259− -- Map and filter
1260− , benchIOSink value "mapMaybe" (mapMaybe 1)
1261− , benchIOSink value "mapMaybeM" (mapMaybeM 1)
1262−
1263− -- Searching (stateful map and filter)
1264− , benchIOSink value "findIndices" (findIndices value 1)
1265− , benchIOSink value "elemIndices" (elemIndices value 1)
1266− ]
1267− ]
1268−
1269−o_1_space_filteringX4 :: Int -> [Benchmark]
1270−o_1_space_filteringX4 value =
1271− [ bgroup "filteringX4"
1272− [ benchIOSink value "filter-even" (filterEven 4)
1273− , benchIOSink value "filter-all-out" (filterAllOut value 4)
1274− , benchIOSink value "filter-all-in" (filterAllIn value 4)
1275−
1276− , benchIOSink value "filterM-even" (filterMEven 4)
1277− , benchIOSink value "filterM-all-out" (filterMAllOut value 4)
1278− , benchIOSink value "filterM-all-in" (filterMAllIn value 4)
1279−
1280− -- trimming
1281− , benchIOSink value "take-all" (takeAll value 4)
1282− , benchIOSink value "takeWhile-true" (takeWhileTrue value 4)
1283− , benchIOSink value "takeWhileM-true" (takeWhileMTrue value 4)
1284− , benchIOSink value "drop-one" (dropOne 4)
1285− , benchIOSink value "drop-all" (dropAll value 4)
1286− , benchIOSink value "dropWhile-true" (dropWhileTrue value 4)
1287− , benchIOSink value "dropWhileM-true" (dropWhileMTrue value 4)
1288− , benchIOSink
1289− value
1290− "dropWhile-false"
1291− (dropWhileFalse value 4)
1292− , benchIOSink value "deleteBy" (deleteBy value 4)
1293−
1294− , benchIOSink value "uniq" (uniq 4)
1295−
1296− -- map and filter
1297− , benchIOSink value "mapMaybe" (mapMaybe 4)
1298− , benchIOSink value "mapMaybeM" (mapMaybeM 4)
1299−
1300− -- searching
1301− , benchIOSink value "findIndices" (findIndices value 4)
1302− , benchIOSink value "elemIndices" (elemIndices value 4)
1303− ]
1304− ]
1305−
1306−-------------------------------------------------------------------------------
1307−-- Grouping transformations
1308−-------------------------------------------------------------------------------
1309−
1310−{-# INLINE groups #-}
1311−groups :: MonadIO m => SerialT m Int -> m ()
1312−groups = S.drain . S.groups FL.drain
1313−
1314−-- XXX Change this test when the order of comparison is later changed
1315−{-# INLINE groupsByGT #-}
1316−groupsByGT :: MonadIO m => SerialT m Int -> m ()
1317−groupsByGT = S.drain . S.groupsBy (>) FL.drain
1318−
1319−{-# INLINE groupsByEq #-}
1320−groupsByEq :: MonadIO m => SerialT m Int -> m ()
1321−groupsByEq = S.drain . S.groupsBy (==) FL.drain
1322−
1323−{-# INLINE groupsByRollingLT #-}
1324−groupsByRollingLT :: MonadIO m => SerialT m Int -> m ()
1325−groupsByRollingLT =
1326− S.drain . S.groupsByRolling (<) FL.drain
1327−
1328−{-# INLINE groupsByRollingEq #-}
1329−groupsByRollingEq :: MonadIO m => SerialT m Int -> m ()
1330−groupsByRollingEq =
1331− S.drain . S.groupsByRolling (==) FL.drain
1332−
1333−o_1_space_grouping :: Int -> [Benchmark]
1334−o_1_space_grouping value =
1335− [ bgroup "grouping"
1336− [ benchIOSink value "groups" groups
1337− , benchIOSink value "groupsByGT" groupsByGT
1338− , benchIOSink value "groupsByEq" groupsByEq
1339− , benchIOSink value "groupsByRollingLT" groupsByRollingLT
1340− , benchIOSink value "groupsByRollingEq" groupsByRollingEq
1341− ]
1342− ]
1343−
1344−-------------------------------------------------------------------------------
1345−-- Size increasing transformations (insertions)
1346−-------------------------------------------------------------------------------
1347−
1348−{-# INLINE intersperse #-}
1349−intersperse :: S.MonadAsync m => Int -> Int -> SerialT m Int -> m ()
1350−intersperse value n = composeN n $ S.intersperse (value + 1)
1351−
1352−{-# INLINE intersperseM #-}
1353−intersperseM :: S.MonadAsync m => Int -> Int -> SerialT m Int -> m ()
1354−intersperseM value n = composeN n $ S.intersperseM (return $ value + 1)
1355−
1356−{-# INLINE insertBy #-}
1357−insertBy :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1358−insertBy value n = composeN n $ S.insertBy compare (value + 1)
1359−
1360−o_1_space_inserting :: Int -> [Benchmark]
1361−o_1_space_inserting value =
1362− [ bgroup "filtering"
1363− [ benchIOSink value "intersperse" (intersperse value 1)
1364− , benchIOSink value "intersperseM" (intersperseM value 1)
1365− , benchIOSink value "insertBy" (insertBy value 1)
1366− ]
1367− ]
1368−
1369−o_1_space_insertingX4 :: Int -> [Benchmark]
1370−o_1_space_insertingX4 value =
1371− [ bgroup "insertingX4"
1372− [ benchIOSink value "intersperse" (intersperse value 4)
1373− , benchIOSink value "insertBy" (insertBy value 4)
1374− ]
1375− ]
1376−
1377−-------------------------------------------------------------------------------
1378−-- Indexing
1379−-------------------------------------------------------------------------------
1380−
1381−{-# INLINE indexed #-}
1382−indexed :: MonadIO m => Int -> SerialT m Int -> m ()
1383−indexed n = composeN n (S.map snd . S.indexed)
1384−
1385−{-# INLINE indexedR #-}
1386−indexedR :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1387−indexedR value n = composeN n (S.map snd . S.indexedR value)
1388−
1389−o_1_space_indexing :: Int -> [Benchmark]
1390−o_1_space_indexing value =
1391− [ bgroup "indexing"
1392− [ benchIOSink value "indexed" (indexed 1)
1393− , benchIOSink value "indexedR" (indexedR value 1)
1394− ]
1395− ]
1396−
1397−o_1_space_indexingX4 :: Int -> [Benchmark]
1398−o_1_space_indexingX4 value =
1399− [ bgroup "indexingx4"
1400− [ benchIOSink value "indexed" (indexed 4)
1401− , benchIOSink value "indexedR" (indexedR value 4)
1402− ]
1403− ]
1404−
1405−-------------------------------------------------------------------------------
1406−-- Size conserving transformations (reordering, buffering, etc.)
1407−-------------------------------------------------------------------------------
1408−
1409−{-# INLINE reverse #-}
1410−reverse :: MonadIO m => Int -> SerialT m Int -> m ()
1411−reverse n = composeN n S.reverse
1412−
1413−{-# INLINE reverse' #-}
1414−reverse' :: MonadIO m => Int -> SerialT m Int -> m ()
1415−reverse' n = composeN n Internal.reverse'
1416−
1417−o_n_heap_buffering :: Int -> [Benchmark]
1418−o_n_heap_buffering value =
1419− [ bgroup "buffered"
1420− [
1421− -- Reversing/sorting a stream
1422− benchIOSink value "reverse" (reverse 1)
1423− , benchIOSink value "reverse'" (reverse' 1)
1424−
1425− , benchIOSink value "mkAsync" (mkAsync serially)
1426− ]
1427− ]
1428−
1429−-------------------------------------------------------------------------------
1430−-- Grouping/Splitting
1431−-------------------------------------------------------------------------------
1432−
1433−{-# INLINE classifySessionsOf #-}
1434−classifySessionsOf :: (S.MonadAsync m) => SerialT m Int -> m ()
1435−classifySessionsOf =
1436− S.drain
1437− . Internal.classifySessionsOf
1438− 3 (const (return False)) (P.fmap Right FL.drain)
1439− . S.map (\(ts,(k,a)) -> (k, a, ts))
1440− . Internal.timestamped
1441− . S.concatMap (\x -> S.map (x,) (S.enumerateFromTo 1 (10 :: Int)))
1442−
1443−o_n_space_grouping :: Int -> [Benchmark]
1444−o_n_space_grouping value =
1445− -- Buffering operations using heap proportional to group/window sizes.
1446− [ bgroup "grouping"
1447− [ benchIOSink (value `div` 10) "classifySessionsOf" classifySessionsOf
1448− ]
1449− ]
1450−
1451−-------------------------------------------------------------------------------
1452−-- Mixed Transformation
1453−-------------------------------------------------------------------------------
1454−
1455−{-# INLINE scanMap #-}
1456−scanMap :: MonadIO m => Int -> SerialT m Int -> m ()
1457−scanMap n = composeN n $ S.map (subtract 1) . S.scanl' (+) 0
1458−
1459−{-# INLINE dropMap #-}
1460−dropMap :: MonadIO m => Int -> SerialT m Int -> m ()
1461−dropMap n = composeN n $ S.map (subtract 1) . S.drop 1
1462−
1463−{-# INLINE dropScan #-}
1464−dropScan :: MonadIO m => Int -> SerialT m Int -> m ()
1465−dropScan n = composeN n $ S.scanl' (+) 0 . S.drop 1
1466−
1467−{-# INLINE takeDrop #-}
1468−takeDrop :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1469−takeDrop value n = composeN n $ S.drop 1 . S.take (value + 1)
1470−
1471−{-# INLINE takeScan #-}
1472−takeScan :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1473−takeScan value n = composeN n $ S.scanl' (+) 0 . S.take (value + 1)
1474−
1475−{-# INLINE takeMap #-}
1476−takeMap :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1477−takeMap value n = composeN n $ S.map (subtract 1) . S.take (value + 1)
1478−
1479−{-# INLINE filterDrop #-}
1480−filterDrop :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1481−filterDrop value n = composeN n $ S.drop 1 . S.filter (<= (value + 1))
1482−
1483−{-# INLINE filterTake #-}
1484−filterTake :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1485−filterTake value n = composeN n $ S.take (value + 1) . S.filter (<= (value + 1))
1486−
1487−{-# INLINE filterScan #-}
1488−filterScan :: MonadIO m => Int -> SerialT m Int -> m ()
1489−filterScan n = composeN n $ S.scanl' (+) 0 . S.filter (<= maxBound)
1490−
1491−{-# INLINE filterScanl1 #-}
1492−filterScanl1 :: MonadIO m => Int -> SerialT m Int -> m ()
1493−filterScanl1 n = composeN n $ S.scanl1' (+) . S.filter (<= maxBound)
1494−
1495−{-# INLINE filterMap #-}
1496−filterMap :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
1497−filterMap value n = composeN n $ S.map (subtract 1) . S.filter (<= (value + 1))
1498−
1499−-------------------------------------------------------------------------------
1500−-- Scan and fold
1501−-------------------------------------------------------------------------------
1502−
1503−data Pair a b =
1504− Pair !a !b
1505− deriving (Generic, NFData)
1506−
1507−{-# INLINE sumProductFold #-}
1508−sumProductFold :: Monad m => SerialT m Int -> m (Int, Int)
1509−sumProductFold = S.foldl' (\(s, p) x -> (s + x, p P.* x)) (0, 1)
1510−
1511−{-# INLINE sumProductScan #-}
1512−sumProductScan :: Monad m => SerialT m Int -> m (Pair Int Int)
1513−sumProductScan =
1514− S.foldl' (\(Pair _ p) (s0, x) -> Pair s0 (p P.* x)) (Pair 0 1) .
1515− S.scanl' (\(s, _) x -> (s + x, x)) (0, 0)
1516−
1517−{-# INLINE foldl'ReduceMap #-}
1518−foldl'ReduceMap :: Monad m => SerialT m Int -> m Int
1519−foldl'ReduceMap = P.fmap (+ 1) . S.foldl' (+) 0
1520−
1521−o_1_space_transformations_mixed :: Int -> [Benchmark]
1522−o_1_space_transformations_mixed value =
1523− -- scanl-map and foldl-map are equivalent to the scan and fold in the foldl
1524− -- library. If scan/fold followed by a map is efficient enough we may not
1525− -- need monolithic implementations of these.
1526− [ bgroup "mixed"
1527− [ benchIOSink value "scanl-map" (scanMap 1)
1528− , benchIOSink value "foldl-map" foldl'ReduceMap
1529− , benchIOSink value "sum-product-fold" sumProductFold
1530− , benchIOSink value "sum-product-scan" sumProductScan
1531− ]
1532− ]
1533−
1534−o_1_space_transformations_mixedX4 :: Int -> [Benchmark]
1535−o_1_space_transformations_mixedX4 value =
1536− [ bgroup "mixedX4"
1537− [ benchIOSink value "scan-map" (scanMap 4)
1538− , benchIOSink value "drop-map" (dropMap 4)
1539− , benchIOSink value "drop-scan" (dropScan 4)
1540− , benchIOSink value "take-drop" (takeDrop value 4)
1541− , benchIOSink value "take-scan" (takeScan value 4)
1542− , benchIOSink value "take-map" (takeMap value 4)
1543− , benchIOSink value "filter-drop" (filterDrop value 4)
1544− , benchIOSink value "filter-take" (filterTake value 4)
1545− , benchIOSink value "filter-scan" (filterScan 4)
1546− , benchIOSink value "filter-scanl1" (filterScanl1 4)
1547− , benchIOSink value "filter-map" (filterMap value 4)
1548− ]
1549− ]
1550−
1551−-------------------------------------------------------------------------------
1552−-- Iterating a transformation over and over again
1553−-------------------------------------------------------------------------------
1554−
1555−-- this is quadratic
1556−{-# INLINE iterateScan #-}
1557−iterateScan :: S.MonadAsync m => Int -> Int -> Int -> SerialT m Int
1558−iterateScan = iterateSource (S.scanl' (+) 0)
1559−
1560−-- this is quadratic
1561−{-# INLINE iterateScanl1 #-}
1562−iterateScanl1 :: S.MonadAsync m => Int -> Int -> Int -> SerialT m Int
1563−iterateScanl1 = iterateSource (S.scanl1' (+))
1564−
1565−{-# INLINE iterateMapM #-}
1566−iterateMapM :: S.MonadAsync m => Int -> Int -> Int -> SerialT m Int
1567−iterateMapM = iterateSource (S.mapM return)
1568−
1569−{-# INLINE iterateFilterEven #-}
1570−iterateFilterEven :: S.MonadAsync m => Int -> Int -> Int -> SerialT m Int
1571−iterateFilterEven = iterateSource (S.filter even)
1572−
1573−{-# INLINE iterateTakeAll #-}
1574−iterateTakeAll :: S.MonadAsync m => Int -> Int -> Int -> Int -> SerialT m Int
1575−iterateTakeAll value = iterateSource (S.take (value + 1))
1576−
1577−{-# INLINE iterateDropOne #-}
1578−iterateDropOne :: S.MonadAsync m => Int -> Int -> Int -> SerialT m Int
1579−iterateDropOne = iterateSource (S.drop 1)
1580−
1581−{-# INLINE iterateDropWhileFalse #-}
1582−iterateDropWhileFalse :: S.MonadAsync m
1583− => Int -> Int -> Int -> Int -> SerialT m Int
1584−iterateDropWhileFalse value = iterateSource (S.dropWhile (> (value + 1)))
1585−
1586−{-# INLINE iterateDropWhileTrue #-}
1587−iterateDropWhileTrue :: S.MonadAsync m
1588− => Int -> Int -> Int -> Int -> SerialT m Int
1589−iterateDropWhileTrue value = iterateSource (S.dropWhile (<= (value + 1)))
1590−
1591−{-# INLINE tail #-}
1592−tail :: Monad m => SerialT m a -> m ()
1593−tail s = S.tail s >>= P.mapM_ tail
1594−
1595−{-# INLINE nullHeadTail #-}
1596−nullHeadTail :: Monad m => SerialT m Int -> m ()
1597−nullHeadTail s = do
1598− r <- S.null s
1599− when (not r) $ do
1600− _ <- S.head s
1601− S.tail s >>= P.mapM_ nullHeadTail
1602−
1603−-- Head recursive operations.
1604−o_n_stack_iterated :: Int -> [Benchmark]
1605−o_n_stack_iterated value = by10 `seq` by100 `seq`
1606− [ bgroup "iterated"
1607− [ benchIOSrc serially "mapM (n/10 x 10)" $ iterateMapM by10 10
1608− , benchIOSrc serially "scanl' (quadratic) (n/100 x 100)" $
1609− iterateScan by100 100
1610− , benchIOSrc serially "scanl1' (n/10 x 10)" $ iterateScanl1 by10 10
1611− , benchIOSrc serially "filterEven (n/10 x 10)" $
1612− iterateFilterEven by10 10
1613− , benchIOSrc serially "takeAll (n/10 x 10)" $
1614− iterateTakeAll value by10 10
1615− , benchIOSrc serially "dropOne (n/10 x 10)" $ iterateDropOne by10 10
1616− , benchIOSrc serially "dropWhileFalse (n/10 x 10)" $
1617− iterateDropWhileFalse value by10 10
1618− , benchIOSrc serially "dropWhileTrue (n/10 x 10)" $
1619− iterateDropWhileTrue value by10 10
1620− , benchIOSink value "tail" tail
1621− , benchIOSink value "nullHeadTail" nullHeadTail
1622− ]
1623− ]
1624−
1625− where
1626−
1627− by10 = value `div` 10
1628− by100 = value `div` 100
1629−
1630−-------------------------------------------------------------------------------
1631−-- Pipes
1632−-------------------------------------------------------------------------------
1633−
1634−o_1_space_pipes :: Int -> [Benchmark]
1635−o_1_space_pipes value =
1636− [ bgroup "pipes"
1637− [ benchIOSink value "mapM" (transformMapM serially 1)
1638− , benchIOSink value "compose" (transformComposeMapM serially 1)
1639− , benchIOSink value "tee" (transformTeeMapM serially 1)
1640− , benchIOSink value "zip" (transformZipMapM serially 1)
1641− ]
1642− ]
1643−
1644−o_1_space_pipesX4 :: Int -> [Benchmark]
1645−o_1_space_pipesX4 value =
1646− [ bgroup "pipesX4"
1647− [ benchIOSink value "mapM" (transformMapM serially 4)
1648− , benchIOSink value "compose" (transformComposeMapM serially 4)
1649− , benchIOSink value "tee" (transformTeeMapM serially 4)
1650− , benchIOSink value "zip" (transformZipMapM serially 4)
1651− ]
1652− ]
1653−
1654−-------------------------------------------------------------------------------
1655−-- Multi-Stream
1656−-------------------------------------------------------------------------------
1657−
1658−-------------------------------------------------------------------------------
1659−-- Appending
1660−-------------------------------------------------------------------------------
1661−
1662−{-# INLINE serial2 #-}
1663−serial2 :: Int -> Int -> IO ()
1664−serial2 count n =
1665− S.drain $
1666− S.serial (sourceUnfoldrM count n) (sourceUnfoldrM count (n + 1))
1667−
1668−{-# INLINE serial4 #-}
1669−serial4 :: Int -> Int -> IO ()
1670−serial4 count n =
1671− S.drain $
1672− S.serial
1673− (S.serial (sourceUnfoldrM count n) (sourceUnfoldrM count (n + 1)))
1674− (S.serial
1675− (sourceUnfoldrM count (n + 2))
1676− (sourceUnfoldrM count (n + 3)))
1677−
1678−{-# INLINE append2 #-}
1679−append2 :: Int -> Int -> IO ()
1680−append2 count n =
1681− S.drain $
1682− Internal.append (sourceUnfoldrM count n) (sourceUnfoldrM count (n + 1))
1683−
1684−{-# INLINE append4 #-}
1685−append4 :: Int -> Int -> IO ()
1686−append4 count n =
1687− S.drain $
1688− Internal.append
1689− (Internal.append
1690− (sourceUnfoldrM count n)
1691− (sourceUnfoldrM count (n + 1)))
1692− (Internal.append
1693− (sourceUnfoldrM count (n + 2))
1694− (sourceUnfoldrM count (n + 3)))
1695−
1696−#ifdef INSPECTION
1697−inspect $ hasNoTypeClasses 'append2
1698−inspect $ 'append2 `hasNoType` ''SPEC
1699−inspect $ 'append2 `hasNoType` ''D.AppendState
1700−#endif
1701−
1702−-------------------------------------------------------------------------------
1703−-- Merging
1704−-------------------------------------------------------------------------------
1705−
1706−{-# INLINE mergeBy #-}
1707−mergeBy :: Int -> Int -> IO ()
1708−mergeBy count n =
1709− S.drain $
1710− S.mergeBy
1711− P.compare
1712− (sourceUnfoldrM count n)
1713− (sourceUnfoldrM count (n + 1))
1714−
1715−{-# INLINE mergeByM #-}
1716−mergeByM :: Int -> Int -> IO ()
1717−mergeByM count n =
1718− S.drain $
1719− S.mergeByM
1720− (\a b -> return $ P.compare a b)
1721− (sourceUnfoldrM count n)
1722− (sourceUnfoldrM count (n + 1))
1723−
1724−#ifdef INSPECTION
1725−inspect $ hasNoTypeClasses 'mergeBy
1726−inspect $ 'mergeBy `hasNoType` ''SPEC
1727−inspect $ 'mergeBy `hasNoType` ''D.Step
1728−
1729−inspect $ hasNoTypeClasses 'mergeByM
1730−inspect $ 'mergeByM `hasNoType` ''SPEC
1731−inspect $ 'mergeByM `hasNoType` ''D.Step
1732−#endif
1733−
1734−o_1_space_joining :: Int -> [Benchmark]
1735−o_1_space_joining value =
1736− [ bgroup "joining"
1737− [ benchIOSrc1 "serial (2,x/2)" (serial2 (value `div` 2))
1738− , benchIOSrc1 "append (2,x/2)" (append2 (value `div` 2))
1739− , benchIOSrc1 "serial (2,2,x/4)" (serial4 (value `div` 4))
1740− , benchIOSrc1 "append (2,2,x/4)" (append4 (value `div` 4))
1741− , benchIOSrc1 "mergeBy (2,x/2)" (mergeBy (value `div` 2))
1742− , benchIOSrc1 "mergeByM (2,x/2)" (mergeByM (value `div` 2))
1743− ]
1744− ]
1745−
1746−-------------------------------------------------------------------------------
1747−-- Concat Foldable containers
1748−-------------------------------------------------------------------------------
1749−
1750−o_1_space_concatFoldable :: Int -> [Benchmark]
1751−o_1_space_concatFoldable value =
1752− [ bgroup "concat-foldable"
1753− [ benchIOSrc serially "foldMapWith (<>) (List)"
1754− (sourceFoldMapWith value)
1755− , benchIOSrc serially "foldMapWith (<>) (Stream)"
1756− (sourceFoldMapWithStream value)
1757− , benchIOSrc serially "foldMapWithM (<>) (List)"
1758− (sourceFoldMapWithM value)
1759− , benchIOSrc serially "S.concatFoldableWith (<>) (List)"
1760− (concatFoldableWith value)
1761− , benchIOSrc serially "S.concatForFoldableWith (<>) (List)"
1762− (concatForFoldableWith value)
1763− , benchIOSrc serially "foldMapM (List)" (sourceFoldMapM value)
1764− ]
1765− ]
1766−
1767−-------------------------------------------------------------------------------
1768−-- Concat
1769−-------------------------------------------------------------------------------
1770−
1771−-- concatMap unfoldrM/unfoldrM
1772−
1773−{-# INLINE concatMap #-}
1774−concatMap :: Int -> Int -> Int -> IO ()
1775−concatMap outer inner n =
1776− S.drain $ S.concatMap
1777− (\_ -> sourceUnfoldrM inner n)
1778− (sourceUnfoldrM outer n)
1779−
1780−{-# INLINE concatMapM #-}
1781−concatMapM :: Int -> Int -> Int -> IO ()
1782−concatMapM outer inner n =
1783− S.drain $ S.concatMapM
1784− (\_ -> return $ sourceUnfoldrM inner n)
1785− (sourceUnfoldrM outer n)
1786−
1787−#ifdef INSPECTION
1788−inspect $ hasNoTypeClasses 'concatMap
1789−inspect $ 'concatMap `hasNoType` ''SPEC
1790−#endif
1791−
1792−-- concatMap unfoldr/unfoldr
1793−
1794−{-# INLINE concatMapPure #-}
1795−concatMapPure :: Int -> Int -> Int -> IO ()
1796−concatMapPure outer inner n =
1797− S.drain $ S.concatMap
1798− (\_ -> sourceUnfoldr inner n)
1799− (sourceUnfoldr outer n)
1800−
1801−#ifdef INSPECTION
1802−inspect $ hasNoTypeClasses 'concatMapPure
1803−inspect $ 'concatMapPure `hasNoType` ''SPEC
1804−#endif
1805−
1806−-- concatMap replicate/unfoldrM
1807−
1808−{-# INLINE concatMapRepl #-}
1809−concatMapRepl :: Int -> Int -> Int -> IO ()
1810−concatMapRepl outer inner n =
1811− S.drain $ S.concatMap (S.replicate inner) (sourceUnfoldrM outer n)
1812−
1813−#ifdef INSPECTION
1814−inspect $ hasNoTypeClasses 'concatMapRepl
1815−inspect $ 'concatMapRepl `hasNoType` ''SPEC
1816−#endif
1817−
1818−-- concatMapWith
1819−
1820−{-# INLINE concatMapWithSerial #-}
1821−concatMapWithSerial :: Int -> Int -> Int -> IO ()
1822−concatMapWithSerial = concatStreamsWith S.serial
1823−
1824−#ifdef INSPECTION
1825−inspect $ hasNoTypeClasses 'concatMapWithSerial
1826−inspect $ 'concatMapWithSerial `hasNoType` ''SPEC
1827−#endif
1828−
1829−{-# INLINE concatMapWithAppend #-}
1830−concatMapWithAppend :: Int -> Int -> Int -> IO ()
1831−concatMapWithAppend = concatStreamsWith Internal.append
1832−
1833−#ifdef INSPECTION
1834−inspect $ hasNoTypeClasses 'concatMapWithAppend
1835−inspect $ 'concatMapWithAppend `hasNoType` ''SPEC
1836−#endif
1837−
1838−-- concatUnfold
1839−
1840−-- concatUnfold replicate/unfoldrM
1841−
1842−{-# INLINE concatUnfoldRepl #-}
1843−concatUnfoldRepl :: Int -> Int -> Int -> IO ()
1844−concatUnfoldRepl outer inner n =
1845− S.drain
1846− $ S.concatUnfold
1847− (UF.lmap return (UF.replicateM inner))
1848− (sourceUnfoldrM outer n)
1849−
1850−#ifdef INSPECTION
1851−inspect $ hasNoTypeClasses 'concatUnfoldRepl
1852−inspect $ 'concatUnfoldRepl `hasNoType` ''D.ConcatMapUState
1853−inspect $ 'concatUnfoldRepl `hasNoType` ''SPEC
1854−#endif
1855−
1856−o_1_space_concat :: Int -> [Benchmark]
1857−o_1_space_concat value = sqrtVal `seq`
1858− [ bgroup "concat"
1859− [ benchIOSrc1 "concatMapPure (n of 1)"
1860− (concatMapPure value 1)
1861− , benchIOSrc1 "concatMapPure (sqrt n of sqrt n)"
1862− (concatMapPure sqrtVal sqrtVal)
1863− , benchIOSrc1 "concatMapPure (1 of n)"
1864− (concatMapPure 1 value)
1865−
1866− -- This is for comparison with foldMapWith
1867− , benchIOSrc serially "concatMapId (n of 1) (fromFoldable)"
1868− (S.concatMap id . sourceConcatMapId value)
1869−
1870− , benchIOSrc1 "concatMap (n of 1)"
1871− (concatMap value 1)
1872− , benchIOSrc1 "concatMap (sqrt n of sqrt n)"
1873− (concatMap sqrtVal sqrtVal)
1874− , benchIOSrc1 "concatMap (1 of n)"
1875− (concatMap 1 value)
1876−
1877− , benchIOSrc1 "concatMapM (n of 1)"
1878− (concatMapM value 1)
1879− , benchIOSrc1 "concatMapM (sqrt n of sqrt n)"
1880− (concatMapM sqrtVal sqrtVal)
1881− , benchIOSrc1 "concatMapM (1 of n)"
1882− (concatMapM 1 value)
1883−
1884− -- This is for comparison with foldMapWith
1885− , benchIOSrc serially "concatMapWithId (n of 1) (fromFoldable)"
1886− (S.concatMapWith serial id . sourceConcatMapId value)
1887−
1888− , benchIOSrc1 "concatMapWith (n of 1)"
1889− (concatMapWithSerial value 1)
1890− , benchIOSrc1 "concatMapWith (sqrt n of sqrt n)"
1891− (concatMapWithSerial sqrtVal sqrtVal)
1892− , benchIOSrc1 "concatMapWith (1 of n)"
1893− (concatMapWithSerial 1 value)
1894−
1895− -- quadratic with number of outer streams
1896− , benchIOSrc1 "concatMapWithAppend (2 of n/2)"
1897− (concatMapWithAppend 2 (value `div` 2))
1898−
1899− -- concatMap vs concatUnfold
1900− , benchIOSrc1 "concatMapRepl (sqrt n of sqrt n)"
1901− (concatMapRepl sqrtVal sqrtVal)
1902− , benchIOSrc1 "concatUnfoldRepl (sqrt n of sqrt n)"
1903− (concatUnfoldRepl sqrtVal sqrtVal)
1904− ]
1905− ]
1906−
1907− where
1908−
1909− sqrtVal = round $ sqrt (fromIntegral value :: Double)
1910−
1911−-------------------------------------------------------------------------------
1912−-- Applicative
1913−-------------------------------------------------------------------------------
1914−
1915−o_1_space_applicative :: Int -> [Benchmark]
1916−o_1_space_applicative value =
1917− [ bgroup "Applicative"
1918− [ benchIO "(*>) (sqrt n x sqrt n)" $ apDiscardFst value serially
1919− , benchIO "(<*) (sqrt n x sqrt n)" $ apDiscardSnd value serially
1920− , benchIO "(<*>) (sqrt n x sqrt n)" $ toNullAp value serially
1921− , benchIO "liftA2 (sqrt n x sqrt n)" $ apLiftA2 value serially
1922− ]
1923− ]
1924−
1925−o_n_space_applicative :: Int -> [Benchmark]
1926−o_n_space_applicative value =
1927− [ bgroup "Applicative"
1928− [ benchIOSrc serially "(*>) (n times)" $
1929− iterateSingleton ((*>) . pure) value
1930− , benchIOSrc serially "(<*) (n times)" $
1931− iterateSingleton (\x xs -> xs <* pure x) value
1932− , benchIOSrc serially "(<*>) (n times)" $
1933− iterateSingleton (\x xs -> (P.fmap (+) (pure x) <*> xs)) value
1934− , benchIOSrc serially "liftA2 (n times)" $
1935− iterateSingleton (\x xs -> AP.liftA2 (+) (pure x) xs) value
1936− ]
1937− ]
1938−
1939−-------------------------------------------------------------------------------
1940−-- Monad
1941−-------------------------------------------------------------------------------
1942−
1943−o_1_space_monad :: Int -> [Benchmark]
1944−o_1_space_monad value =
1945− [ bgroup "Monad"
1946− [ benchIO "(>>) (sqrt n x sqrt n)" $ monadThen value serially
1947− , benchIO "(>>=) (sqrt n x sqrt n)" $ toNullM value serially
1948− , benchIO "(>>=) (sqrt n x sqrt n) (filterAllOut)" $
1949− filterAllOutM value serially
1950− , benchIO "(>>=) (sqrt n x sqrt n) (filterAllIn)" $
1951− filterAllInM value serially
1952− , benchIO "(>>=) (sqrt n x sqrt n) (filterSome)" $
1953− filterSome value serially
1954− , benchIO "(>>=) (sqrt n x sqrt n) (breakAfterSome)" $
1955− breakAfterSome value serially
1956− , benchIO "(>>=) (cubert n x cubert n x cubert n)" $
1957− toNullM3 value serially
1958− ]
1959− ]
1960−
1961−-- This is a good benchmark but inefficient way to compute primes. As we see a
1962−-- new prime we keep appending a division filter for all the future numbers.
1963−{-# INLINE sieve #-}
1964−sieve :: Monad m => SerialT m Int -> SerialT m Int
1965−sieve s = do
1966− r <- lift $ S.uncons s
1967− case r of
1968− Just (prime, rest) ->
1969− prime `S.cons` sieve (S.filter (\n -> n `mod` prime /= 0) rest)
1970− Nothing -> S.nil
1971−
1972−o_n_space_monad :: Int -> [Benchmark]
1973−o_n_space_monad value =
1974− [ bgroup "Monad"
1975− [ benchIOSrc serially "(>>) (n times)" $
1976− iterateSingleton ((>>) . pure) value
1977− , benchIOSrc serially "(>>=) (n times)" $
1978− iterateSingleton (\x xs -> xs >>= \y -> return (x + y)) value
1979− , benchIO "(>>=) (sqrt n x sqrt n) (toList)" $
1980− toListM value serially
1981− , benchIO "(>>=) (sqrt n x sqrt n) (toListSome)" $
1982− toListSome value serially
1983− , benchIO "naive prime sieve (n/4)"
1984− (\n -> S.sum $ sieve $ S.enumerateFromTo 2 (value `div` 4 + n))
1985− ]
1986− ]
1987−
1988−-------------------------------------------------------------------------------
1989−-- Monad transformation (hoisting etc.)
1990−-------------------------------------------------------------------------------
1991−
1992−{-# INLINE sourceUnfoldrState #-}
1993−sourceUnfoldrState :: (S.IsStream t, S.MonadAsync m)
1994− => Int -> Int -> t (StateT Int m) Int
1995−sourceUnfoldrState value n = S.unfoldrM step n
1996− where
1997− step cnt =
1998− if cnt > n + value
1999− then return Nothing
2000− else do
2001− s <- get
2002− put (s + 1)
2003− return (Just (s, cnt + 1))
2004−
2005−{-# INLINE evalStateT #-}
2006−evalStateT :: S.MonadAsync m => Int -> Int -> SerialT m Int
2007−evalStateT value n =
2008− Internal.evalStateT (return 0) (sourceUnfoldrState value n)
2009−
2010−{-# INLINE withState #-}
2011−withState :: S.MonadAsync m => Int -> Int -> SerialT m Int
2012−withState value n =
2013− Internal.evalStateT
2014− (return (0 :: Int)) (Internal.liftInner (sourceUnfoldrM value n))
2015−
2016−o_1_space_hoisting :: Int -> [Benchmark]
2017−o_1_space_hoisting value =
2018− [ bgroup "hoisting"
2019− [ benchIOSrc serially "evalState" (evalStateT value)
2020− , benchIOSrc serially "withState" (withState value)
2021− ]
2022− ]
2023−
2024−{-# INLINE iterateStateIO #-}
2025−iterateStateIO ::
2026− (S.MonadAsync m)
2027− => Int
2028− -> StateT Int m Int
2029−iterateStateIO n = do
2030− x <- get
2031− if x > n
2032− then do
2033− put (x - 1)
2034− iterateStateIO n
2035− else return x
2036−
2037−{-# INLINE iterateStateT #-}
2038−iterateStateT :: Int -> SerialT (StateT Int IO) Int
2039−iterateStateT n = do
2040− x <- lift get
2041− if x > n
2042− then do
2043− lift $ put (x - 1)
2044− iterateStateT n
2045− else return x
2046−
2047−{-# INLINE iterateState #-}
2048−iterateState ::
2049− (S.MonadAsync m, MonadState Int m)
2050− => Int
2051− -> SerialT m Int
2052−iterateState n = do
2053− x <- get
2054− if x > n
2055− then do
2056− put (x - 1)
2057− iterateState n
2058− else return x
2059−
2060−o_n_heap_transformer :: Int -> [Benchmark]
2061−o_n_heap_transformer value =
2062− [ bgroup "transformer"
2063− [ benchIO "StateT Int IO (n times) (baseline)" $ \n ->
2064− State.evalStateT (iterateStateIO n) value
2065− , benchIO "SerialT (StateT Int IO) (n times)" $ \n ->
2066− State.evalStateT (S.drain (iterateStateT n)) value
2067− , benchIO "MonadState Int m => SerialT m Int" $ \n ->
2068− State.evalStateT (S.drain (iterateState n)) value
2069− ]
2070− ]
2071−
2072−
2073-------------------------------------------------------------------------------
27-------------------------------------------------------------------------------
2074-- Main
28-- Main
2075-------------------------------------------------------------------------------
29-------------------------------------------------------------------------------
@@ -2084,71 +38,11 @@ main = do
2084
38
2085 where
39 where
2086
40
2087− allBenchmarks size =
41+ allBenchmarks size = Prelude.concat
2088− [ bgroup (o_1_space_prefix moduleName) $ Prelude.concat
42+ [ Generation.benchmarks moduleName size
2089− [ o_1_space_generation size
43+ , Elimination.benchmarks moduleName size
2090−
44+ , Transformation1.benchmarks moduleName size
2091− -- elimination
45+ , Transformation2.benchmarks moduleName size
2092− , o_1_space_elimination_foldable size
46+ , Transformation3.benchmarks moduleName size
2093− , o_1_space_elimination_folds size
47+ , Nested.benchmarks moduleName size
2094− , o_1_space_elimination_multi_stream_pure size
2095− , o_1_space_elimination_multi_stream size
2096−
2097− -- transformation
2098− , o_1_space_functor size
2099− , o_1_space_mapping size
2100− , o_1_space_mappingX4 size
2101− , o_1_space_filtering size
2102− , o_1_space_filteringX4 size
2103− , o_1_space_inserting size
2104− , o_1_space_insertingX4 size
2105− , o_1_space_transformations_mixed size
2106− , o_1_space_transformations_mixedX4 size
2107− , o_1_space_indexing size
2108− , o_1_space_indexingX4 size
2109−
2110− -- grouping
2111− , o_1_space_grouping size
2112−
2113− -- pipes
2114− , o_1_space_pipes size
2115− , o_1_space_pipesX4 size
2116−
2117− -- multi-stream
2118− , o_1_space_joining size
2119− , o_1_space_concatFoldable size
2120− , o_1_space_concat size
2121−
2122− , o_1_space_applicative size
2123− , o_1_space_monad size
2124−
2125− -- Monad transformation
2126− , o_1_space_hoisting size
2127− ]
2128− , bgroup (o_n_stack_prefix moduleName) (o_n_stack_iterated size)
2129− , bgroup (o_n_heap_prefix moduleName) $ Prelude.concat
2130− [ o_n_heap_generation size
2131− , o_n_heap_elimination_foldl size
2132− , o_n_heap_elimination_toList size
2133− , o_n_heap_elimination_buffered size
2134−
2135− -- transformation
2136− , o_n_heap_buffering size
2137− , o_n_heap_transformer size
2138− ]
2139− , bgroup (o_n_space_prefix moduleName) $ Prelude.concat
2140− [ o_n_space_elimination_foldable size
2141− , o_n_space_elimination_toList size
2142− , o_n_space_elimination_foldr size
2143−
2144− -- transformation
2145− , o_n_space_traversable size
2146− , o_n_space_mapping size
2147− , o_n_space_grouping size
2148−
2149− -- multi-stream
2150− , o_n_space_functor size
2151− , o_n_space_applicative size
2152− , o_n_space_monad size
2153− ]
2154 ]
48 ]
benchmark/Streamly/Benchmark/Prelude/Serial/Elimination.hsnew file+683
@@ -0,0 +1,683 @@
1+-- |
2+-- Module : Serial.Elimination
3+-- Copyright : (c) 2018 Composewell Technologies
4+-- License : BSD-3-Clause
5+-- Maintainer : streamly@composewell.com
6+
7+{-# LANGUAGE CPP #-}
8+{-# LANGUAGE FlexibleContexts #-}
9+{-# LANGUAGE ScopedTypeVariables #-}
10+{-# LANGUAGE RankNTypes #-}
11+
12+#ifdef __HADDOCK_VERSION__
13+#undef INSPECTION
14+#endif
15+
16+#ifdef INSPECTION
17+{-# LANGUAGE TemplateHaskell #-}
18+{-# OPTIONS_GHC -fplugin Test.Inspection.Plugin #-}
19+#endif
20+
21+module Serial.Elimination (benchmarks) where
22+
23+import Control.DeepSeq (NFData(..))
24+import Data.Functor.Identity (Identity, runIdentity)
25+import System.Random (randomRIO)
26+
27+import qualified Data.Foldable as F
28+import qualified GHC.Exts as GHC
29+
30+#ifdef INSPECTION
31+import GHC.Types (SPEC(..))
32+import Test.Inspection
33+
34+import qualified Streamly.Internal.Data.Stream.StreamD as D
35+#endif
36+
37+import qualified Streamly.Prelude as S
38+import qualified Streamly.Internal.Data.Stream.IsStream as Internal
39+
40+import Gauge
41+import Streamly.Prelude (SerialT, IsStream, serially)
42+import Streamly.Benchmark.Common
43+import Streamly.Benchmark.Prelude
44+import Prelude hiding (length, sum, or, and, any, all, notElem, elem, (!!),
45+ lookup, repeat, minimum, maximum, product, last, mapM_, init)
46+import qualified Prelude
47+
48+{-# INLINE repeat #-}
49+repeat :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
50+repeat count = S.take count . S.repeat
51+
52+-------------------------------------------------------------------------------
53+-- Elimination
54+-------------------------------------------------------------------------------
55+
56+-------------------------------------------------------------------------------
57+-- Foldable Instance
58+-------------------------------------------------------------------------------
59+
60+{-# INLINE foldableFoldl' #-}
61+foldableFoldl' :: Int -> Int -> Int
62+foldableFoldl' value n =
63+ F.foldl' (+) 0 (sourceUnfoldr value n :: S.SerialT Identity Int)
64+
65+{-# INLINE foldableFoldrElem #-}
66+foldableFoldrElem :: Int -> Int -> Bool
67+foldableFoldrElem value n =
68+ F.foldr (\x xs -> x == value || xs)
69+ False
70+ (sourceUnfoldr value n :: S.SerialT Identity Int)
71+
72+{-# INLINE foldableSum #-}
73+foldableSum :: Int -> Int -> Int
74+foldableSum value n =
75+ Prelude.sum (sourceUnfoldr value n :: S.SerialT Identity Int)
76+
77+{-# INLINE foldableProduct #-}
78+foldableProduct :: Int -> Int -> Int
79+foldableProduct value n =
80+ Prelude.product (sourceUnfoldr value n :: S.SerialT Identity Int)
81+
82+{-# INLINE _foldableNull #-}
83+_foldableNull :: Int -> Int -> Bool
84+_foldableNull value n =
85+ Prelude.null (sourceUnfoldr value n :: S.SerialT Identity Int)
86+
87+{-# INLINE foldableElem #-}
88+foldableElem :: Int -> Int -> Bool
89+foldableElem value n =
90+ value `Prelude.elem` (sourceUnfoldr value n :: S.SerialT Identity Int)
91+
92+{-# INLINE foldableNotElem #-}
93+foldableNotElem :: Int -> Int -> Bool
94+foldableNotElem value n =
95+ value `Prelude.notElem` (sourceUnfoldr value n :: S.SerialT Identity Int)
96+
97+{-# INLINE foldableFind #-}
98+foldableFind :: Int -> Int -> Maybe Int
99+foldableFind value n =
100+ F.find (== (value + 1)) (sourceUnfoldr value n :: S.SerialT Identity Int)
101+
102+{-# INLINE foldableAll #-}
103+foldableAll :: Int -> Int -> Bool
104+foldableAll value n =
105+ Prelude.all (<= (value + 1)) (sourceUnfoldr value n :: S.SerialT Identity Int)
106+
107+{-# INLINE foldableAny #-}
108+foldableAny :: Int -> Int -> Bool
109+foldableAny value n =
110+ Prelude.any (> (value + 1)) (sourceUnfoldr value n :: S.SerialT Identity Int)
111+
112+{-# INLINE foldableAnd #-}
113+foldableAnd :: Int -> Int -> Bool
114+foldableAnd value n =
115+ Prelude.and $ S.map
116+ (<= (value + 1)) (sourceUnfoldr value n :: S.SerialT Identity Int)
117+
118+{-# INLINE foldableOr #-}
119+foldableOr :: Int -> Int -> Bool
120+foldableOr value n =
121+ Prelude.or $ S.map
122+ (> (value + 1)) (sourceUnfoldr value n :: S.SerialT Identity Int)
123+
124+{-# INLINE foldableLength #-}
125+foldableLength :: Int -> Int -> Int
126+foldableLength value n =
127+ Prelude.length (sourceUnfoldr value n :: S.SerialT Identity Int)
128+
129+{-# INLINE foldableMin #-}
130+foldableMin :: Int -> Int -> Int
131+foldableMin value n =
132+ Prelude.minimum (sourceUnfoldr value n :: S.SerialT Identity Int)
133+
134+{-# INLINE ordInstanceMin #-}
135+ordInstanceMin :: SerialT Identity Int -> SerialT Identity Int
136+ordInstanceMin src = min src src
137+
138+{-# INLINE foldableMax #-}
139+foldableMax :: Int -> Int -> Int
140+foldableMax value n =
141+ Prelude.maximum (sourceUnfoldr value n :: S.SerialT Identity Int)
142+
143+{-# INLINE foldableMinBy #-}
144+foldableMinBy :: Int -> Int -> Int
145+foldableMinBy value n =
146+ F.minimumBy compare (sourceUnfoldr value n :: S.SerialT Identity Int)
147+
148+{-# INLINE foldableListMinBy #-}
149+foldableListMinBy :: Int -> Int -> Int
150+foldableListMinBy value n = F.minimumBy compare [1..value+n]
151+
152+{-# INLINE foldableMaxBy #-}
153+foldableMaxBy :: Int -> Int -> Int
154+foldableMaxBy value n =
155+ F.maximumBy compare (sourceUnfoldr value n :: S.SerialT Identity Int)
156+
157+{-# INLINE foldableToList #-}
158+foldableToList :: Int -> Int -> [Int]
159+foldableToList value n =
160+ F.toList (sourceUnfoldr value n :: S.SerialT Identity Int)
161+
162+{-# INLINE foldableMapM_ #-}
163+foldableMapM_ :: Monad m => Int -> Int -> m ()
164+foldableMapM_ value n =
165+ F.mapM_ (\_ -> return ()) (sourceUnfoldr value n :: S.SerialT Identity Int)
166+
167+{-# INLINE foldableSequence_ #-}
168+foldableSequence_ :: Int -> Int -> IO ()
169+foldableSequence_ value n =
170+ F.sequence_ (sourceUnfoldrAction value n :: S.SerialT Identity (IO Int))
171+
172+{-# INLINE _foldableMsum #-}
173+_foldableMsum :: Int -> Int -> IO Int
174+_foldableMsum value n =
175+ F.msum (sourceUnfoldrAction value n :: S.SerialT Identity (IO Int))
176+
177+{-# INLINE showInstance #-}
178+showInstance :: SerialT Identity Int -> String
179+showInstance = show
180+
181+o_1_space_elimination_foldable :: Int -> [Benchmark]
182+o_1_space_elimination_foldable value =
183+ [ bgroup "foldable"
184+ -- Foldable instance
185+ [ bench "foldl'" $ nf (foldableFoldl' value) 1
186+ , bench "foldrElem" $ nf (foldableFoldrElem value) 1
187+ -- , bench "null" $ nf (_foldableNull value) 1
188+ , bench "elem" $ nf (foldableElem value) 1
189+ , bench "length" $ nf (foldableLength value) 1
190+ , bench "sum" $ nf (foldableSum value) 1
191+ , bench "product" $ nf (foldableProduct value) 1
192+ , bench "minimum" $ nf (foldableMin value) 1
193+ , benchPureSink value "min (ord)" ordInstanceMin
194+ , bench "maximum" $ nf (foldableMax value) 1
195+ , bench "length . toList"
196+ $ nf (Prelude.length . foldableToList value) 1
197+ , bench "notElem" $ nf (foldableNotElem value) 1
198+ , bench "find" $ nf (foldableFind value) 1
199+ , bench "all" $ nf (foldableAll value) 1
200+ , bench "any" $ nf (foldableAny value) 1
201+ , bench "and" $ nf (foldableAnd value) 1
202+ , bench "or" $ nf (foldableOr value) 1
203+
204+ -- Applicative and Traversable operations
205+ -- TBD: traverse_
206+ , benchIOSink1 "mapM_" (foldableMapM_ value)
207+ -- TBD: for_
208+ -- TBD: forM_
209+ , benchIOSink1 "sequence_" (foldableSequence_ value)
210+ -- TBD: sequenceA_
211+ -- TBD: asum
212+ -- XXX needs to be fixed, results are in ns
213+ -- , benchIOSink1 "msum" (foldableMsum value)
214+ ]
215+ ]
216+
217+o_n_space_elimination_foldable :: Int -> [Benchmark]
218+o_n_space_elimination_foldable value =
219+ -- Head recursive strict right folds.
220+ [ bgroup "foldl"
221+ -- XXX the definitions of minimumBy and maximumBy in Data.Foldable use
222+ -- foldl1 which does not work in constant memory for our
223+ -- implementation. It works in constant memory for lists but even for
224+ -- lists it takes 15x more time compared to our foldl' based
225+ -- implementation.
226+ [ bench "minimumBy" $ nf (`foldableMinBy` 1) value
227+ , bench "maximumBy" $ nf (`foldableMaxBy` 1) value
228+ , bench "minimumByList" $ nf (`foldableListMinBy` 1) value
229+ ]
230+ ]
231+
232+-------------------------------------------------------------------------------
233+-- Stream folds
234+-------------------------------------------------------------------------------
235+
236+{-# INLINE benchPureSink #-}
237+benchPureSink :: NFData b
238+ => Int -> String -> (SerialT Identity Int -> b) -> Benchmark
239+benchPureSink value name = benchPure name (sourceUnfoldr value)
240+
241+{-# INLINE benchHoistSink #-}
242+benchHoistSink
243+ :: (IsStream t, NFData b)
244+ => Int -> String -> (t Identity Int -> IO b) -> Benchmark
245+benchHoistSink value name f =
246+ bench name $ nfIO $ randomRIO (1,1) >>= f . sourceUnfoldr value
247+
248+-- XXX We should be using sourceUnfoldrM for fair comparison with IO monad, but
249+-- we can't use it as it requires MonadAsync constraint.
250+{-# INLINE benchIdentitySink #-}
251+benchIdentitySink
252+ :: (IsStream t, NFData b)
253+ => Int -> String -> (t Identity Int -> Identity b) -> Benchmark
254+benchIdentitySink value name f = bench name $ nf (f . sourceUnfoldr value) 1
255+
256+-------------------------------------------------------------------------------
257+-- Reductions
258+-------------------------------------------------------------------------------
259+
260+{-# INLINE uncons #-}
261+uncons :: Monad m => SerialT m Int -> m ()
262+uncons s = do
263+ r <- S.uncons s
264+ case r of
265+ Nothing -> return ()
266+ Just (_, t) -> uncons t
267+
268+{-# INLINE init #-}
269+init :: Monad m => SerialT m a -> m ()
270+init s = S.init s >>= Prelude.mapM_ S.drain
271+
272+{-# INLINE mapM_ #-}
273+mapM_ :: Monad m => SerialT m Int -> m ()
274+mapM_ = S.mapM_ (\_ -> return ())
275+
276+{-# INLINE foldrMElem #-}
277+foldrMElem :: Monad m => Int -> SerialT m Int -> m Bool
278+foldrMElem e =
279+ S.foldrM
280+ (\x xs ->
281+ if x == e
282+ then return True
283+ else xs)
284+ (return False)
285+
286+{-# INLINE foldrToStream #-}
287+foldrToStream :: Monad m => SerialT m Int -> m (SerialT Identity Int)
288+foldrToStream = S.foldr S.cons S.nil
289+
290+{-# INLINE foldrMBuild #-}
291+foldrMBuild :: Monad m => SerialT m Int -> m [Int]
292+foldrMBuild = S.foldrM (\x xs -> (x :) <$> xs) (return [])
293+
294+{-# INLINE foldl'Reduce #-}
295+foldl'Reduce :: Monad m => SerialT m Int -> m Int
296+foldl'Reduce = S.foldl' (+) 0
297+
298+{-# INLINE foldl1'Reduce #-}
299+foldl1'Reduce :: Monad m => SerialT m Int -> m (Maybe Int)
300+foldl1'Reduce = S.foldl1' (+)
301+
302+{-# INLINE foldlM'Reduce #-}
303+foldlM'Reduce :: Monad m => SerialT m Int -> m Int
304+foldlM'Reduce = S.foldlM' (\xs a -> return $ a + xs) (return 0)
305+
306+{-# INLINE last #-}
307+last :: Monad m => SerialT m Int -> m (Maybe Int)
308+last = S.last
309+
310+{-# INLINE _head #-}
311+_head :: Monad m => SerialT m Int -> m (Maybe Int)
312+_head = S.head
313+
314+{-# INLINE elem #-}
315+elem :: Monad m => Int -> SerialT m Int -> m Bool
316+elem value = S.elem (value + 1)
317+
318+{-# INLINE notElem #-}
319+notElem :: Monad m => Int -> SerialT m Int -> m Bool
320+notElem value = S.notElem (value + 1)
321+
322+{-# INLINE length #-}
323+length :: Monad m => SerialT m Int -> m Int
324+length = S.length
325+
326+{-# INLINE all #-}
327+all :: Monad m => Int -> SerialT m Int -> m Bool
328+all value = S.all (<= (value + 1))
329+
330+{-# INLINE any #-}
331+any :: Monad m => Int -> SerialT m Int -> m Bool
332+any value = S.any (> (value + 1))
333+
334+{-# INLINE and #-}
335+and :: Monad m => Int -> SerialT m Int -> m Bool
336+and value = S.and . S.map (<= (value + 1))
337+
338+{-# INLINE or #-}
339+or :: Monad m => Int -> SerialT m Int -> m Bool
340+or value = S.or . S.map (> (value + 1))
341+
342+{-# INLINE find #-}
343+find :: Monad m => Int -> SerialT m Int -> m (Maybe Int)
344+find value = S.find (== (value + 1))
345+
346+{-# INLINE findM #-}
347+findM :: Monad m => Int -> SerialT m Int -> m (Maybe Int)
348+findM value = S.findM (\z -> return $ z == (value + 1))
349+
350+{-# INLINE findIndex #-}
351+findIndex :: Monad m => Int -> SerialT m Int -> m (Maybe Int)
352+findIndex value = S.findIndex (== (value + 1))
353+
354+{-# INLINE elemIndex #-}
355+elemIndex :: Monad m => Int -> SerialT m Int -> m (Maybe Int)
356+elemIndex value = S.elemIndex (value + 1)
357+
358+{-# INLINE maximum #-}
359+maximum :: Monad m => SerialT m Int -> m (Maybe Int)
360+maximum = S.maximum
361+
362+{-# INLINE minimum #-}
363+minimum :: Monad m => SerialT m Int -> m (Maybe Int)
364+minimum = S.minimum
365+
366+{-# INLINE sum #-}
367+sum :: Monad m => SerialT m Int -> m Int
368+sum = S.sum
369+
370+{-# INLINE product #-}
371+product :: Monad m => SerialT m Int -> m Int
372+product = S.product
373+
374+{-# INLINE minimumBy #-}
375+minimumBy :: Monad m => SerialT m Int -> m (Maybe Int)
376+minimumBy = S.minimumBy compare
377+
378+{-# INLINE maximumBy #-}
379+maximumBy :: Monad m => SerialT m Int -> m (Maybe Int)
380+maximumBy = S.maximumBy compare
381+
382+{-# INLINE the #-}
383+the :: Monad m => SerialT m Int -> m (Maybe Int)
384+the = S.the
385+
386+{-# INLINE drainN #-}
387+drainN :: Monad m => Int -> SerialT m Int -> m ()
388+drainN = S.drainN
389+
390+{-# INLINE drainWhile #-}
391+drainWhile :: Monad m => SerialT m Int -> m ()
392+drainWhile = S.drainWhile (const True)
393+
394+{-# INLINE (!!) #-}
395+(!!) :: Monad m => Int -> SerialT m Int -> m (Maybe Int)
396+(!!) = flip (Internal.!!)
397+
398+{-# INLINE lookup #-}
399+lookup :: Monad m => Int -> SerialT m Int -> m (Maybe Int)
400+lookup val = S.lookup val . S.map (\x -> (x, x))
401+
402+o_1_space_elimination_folds :: Int -> [Benchmark]
403+o_1_space_elimination_folds value =
404+ [ bgroup "elimination"
405+ -- Basic folds
406+ [ bgroup "reduce"
407+ [ bgroup
408+ "IO"
409+ [ benchIOSink value "foldl'" foldl'Reduce
410+ , benchIOSink value "foldl1'" foldl1'Reduce
411+ , benchIOSink value "foldlM'" foldlM'Reduce
412+ ]
413+ , bgroup
414+ "Identity"
415+ [ benchIdentitySink value "foldl'" foldl'Reduce
416+ , benchIdentitySink value "foldl1'" foldl1'Reduce
417+ , benchIdentitySink value "foldlM'" foldlM'Reduce
418+ ]
419+ ]
420+ , bgroup "build"
421+ [ bgroup "IO"
422+ [ benchIOSink value "foldrMElem" (foldrMElem value)
423+ ]
424+ , bgroup "Identity"
425+ [ benchIdentitySink value "foldrMElem" (foldrMElem value)
426+ , benchIdentitySink value "foldrToStreamLength"
427+ (S.length . runIdentity . foldrToStream)
428+ , benchPureSink value "foldrMToListLength"
429+ (Prelude.length . runIdentity . foldrMBuild)
430+ ]
431+ ]
432+
433+ -- deconstruction
434+ , benchIOSink value "uncons" uncons
435+ , benchIOSink value "init" init
436+
437+ -- draining
438+ , benchIOSink value "drain" $ toNull serially
439+ , benchIOSink value "drainN" $ drainN value
440+ , benchIOSink value "drainWhile" drainWhile
441+ , benchPureSink value "drain (pure)" id
442+ , benchIOSink value "mapM_" mapM_
443+
444+ -- this is too fast, causes all benchmarks reported in ns
445+ -- , benchIOSink value "head" head
446+ , benchIOSink value "last" last
447+ , benchIOSink value "length" length
448+ , benchHoistSink value "length . generally"
449+ (length . Internal.generally)
450+ , benchIOSink value "sum" sum
451+ , benchIOSink value "product" product
452+ , benchIOSink value "maximumBy" maximumBy
453+ , benchIOSink value "maximum" maximum
454+ , benchIOSink value "minimumBy" minimumBy
455+ , benchIOSink value "minimum" minimum
456+
457+ , bench "the" $ nfIO $ randomRIO (1,1) >>= the . repeat value
458+ , benchIOSink value "find" (find value)
459+ , benchIOSink value "findM" (findM value)
460+ -- , benchIOSink value "lookupFirst" (lookup 1)
461+ , benchIOSink value "lookupNever" (lookup (value + 1))
462+ , benchIOSink value "(!!)" (value !!)
463+ , benchIOSink value "findIndex" (findIndex value)
464+ , benchIOSink value "elemIndex" (elemIndex value)
465+ -- this is too fast, causes all benchmarks reported in ns
466+ -- , benchIOSink value "null" S.null
467+ , benchIOSink value "elem" (elem value)
468+ , benchIOSink value "notElem" (notElem value)
469+ , benchIOSink value "all" (all value)
470+ , benchIOSink value "any" (any value)
471+ , benchIOSink value "and" (and value)
472+ , benchIOSink value "or" (or value)
473+
474+ , benchPureSink value "showsPrec pure streams" showInstance
475+ -- length is used to check for foldr/build fusion
476+ , benchPureSink value "length . IsList.toList" (Prelude.length . GHC.toList)
477+ ]
478+ ]
479+
480+-------------------------------------------------------------------------------
481+-- Buffered Transformations by fold
482+-------------------------------------------------------------------------------
483+
484+{-# INLINE foldl'Build #-}
485+foldl'Build :: Monad m => SerialT m Int -> m [Int]
486+foldl'Build = S.foldl' (flip (:)) []
487+
488+{-# INLINE foldlM'Build #-}
489+foldlM'Build :: Monad m => SerialT m Int -> m [Int]
490+foldlM'Build = S.foldlM' (\xs x -> return $ x : xs) (return [])
491+
492+o_n_heap_elimination_foldl :: Int -> [Benchmark]
493+o_n_heap_elimination_foldl value =
494+ [ bgroup "foldl"
495+ -- Left folds for building a structure are inherently non-streaming
496+ -- as the structure cannot be lazily consumed until fully built.
497+ [ benchIOSink value "foldl'/build/IO" foldl'Build
498+ , benchIdentitySink value "foldl'/build/Identity" foldl'Build
499+ , benchIOSink value "foldlM'/build/IO" foldlM'Build
500+ , benchIdentitySink value "foldlM'/build/Identity" foldlM'Build
501+ ]
502+ ]
503+
504+-- For comparisons
505+{-# INLINE showInstanceList #-}
506+showInstanceList :: [Int] -> String
507+showInstanceList = show
508+
509+o_n_heap_elimination_buffered :: Int -> [Benchmark]
510+o_n_heap_elimination_buffered value =
511+ [ bgroup "buffered"
512+ -- Buffers the output of show/read.
513+ -- XXX can the outputs be streaming? Can we have special read/show
514+ -- style type classes, readM/showM supporting streaming effects?
515+ [ bench "showPrec Haskell lists" $ nf showInstanceList (mkList value)
516+ ]
517+ ]
518+
519+{-# INLINE foldrMReduce #-}
520+foldrMReduce :: Monad m => SerialT m Int -> m Int
521+foldrMReduce = S.foldrM (\x xs -> (x +) <$> xs) (return 0)
522+
523+o_n_space_elimination_foldr :: Int -> [Benchmark]
524+o_n_space_elimination_foldr value =
525+ -- Head recursive strict right folds.
526+ [ bgroup "foldr"
527+ -- accumulation due to strictness of IO monad
528+ [ benchIOSink value "foldrM/build/IO (toList)" foldrMBuild
529+ -- Right folds for reducing are inherently non-streaming as the
530+ -- expression needs to be fully built before it can be reduced.
531+ , benchIdentitySink value "foldrM/reduce/Identity (sum)" foldrMReduce
532+ , benchIOSink value "foldrM/reduce/IO (sum)" foldrMReduce
533+ ]
534+ ]
535+
536+o_n_heap_elimination_toList :: Int -> [Benchmark]
537+o_n_heap_elimination_toList value =
538+ [ bgroup "toList"
539+ -- Converting the stream to a list or pure stream in a strict monad
540+ [ benchIOSink value "toListRev" Internal.toListRev
541+ , benchIOSink value "toPureRev" Internal.toPureRev
542+ ]
543+ ]
544+
545+o_n_space_elimination_toList :: Int -> [Benchmark]
546+o_n_space_elimination_toList value =
547+ [ bgroup "toList"
548+ -- Converting the stream to a list or pure stream in a strict monad
549+ [ benchIOSink value "toList" S.toList
550+ , benchIOSink value "toPure" Internal.toPure
551+ ]
552+ ]
553+
554+-------------------------------------------------------------------------------
555+-- Multi-stream folds
556+-------------------------------------------------------------------------------
557+
558+-------------------------------------------------------------------------------
559+-- Multi-stream pure
560+-------------------------------------------------------------------------------
561+
562+{-# INLINE eqBy' #-}
563+eqBy' :: (Monad m, Eq a) => SerialT m a -> m Bool
564+eqBy' src = S.eqBy (==) src src
565+
566+{-# INLINE eqByPure #-}
567+eqByPure :: Int -> Int -> Identity Bool
568+eqByPure value n = eqBy' (sourceUnfoldr value n)
569+
570+#ifdef INSPECTION
571+inspect $ hasNoTypeClasses 'eqByPure
572+inspect $ 'eqByPure `hasNoType` ''SPEC
573+inspect $ 'eqByPure `hasNoType` ''D.Step
574+#endif
575+
576+{-# INLINE eqInstance #-}
577+eqInstance :: SerialT Identity Int -> Bool
578+eqInstance src = src == src
579+
580+{-# INLINE eqInstanceNotEq #-}
581+eqInstanceNotEq :: SerialT Identity Int -> Bool
582+eqInstanceNotEq src = src /= src
583+
584+{-# INLINE cmpBy' #-}
585+cmpBy' :: (Monad m, Ord a) => SerialT m a -> m Ordering
586+cmpBy' src = S.cmpBy compare src src
587+
588+{-# INLINE cmpByPure #-}
589+cmpByPure :: Int -> Int -> Identity Ordering
590+cmpByPure value n = cmpBy' (sourceUnfoldr value n)
591+
592+#ifdef INSPECTION
593+inspect $ hasNoTypeClasses 'cmpByPure
594+inspect $ 'cmpByPure `hasNoType` ''SPEC
595+inspect $ 'cmpByPure `hasNoType` ''D.Step
596+#endif
597+
598+{-# INLINE ordInstance #-}
599+ordInstance :: SerialT Identity Int -> Bool
600+ordInstance src = src < src
601+
602+o_1_space_elimination_multi_stream_pure :: Int -> [Benchmark]
603+o_1_space_elimination_multi_stream_pure value =
604+ [ bgroup "multi-stream-pure"
605+ [ benchPureSink1 "eqBy" (eqByPure value)
606+ , benchPureSink value "==" eqInstance
607+ , benchPureSink value "/=" eqInstanceNotEq
608+ , benchPureSink1 "cmpBy" (cmpByPure value)
609+ , benchPureSink value "<" ordInstance
610+ ]
611+ ]
612+
613+{-# INLINE isPrefixOf #-}
614+isPrefixOf :: Monad m => SerialT m Int -> m Bool
615+isPrefixOf src = S.isPrefixOf src src
616+
617+{-# INLINE isSubsequenceOf #-}
618+isSubsequenceOf :: Monad m => SerialT m Int -> m Bool
619+isSubsequenceOf src = S.isSubsequenceOf src src
620+
621+{-# INLINE stripPrefix #-}
622+stripPrefix :: Monad m => SerialT m Int -> m ()
623+stripPrefix src = do
624+ _ <- S.stripPrefix src src
625+ return ()
626+
627+{-# INLINE eqBy #-}
628+eqBy :: Int -> Int -> IO Bool
629+eqBy value n = eqBy' (sourceUnfoldrM value n)
630+
631+#ifdef INSPECTION
632+inspect $ hasNoTypeClasses 'eqBy
633+inspect $ 'eqBy `hasNoType` ''SPEC
634+inspect $ 'eqBy `hasNoType` ''D.Step
635+#endif
636+
637+{-# INLINE cmpBy #-}
638+cmpBy :: Int -> Int -> IO Ordering
639+cmpBy value n = cmpBy' (sourceUnfoldrM value n)
640+
641+#ifdef INSPECTION
642+inspect $ hasNoTypeClasses 'cmpBy
643+inspect $ 'cmpBy `hasNoType` ''SPEC
644+inspect $ 'cmpBy `hasNoType` ''D.Step
645+#endif
646+
647+o_1_space_elimination_multi_stream :: Int -> [Benchmark]
648+o_1_space_elimination_multi_stream value =
649+ [ bgroup "multi-stream"
650+ [ benchIOSink1 "eqBy" (eqBy value)
651+ , benchIOSink1 "cmpBy" (cmpBy value)
652+ , benchIOSink value "isPrefixOf" isPrefixOf
653+ , benchIOSink value "isSubsequenceOf" isSubsequenceOf
654+ , benchIOSink value "stripPrefix" stripPrefix
655+ ]
656+ ]
657+
658+-------------------------------------------------------------------------------
659+-- Main
660+-------------------------------------------------------------------------------
661+
662+-- In addition to gauge options, the number of elements in the stream can be
663+-- passed using the --stream-size option.
664+--
665+benchmarks :: String -> Int -> [Benchmark]
666+benchmarks moduleName size =
667+ [ bgroup (o_1_space_prefix moduleName) $ concat
668+ [ o_1_space_elimination_foldable size
669+ , o_1_space_elimination_folds size
670+ , o_1_space_elimination_multi_stream_pure size
671+ , o_1_space_elimination_multi_stream size
672+ ]
673+ , bgroup (o_n_heap_prefix moduleName) $ concat
674+ [ o_n_heap_elimination_foldl size
675+ , o_n_heap_elimination_toList size
676+ , o_n_heap_elimination_buffered size
677+ ]
678+ , bgroup (o_n_space_prefix moduleName) $ concat
679+ [ o_n_space_elimination_foldable size
680+ , o_n_space_elimination_toList size
681+ , o_n_space_elimination_foldr size
682+ ]
683+ ]
benchmark/Streamly/Benchmark/Prelude/Serial/Generation.hsnew file+179
@@ -0,0 +1,179 @@
1+-- |
2+-- Module : Serial.Generation
3+-- Copyright : (c) 2018 Composewell Technologies
4+-- License : BSD-3-Clause
5+-- Maintainer : streamly@composewell.com
6+
7+{-# LANGUAGE CPP #-}
8+{-# LANGUAGE FlexibleContexts #-}
9+{-# LANGUAGE ScopedTypeVariables #-}
10+{-# LANGUAGE RankNTypes #-}
11+
12+module Serial.Generation (benchmarks) where
13+
14+import Data.Functor.Identity (Identity)
15+
16+import qualified Prelude
17+import qualified GHC.Exts as GHC
18+
19+import qualified Streamly.Prelude as S
20+
21+import Gauge
22+import Streamly.Prelude (SerialT, serially, MonadAsync)
23+import Streamly.Benchmark.Common
24+import Streamly.Benchmark.Prelude
25+import Prelude hiding (repeat, replicate, iterate)
26+
27+-------------------------------------------------------------------------------
28+-- Generation
29+-------------------------------------------------------------------------------
30+
31+-------------------------------------------------------------------------------
32+-- fromList
33+-------------------------------------------------------------------------------
34+
35+{-# INLINE sourceIsList #-}
36+sourceIsList :: Int -> Int -> SerialT Identity Int
37+sourceIsList value n = GHC.fromList [n..n+value]
38+
39+{-# INLINE sourceIsString #-}
40+sourceIsString :: Int -> Int -> SerialT Identity Char
41+sourceIsString value n = GHC.fromString (Prelude.replicate (n + value) 'a')
42+
43+{-# INLINE readInstance #-}
44+readInstance :: String -> SerialT Identity Int
45+readInstance str =
46+ let r = reads str
47+ in case r of
48+ [(x,"")] -> x
49+ _ -> error "readInstance: no parse"
50+
51+-- For comparisons
52+{-# INLINE readInstanceList #-}
53+readInstanceList :: String -> [Int]
54+readInstanceList str =
55+ let r = reads str
56+ in case r of
57+ [(x,"")] -> x
58+ _ -> error "readInstance: no parse"
59+
60+{-# INLINE repeat #-}
61+repeat :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
62+repeat count = S.take count . S.repeat
63+
64+{-# INLINE repeatM #-}
65+repeatM :: (MonadAsync m, S.IsStream t) => Int -> Int -> t m Int
66+repeatM count = S.take count . S.repeatM . return
67+
68+{-# INLINE replicate #-}
69+replicate :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
70+replicate = S.replicate
71+
72+{-# INLINE replicateM #-}
73+replicateM :: (MonadAsync m, S.IsStream t) => Int -> Int -> t m Int
74+replicateM count = S.replicateM count . return
75+
76+{-# INLINE enumerateFrom #-}
77+enumerateFrom :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
78+enumerateFrom count = S.take count . S.enumerateFrom
79+
80+{-# INLINE enumerateFromTo #-}
81+enumerateFromTo :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
82+enumerateFromTo = sourceIntFromTo
83+
84+{-# INLINE enumerateFromThen #-}
85+enumerateFromThen :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
86+enumerateFromThen count inp = S.take count $ S.enumerateFromThen inp (inp + 1)
87+
88+{-# INLINE enumerateFromThenTo #-}
89+enumerateFromThenTo :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
90+enumerateFromThenTo = sourceIntFromThenTo
91+
92+-- n ~ 1
93+{-# INLINE enumerate #-}
94+enumerate :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
95+enumerate count n = S.take (count + n) S.enumerate
96+
97+-- n ~ 1
98+{-# INLINE enumerateTo #-}
99+enumerateTo :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
100+enumerateTo count n = S.enumerateTo (minBound + count + n)
101+
102+{-# INLINE iterate #-}
103+iterate :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
104+iterate count = S.take count . S.iterate (+1)
105+
106+{-# INLINE iterateM #-}
107+iterateM :: (MonadAsync m, S.IsStream t) => Int -> Int -> t m Int
108+iterateM count = S.take count . S.iterateM (return . (+1)) . return
109+
110+{-# INLINE fromIndices #-}
111+fromIndices :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
112+fromIndices value n = S.take value $ S.fromIndices (+ n)
113+
114+{-# INLINE fromIndicesM #-}
115+fromIndicesM :: (MonadAsync m, S.IsStream t) => Int -> Int -> t m Int
116+fromIndicesM value n = S.take value $ S.fromIndicesM (return <$> (+ n))
117+
118+o_1_space_generation :: Int -> [Benchmark]
119+o_1_space_generation value =
120+ [ bgroup "generation"
121+ [ benchIOSrc serially "unfoldr" (sourceUnfoldr value)
122+ , benchIOSrc serially "unfoldrM" (sourceUnfoldrM value)
123+ , benchIOSrc serially "repeat" (repeat value)
124+ , benchIOSrc serially "repeatM" (repeatM value)
125+ , benchIOSrc serially "replicate" (replicate value)
126+ , benchIOSrc serially "replicateM" (replicateM value)
127+ , benchIOSrc serially "iterate" (iterate value)
128+ , benchIOSrc serially "iterateM" (iterateM value)
129+ , benchIOSrc serially "fromIndices" (fromIndices value)
130+ , benchIOSrc serially "fromIndicesM" (fromIndicesM value)
131+ , benchIOSrc serially "intFromTo" (sourceIntFromTo value)
132+ , benchIOSrc serially "intFromThenTo" (sourceIntFromThenTo value)
133+ , benchIOSrc serially "integerFromStep" (sourceIntegerFromStep value)
134+ , benchIOSrc serially "fracFromThenTo" (sourceFracFromThenTo value)
135+ , benchIOSrc serially "fracFromTo" (sourceFracFromTo value)
136+ , benchIOSrc serially "fromList" (sourceFromList value)
137+ , benchPureSrc "IsList.fromList" (sourceIsList value)
138+ , benchPureSrc "IsString.fromString" (sourceIsString value)
139+ , benchIOSrc serially "fromListM" (sourceFromListM value)
140+ , benchIOSrc serially "enumerateFrom" (enumerateFrom value)
141+ , benchIOSrc serially "enumerateFromTo" (enumerateFromTo value)
142+ , benchIOSrc serially "enumerateFromThen" (enumerateFromThen value)
143+ , benchIOSrc serially "enumerateFromThenTo" (enumerateFromThenTo value)
144+ , benchIOSrc serially "enumerate" (enumerate value)
145+ , benchIOSrc serially "enumerateTo" (enumerateTo value)
146+
147+ -- These essentially test cons and consM
148+ , benchIOSrc serially "fromFoldable" (sourceFromFoldable value)
149+ , benchIOSrc serially "fromFoldableM" (sourceFromFoldableM value)
150+
151+ , benchIOSrc serially "absTimes" $ absTimes value
152+ ]
153+ ]
154+
155+o_n_heap_generation :: Int -> [Benchmark]
156+o_n_heap_generation value =
157+ [ bgroup "buffered"
158+ -- Buffers the output of show/read.
159+ -- XXX can the outputs be streaming? Can we have special read/show
160+ -- style type classes, readM/showM supporting streaming effects?
161+ [ bench "readsPrec pure streams" $
162+ nf readInstance (mkString value)
163+ , bench "readsPrec Haskell lists" $
164+ nf readInstanceList (mkListString value)
165+ ]
166+ ]
167+
168+-------------------------------------------------------------------------------
169+-- Main
170+-------------------------------------------------------------------------------
171+
172+-- In addition to gauge options, the number of elements in the stream can be
173+-- passed using the --stream-size option.
174+--
175+benchmarks :: String -> Int -> [Benchmark]
176+benchmarks moduleName size =
177+ [ bgroup (o_1_space_prefix moduleName) (o_1_space_generation size)
178+ , bgroup (o_n_heap_prefix moduleName) (o_n_heap_generation size)
179+ ]
benchmark/Streamly/Benchmark/Prelude/Serial/Nested.hsnew file+440
@@ -0,0 +1,440 @@
1+-- |
2+-- Module : Serial.Nested
3+-- Copyright : (c) 2018 Composewell Technologies
4+-- License : BSD-3-Clause
5+-- Maintainer : streamly@composewell.com
6+
7+{-# LANGUAGE CPP #-}
8+{-# LANGUAGE FlexibleContexts #-}
9+{-# LANGUAGE ScopedTypeVariables #-}
10+{-# LANGUAGE RankNTypes #-}
11+
12+#ifdef __HADDOCK_VERSION__
13+#undef INSPECTION
14+#endif
15+
16+#ifdef INSPECTION
17+{-# LANGUAGE TemplateHaskell #-}
18+{-# OPTIONS_GHC -fplugin Test.Inspection.Plugin #-}
19+#endif
20+
21+module Serial.Nested (benchmarks) where
22+
23+import Control.Monad.Trans.Class (lift)
24+
25+import qualified Control.Applicative as AP
26+
27+#ifdef INSPECTION
28+import GHC.Types (SPEC(..))
29+import Test.Inspection
30+
31+import qualified Streamly.Internal.Data.Stream.StreamD as D
32+#endif
33+
34+import qualified Streamly.Prelude as S
35+import qualified Streamly.Internal.Data.Stream.IsStream as Internal
36+import qualified Streamly.Internal.Data.Unfold as UF
37+
38+import Gauge
39+import Streamly.Prelude (SerialT, serially, serial)
40+import Streamly.Benchmark.Common
41+import Streamly.Benchmark.Prelude
42+import Prelude hiding (concatMap)
43+
44+-------------------------------------------------------------------------------
45+-- Iteration/looping utilities
46+-------------------------------------------------------------------------------
47+
48+{-# INLINE iterateN #-}
49+iterateN :: (Int -> a -> a) -> a -> Int -> a
50+iterateN g initial count = f count initial
51+
52+ where
53+
54+ f (0 :: Int) x = x
55+ f i x = f (i - 1) (g i x)
56+
57+-- Iterate a transformation over a singleton stream
58+{-# INLINE iterateSingleton #-}
59+iterateSingleton :: S.MonadAsync m
60+ => (Int -> SerialT m Int -> SerialT m Int)
61+ -> Int
62+ -> Int
63+ -> SerialT m Int
64+iterateSingleton g count n = iterateN g (return n) count
65+
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 #-}
72+_iterateSingleton ::
73+ S.MonadAsync m
74+ => (Int -> SerialT m Int -> SerialT m Int)
75+ -> Int
76+ -> Int
77+ -> SerialT m Int
78+_iterateSingleton g value n = S.foldrM g (return n) $ sourceIntFromTo value n
79+
80+-------------------------------------------------------------------------------
81+-- Multi-Stream
82+-------------------------------------------------------------------------------
83+
84+-------------------------------------------------------------------------------
85+-- Appending
86+-------------------------------------------------------------------------------
87+
88+{-# INLINE serial2 #-}
89+serial2 :: Int -> Int -> IO ()
90+serial2 count n =
91+ S.drain $
92+ S.serial (sourceUnfoldrM count n) (sourceUnfoldrM count (n + 1))
93+
94+{-# INLINE serial4 #-}
95+serial4 :: Int -> Int -> IO ()
96+serial4 count n =
97+ S.drain $
98+ S.serial
99+ (S.serial (sourceUnfoldrM count n) (sourceUnfoldrM count (n + 1)))
100+ (S.serial
101+ (sourceUnfoldrM count (n + 2))
102+ (sourceUnfoldrM count (n + 3)))
103+
104+{-# INLINE append2 #-}
105+append2 :: Int -> Int -> IO ()
106+append2 count n =
107+ S.drain $
108+ Internal.append (sourceUnfoldrM count n) (sourceUnfoldrM count (n + 1))
109+
110+{-# INLINE append4 #-}
111+append4 :: Int -> Int -> IO ()
112+append4 count n =
113+ S.drain $
114+ Internal.append
115+ (Internal.append
116+ (sourceUnfoldrM count n)
117+ (sourceUnfoldrM count (n + 1)))
118+ (Internal.append
119+ (sourceUnfoldrM count (n + 2))
120+ (sourceUnfoldrM count (n + 3)))
121+
122+#ifdef INSPECTION
123+inspect $ hasNoTypeClasses 'append2
124+inspect $ 'append2 `hasNoType` ''SPEC
125+inspect $ 'append2 `hasNoType` ''D.AppendState
126+#endif
127+
128+-------------------------------------------------------------------------------
129+-- Merging
130+-------------------------------------------------------------------------------
131+
132+{-# INLINE mergeBy #-}
133+mergeBy :: Int -> Int -> IO ()
134+mergeBy count n =
135+ S.drain $
136+ S.mergeBy
137+ compare
138+ (sourceUnfoldrM count n)
139+ (sourceUnfoldrM count (n + 1))
140+
141+{-# INLINE mergeByM #-}
142+mergeByM :: Int -> Int -> IO ()
143+mergeByM count n =
144+ S.drain $
145+ S.mergeByM
146+ (\a b -> return $ compare a b)
147+ (sourceUnfoldrM count n)
148+ (sourceUnfoldrM count (n + 1))
149+
150+#ifdef INSPECTION
151+inspect $ hasNoTypeClasses 'mergeBy
152+inspect $ 'mergeBy `hasNoType` ''SPEC
153+inspect $ 'mergeBy `hasNoType` ''D.Step
154+
155+inspect $ hasNoTypeClasses 'mergeByM
156+inspect $ 'mergeByM `hasNoType` ''SPEC
157+inspect $ 'mergeByM `hasNoType` ''D.Step
158+#endif
159+
160+o_1_space_joining :: Int -> [Benchmark]
161+o_1_space_joining value =
162+ [ bgroup "joining"
163+ [ benchIOSrc1 "serial (2,x/2)" (serial2 (value `div` 2))
164+ , benchIOSrc1 "append (2,x/2)" (append2 (value `div` 2))
165+ , benchIOSrc1 "serial (2,2,x/4)" (serial4 (value `div` 4))
166+ , benchIOSrc1 "append (2,2,x/4)" (append4 (value `div` 4))
167+ , benchIOSrc1 "mergeBy (2,x/2)" (mergeBy (value `div` 2))
168+ , benchIOSrc1 "mergeByM (2,x/2)" (mergeByM (value `div` 2))
169+ ]
170+ ]
171+
172+-------------------------------------------------------------------------------
173+-- Concat Foldable containers
174+-------------------------------------------------------------------------------
175+
176+o_1_space_concatFoldable :: Int -> [Benchmark]
177+o_1_space_concatFoldable value =
178+ [ bgroup "concat-foldable"
179+ [ benchIOSrc serially "foldMapWith (<>) (List)"
180+ (sourceFoldMapWith value)
181+ , benchIOSrc serially "foldMapWith (<>) (Stream)"
182+ (sourceFoldMapWithStream value)
183+ , benchIOSrc serially "foldMapWithM (<>) (List)"
184+ (sourceFoldMapWithM value)
185+ , benchIOSrc serially "S.concatFoldableWith (<>) (List)"
186+ (concatFoldableWith value)
187+ , benchIOSrc serially "S.concatForFoldableWith (<>) (List)"
188+ (concatForFoldableWith value)
189+ , benchIOSrc serially "foldMapM (List)" (sourceFoldMapM value)
190+ ]
191+ ]
192+
193+-------------------------------------------------------------------------------
194+-- Concat
195+-------------------------------------------------------------------------------
196+
197+-- concatMap unfoldrM/unfoldrM
198+
199+{-# INLINE concatMap #-}
200+concatMap :: Int -> Int -> Int -> IO ()
201+concatMap outer inner n =
202+ S.drain $ S.concatMap
203+ (\_ -> sourceUnfoldrM inner n)
204+ (sourceUnfoldrM outer n)
205+
206+{-# INLINE concatMapM #-}
207+concatMapM :: Int -> Int -> Int -> IO ()
208+concatMapM outer inner n =
209+ S.drain $ S.concatMapM
210+ (\_ -> return $ sourceUnfoldrM inner n)
211+ (sourceUnfoldrM outer n)
212+
213+#ifdef INSPECTION
214+inspect $ hasNoTypeClasses 'concatMap
215+inspect $ 'concatMap `hasNoType` ''SPEC
216+#endif
217+
218+-- concatMap unfoldr/unfoldr
219+
220+{-# INLINE concatMapPure #-}
221+concatMapPure :: Int -> Int -> Int -> IO ()
222+concatMapPure outer inner n =
223+ S.drain $ S.concatMap
224+ (\_ -> sourceUnfoldr inner n)
225+ (sourceUnfoldr outer n)
226+
227+#ifdef INSPECTION
228+inspect $ hasNoTypeClasses 'concatMapPure
229+inspect $ 'concatMapPure `hasNoType` ''SPEC
230+#endif
231+
232+-- concatMap replicate/unfoldrM
233+
234+{-# INLINE concatMapRepl #-}
235+concatMapRepl :: Int -> Int -> Int -> IO ()
236+concatMapRepl outer inner n =
237+ S.drain $ S.concatMap (S.replicate inner) (sourceUnfoldrM outer n)
238+
239+#ifdef INSPECTION
240+inspect $ hasNoTypeClasses 'concatMapRepl
241+inspect $ 'concatMapRepl `hasNoType` ''SPEC
242+#endif
243+
244+-- concatMapWith
245+
246+{-# INLINE concatMapWithSerial #-}
247+concatMapWithSerial :: Int -> Int -> Int -> IO ()
248+concatMapWithSerial = concatStreamsWith S.serial
249+
250+#ifdef INSPECTION
251+inspect $ hasNoTypeClasses 'concatMapWithSerial
252+inspect $ 'concatMapWithSerial `hasNoType` ''SPEC
253+#endif
254+
255+{-# INLINE concatMapWithAppend #-}
256+concatMapWithAppend :: Int -> Int -> Int -> IO ()
257+concatMapWithAppend = concatStreamsWith Internal.append
258+
259+#ifdef INSPECTION
260+inspect $ hasNoTypeClasses 'concatMapWithAppend
261+inspect $ 'concatMapWithAppend `hasNoType` ''SPEC
262+#endif
263+
264+-- concatUnfold
265+
266+-- concatUnfold replicate/unfoldrM
267+
268+{-# INLINE concatUnfoldRepl #-}
269+concatUnfoldRepl :: Int -> Int -> Int -> IO ()
270+concatUnfoldRepl outer inner n =
271+ S.drain
272+ $ S.concatUnfold
273+ (UF.lmap return (UF.replicateM inner))
274+ (sourceUnfoldrM outer n)
275+
276+#ifdef INSPECTION
277+inspect $ hasNoTypeClasses 'concatUnfoldRepl
278+inspect $ 'concatUnfoldRepl `hasNoType` ''D.ConcatMapUState
279+inspect $ 'concatUnfoldRepl `hasNoType` ''SPEC
280+#endif
281+
282+o_1_space_concat :: Int -> [Benchmark]
283+o_1_space_concat value = sqrtVal `seq`
284+ [ bgroup "concat"
285+ [ benchIOSrc1 "concatMapPure (n of 1)"
286+ (concatMapPure value 1)
287+ , benchIOSrc1 "concatMapPure (sqrt n of sqrt n)"
288+ (concatMapPure sqrtVal sqrtVal)
289+ , benchIOSrc1 "concatMapPure (1 of n)"
290+ (concatMapPure 1 value)
291+
292+ -- This is for comparison with foldMapWith
293+ , benchIOSrc serially "concatMapId (n of 1) (fromFoldable)"
294+ (S.concatMap id . sourceConcatMapId value)
295+
296+ , benchIOSrc1 "concatMap (n of 1)"
297+ (concatMap value 1)
298+ , benchIOSrc1 "concatMap (sqrt n of sqrt n)"
299+ (concatMap sqrtVal sqrtVal)
300+ , benchIOSrc1 "concatMap (1 of n)"
301+ (concatMap 1 value)
302+
303+ , benchIOSrc1 "concatMapM (n of 1)"
304+ (concatMapM value 1)
305+ , benchIOSrc1 "concatMapM (sqrt n of sqrt n)"
306+ (concatMapM sqrtVal sqrtVal)
307+ , benchIOSrc1 "concatMapM (1 of n)"
308+ (concatMapM 1 value)
309+
310+ -- This is for comparison with foldMapWith
311+ , benchIOSrc serially "concatMapWithId (n of 1) (fromFoldable)"
312+ (S.concatMapWith serial id . sourceConcatMapId value)
313+
314+ , benchIOSrc1 "concatMapWith (n of 1)"
315+ (concatMapWithSerial value 1)
316+ , benchIOSrc1 "concatMapWith (sqrt n of sqrt n)"
317+ (concatMapWithSerial sqrtVal sqrtVal)
318+ , benchIOSrc1 "concatMapWith (1 of n)"
319+ (concatMapWithSerial 1 value)
320+
321+ -- quadratic with number of outer streams
322+ , benchIOSrc1 "concatMapWithAppend (2 of n/2)"
323+ (concatMapWithAppend 2 (value `div` 2))
324+
325+ -- concatMap vs concatUnfold
326+ , benchIOSrc1 "concatMapRepl (sqrt n of sqrt n)"
327+ (concatMapRepl sqrtVal sqrtVal)
328+ , benchIOSrc1 "concatUnfoldRepl (sqrt n of sqrt n)"
329+ (concatUnfoldRepl sqrtVal sqrtVal)
330+ ]
331+ ]
332+
333+ where
334+
335+ sqrtVal = round $ sqrt (fromIntegral value :: Double)
336+
337+-------------------------------------------------------------------------------
338+-- Applicative
339+-------------------------------------------------------------------------------
340+
341+o_1_space_applicative :: Int -> [Benchmark]
342+o_1_space_applicative value =
343+ [ bgroup "Applicative"
344+ [ benchIO "(*>) (sqrt n x sqrt n)" $ apDiscardFst value serially
345+ , benchIO "(<*) (sqrt n x sqrt n)" $ apDiscardSnd value serially
346+ , benchIO "(<*>) (sqrt n x sqrt n)" $ toNullAp value serially
347+ , benchIO "liftA2 (sqrt n x sqrt n)" $ apLiftA2 value serially
348+ ]
349+ ]
350+
351+o_n_space_applicative :: Int -> [Benchmark]
352+o_n_space_applicative value =
353+ [ bgroup "Applicative"
354+ [ benchIOSrc serially "(*>) (n times)" $
355+ iterateSingleton ((*>) . pure) value
356+ , benchIOSrc serially "(<*) (n times)" $
357+ iterateSingleton (\x xs -> xs <* pure x) value
358+ , benchIOSrc serially "(<*>) (n times)" $
359+ iterateSingleton (\x xs -> pure (+ x) <*> xs) value
360+ , benchIOSrc serially "liftA2 (n times)" $
361+ iterateSingleton (AP.liftA2 (+) . pure) value
362+ ]
363+ ]
364+
365+-------------------------------------------------------------------------------
366+-- Monad
367+-------------------------------------------------------------------------------
368+
369+o_1_space_monad :: Int -> [Benchmark]
370+o_1_space_monad value =
371+ [ bgroup "Monad"
372+ [ benchIO "(>>) (sqrt n x sqrt n)" $ monadThen value serially
373+ , benchIO "(>>=) (sqrt n x sqrt n)" $ toNullM value serially
374+ , benchIO "(>>=) (sqrt n x sqrt n) (filterAllOut)" $
375+ filterAllOutM value serially
376+ , benchIO "(>>=) (sqrt n x sqrt n) (filterAllIn)" $
377+ filterAllInM value serially
378+ , benchIO "(>>=) (sqrt n x sqrt n) (filterSome)" $
379+ filterSome value serially
380+ , benchIO "(>>=) (sqrt n x sqrt n) (breakAfterSome)" $
381+ breakAfterSome value serially
382+ , benchIO "(>>=) (cubert n x cubert n x cubert n)" $
383+ toNullM3 value serially
384+ ]
385+ ]
386+
387+-- This is a good benchmark but inefficient way to compute primes. As we see a
388+-- new prime we keep appending a division filter for all the future numbers.
389+{-# INLINE sieve #-}
390+sieve :: Monad m => SerialT m Int -> SerialT m Int
391+sieve s = do
392+ r <- lift $ S.uncons s
393+ case r of
394+ Just (prime, rest) ->
395+ prime `S.cons` sieve (S.filter (\n -> n `mod` prime /= 0) rest)
396+ Nothing -> S.nil
397+
398+o_n_space_monad :: Int -> [Benchmark]
399+o_n_space_monad value =
400+ [ bgroup "Monad"
401+ [ benchIOSrc serially "(>>) (n times)" $
402+ iterateSingleton ((>>) . pure) value
403+ , benchIOSrc serially "(>>=) (n times)" $
404+ iterateSingleton (\x xs -> xs >>= \y -> return (x + y)) value
405+ , benchIO "(>>=) (sqrt n x sqrt n) (toList)" $
406+ toListM value serially
407+ , benchIO "(>>=) (sqrt n x sqrt n) (toListSome)" $
408+ toListSome value serially
409+ , benchIO "naive prime sieve (n/4)"
410+ (\n -> S.sum $ sieve $ S.enumerateFromTo 2 (value `div` 4 + n))
411+ ]
412+ ]
413+
414+-------------------------------------------------------------------------------
415+-- Main
416+-------------------------------------------------------------------------------
417+
418+-- In addition to gauge options, the number of elements in the stream can be
419+-- passed using the --stream-size option.
420+--
421+benchmarks :: String -> Int -> [Benchmark]
422+benchmarks moduleName size =
423+ [ bgroup (o_1_space_prefix moduleName) $ Prelude.concat
424+ [
425+ -- multi-stream
426+ o_1_space_joining size
427+ , o_1_space_concatFoldable size
428+ , o_1_space_concat size
429+
430+ , o_1_space_applicative size
431+ , o_1_space_monad size
432+
433+ ]
434+ , bgroup (o_n_space_prefix moduleName) $ Prelude.concat
435+ [
436+ -- multi-stream
437+ o_n_space_applicative size
438+ , o_n_space_monad size
439+ ]
440+ ]
benchmark/Streamly/Benchmark/Prelude/Serial/Transformation1.hsnew file+554
@@ -0,0 +1,554 @@
1+-- |
2+-- Module : Serial.Transformation1
3+-- Copyright : (c) 2018 Composewell Technologies
4+-- License : BSD-3-Clause
5+-- Maintainer : streamly@composewell.com
6+
7+{-# LANGUAGE CPP #-}
8+{-# LANGUAGE FlexibleContexts #-}
9+{-# LANGUAGE ScopedTypeVariables #-}
10+{-# LANGUAGE RankNTypes #-}
11+
12+#ifdef __HADDOCK_VERSION__
13+#undef INSPECTION
14+#endif
15+
16+#ifdef INSPECTION
17+{-# LANGUAGE TemplateHaskell #-}
18+{-# OPTIONS_GHC -fplugin Test.Inspection.Plugin #-}
19+#endif
20+
21+module Serial.Transformation1 (benchmarks) where
22+
23+import Control.DeepSeq (NFData(..))
24+import Control.Monad.IO.Class (MonadIO(..))
25+import Data.Functor.Identity (Identity)
26+import Data.IORef (newIORef, modifyIORef')
27+import System.Random (randomRIO)
28+
29+#ifdef INSPECTION
30+import Test.Inspection
31+#endif
32+
33+import qualified Streamly.Prelude as S
34+import qualified Streamly.Internal.Data.Stream.IsStream as Internal
35+import qualified Streamly.Internal.Data.Fold as FL
36+import qualified Prelude
37+
38+import Gauge
39+import Streamly.Prelude (SerialT, serially, MonadAsync)
40+import Streamly.Benchmark.Common
41+import Streamly.Benchmark.Prelude
42+import Streamly.Internal.Data.Time.Units
43+import Prelude hiding (sequence, mapM, fmap)
44+
45+-------------------------------------------------------------------------------
46+-- Pipelines (stream-to-stream transformations)
47+-------------------------------------------------------------------------------
48+
49+-------------------------------------------------------------------------------
50+-- one-to-one transformations
51+-------------------------------------------------------------------------------
52+
53+-------------------------------------------------------------------------------
54+-- Traversable Instance
55+-------------------------------------------------------------------------------
56+
57+{-# INLINE traversableTraverse #-}
58+traversableTraverse :: SerialT Identity Int -> IO (SerialT Identity Int)
59+traversableTraverse = traverse return
60+
61+{-# INLINE traversableSequenceA #-}
62+traversableSequenceA :: SerialT Identity Int -> IO (SerialT Identity Int)
63+traversableSequenceA = sequenceA . Prelude.fmap return
64+
65+{-# INLINE traversableMapM #-}
66+traversableMapM :: SerialT Identity Int -> IO (SerialT Identity Int)
67+traversableMapM = Prelude.mapM return
68+
69+{-# INLINE traversableSequence #-}
70+traversableSequence :: SerialT Identity Int -> IO (SerialT Identity Int)
71+traversableSequence = Prelude.sequence . Prelude.fmap return
72+
73+{-# INLINE benchPureSinkIO #-}
74+benchPureSinkIO
75+ :: NFData b
76+ => Int -> String -> (SerialT Identity Int -> IO b) -> Benchmark
77+benchPureSinkIO value name f =
78+ bench name $ nfIO $ randomRIO (1, 1) >>= f . sourceUnfoldr value
79+
80+o_n_space_traversable :: Int -> [Benchmark]
81+o_n_space_traversable value =
82+ -- Buffering operations using heap proportional to number of elements.
83+ [ bgroup "traversable"
84+ -- Traversable instance
85+ [ benchPureSinkIO value "traverse" traversableTraverse
86+ , benchPureSinkIO value "sequenceA" traversableSequenceA
87+ , benchPureSinkIO value "mapM" traversableMapM
88+ , benchPureSinkIO value "sequence" traversableSequence
89+ ]
90+ ]
91+
92+-------------------------------------------------------------------------------
93+-- maps and scans
94+-------------------------------------------------------------------------------
95+
96+{-# INLINE scanl' #-}
97+scanl' :: MonadIO m => Int -> SerialT m Int -> m ()
98+scanl' n = composeN n $ S.scanl' (+) 0
99+
100+{-# INLINE scanlM' #-}
101+scanlM' :: MonadIO m => Int -> SerialT m Int -> m ()
102+scanlM' n = composeN n $ S.scanlM' (\b a -> return $ b + a) (return 0)
103+
104+{-# INLINE scanl1' #-}
105+scanl1' :: MonadIO m => Int -> SerialT m Int -> m ()
106+scanl1' n = composeN n $ S.scanl1' (+)
107+
108+{-# INLINE scanl1M' #-}
109+scanl1M' :: MonadIO m => Int -> SerialT m Int -> m ()
110+scanl1M' n = composeN n $ S.scanl1M' (\b a -> return $ b + a)
111+
112+{-# INLINE scan #-}
113+scan :: MonadIO m => Int -> SerialT m Int -> m ()
114+scan n = composeN n $ S.scan FL.sum
115+
116+{-# INLINE postscanl' #-}
117+postscanl' :: MonadIO m => Int -> SerialT m Int -> m ()
118+postscanl' n = composeN n $ S.postscanl' (+) 0
119+
120+{-# INLINE postscanlM' #-}
121+postscanlM' :: MonadIO m => Int -> SerialT m Int -> m ()
122+postscanlM' n = composeN n $ S.postscanlM' (\b a -> return $ b + a) (return 0)
123+
124+{-# INLINE postscan #-}
125+postscan :: MonadIO m => Int -> SerialT m Int -> m ()
126+postscan n = composeN n $ S.postscan FL.sum
127+
128+{-# INLINE sequence #-}
129+sequence ::
130+ (S.IsStream t, S.MonadAsync m)
131+ => (t m Int -> S.SerialT m Int)
132+ -> t m (m Int)
133+ -> m ()
134+sequence t = S.drain . t . S.sequence
135+
136+{-# INLINE tap #-}
137+tap :: MonadIO m => Int -> SerialT m Int -> m ()
138+tap n = composeN n $ S.tap FL.sum
139+
140+{-# INLINE tapRate #-}
141+tapRate :: Int -> SerialT IO Int -> IO ()
142+tapRate n str = do
143+ cref <- newIORef 0
144+ composeN n (Internal.tapRate 1 (\c -> modifyIORef' cref (c +))) str
145+
146+{-# INLINE pollCounts #-}
147+pollCounts :: Int -> SerialT IO Int -> IO ()
148+pollCounts n =
149+ composeN n (Internal.pollCounts (const True) f FL.drain)
150+
151+ where
152+
153+ f = Internal.rollingMap (-) . Internal.delayPost 1
154+
155+{-# INLINE timestamped #-}
156+timestamped :: (S.MonadAsync m) => SerialT m Int -> m ()
157+timestamped = S.drain . Internal.timestamped
158+
159+{-# INLINE foldrS #-}
160+foldrS :: MonadIO m => Int -> SerialT m Int -> m ()
161+foldrS n = composeN n $ Internal.foldrS S.cons S.nil
162+
163+{-# INLINE foldrSMap #-}
164+foldrSMap :: MonadIO m => Int -> SerialT m Int -> m ()
165+foldrSMap n = composeN n $ Internal.foldrS (\x xs -> x + 1 `S.cons` xs) S.nil
166+
167+{-# INLINE foldrT #-}
168+foldrT :: MonadIO m => Int -> SerialT m Int -> m ()
169+foldrT n = composeN n $ Internal.foldrT S.cons S.nil
170+
171+{-# INLINE foldrTMap #-}
172+foldrTMap :: MonadIO m => Int -> SerialT m Int -> m ()
173+foldrTMap n = composeN n $ Internal.foldrT (\x xs -> x + 1 `S.cons` xs) S.nil
174+
175+
176+{-# INLINE trace #-}
177+trace :: MonadAsync m => Int -> SerialT m Int -> m ()
178+trace n = composeN n $ Internal.trace return
179+
180+o_1_space_mapping :: Int -> [Benchmark]
181+o_1_space_mapping value =
182+ [ bgroup
183+ "mapping"
184+ [
185+ -- Right folds
186+ benchIOSink value "foldrS" (foldrS 1)
187+ , benchIOSink value "foldrSMap" (foldrSMap 1)
188+ , benchIOSink value "foldrT" (foldrT 1)
189+ , benchIOSink value "foldrTMap" (foldrTMap 1)
190+
191+ -- Mapping
192+ , benchIOSink value "map" (mapN serially 1)
193+ , bench "sequence" $ nfIO $ randomRIO (1, 1000) >>= \n ->
194+ sequence serially (sourceUnfoldrAction value n)
195+ , benchIOSink value "mapM" (mapM serially 1)
196+ , benchIOSink value "tap" (tap 1)
197+ , benchIOSink value "tapRate 1 second" (tapRate 1)
198+ , benchIOSink value "pollCounts 1 second" (pollCounts 1)
199+ , benchIOSink value "timestamped" timestamped
200+
201+ -- Scanning
202+ , benchIOSink value "scanl'" (scanl' 1)
203+ , benchIOSink value "scanl1'" (scanl1' 1)
204+ , benchIOSink value "scanlM'" (scanlM' 1)
205+ , benchIOSink value "scanl1M'" (scanl1M' 1)
206+ , benchIOSink value "postscanl'" (postscanl' 1)
207+ , benchIOSink value "postscanlM'" (postscanlM' 1)
208+
209+ , benchIOSink value "scan" (scan 1)
210+ , benchIOSink value "postscan" (postscan 1)
211+ ]
212+ ]
213+
214+o_1_space_mappingX4 :: Int -> [Benchmark]
215+o_1_space_mappingX4 value =
216+ [ bgroup "mappingX4"
217+ [ benchIOSink value "map" (mapN serially 4)
218+ , benchIOSink value "mapM" (mapM serially 4)
219+ , benchIOSink value "trace" (trace 4)
220+
221+ , benchIOSink value "scanl'" (scanl' 4)
222+ , benchIOSink value "scanl1'" (scanl1' 4)
223+ , benchIOSink value "scanlM'" (scanlM' 4)
224+ , benchIOSink value "scanl1M'" (scanl1M' 4)
225+ , benchIOSink value "postscanl'" (postscanl' 4)
226+ , benchIOSink value "postscanlM'" (postscanlM' 4)
227+
228+ ]
229+ ]
230+
231+{-# INLINE sieveScan #-}
232+sieveScan :: Monad m => SerialT m Int -> SerialT m Int
233+sieveScan =
234+ S.mapMaybe snd
235+ . S.scanlM' (\(primes, _) n -> do
236+ return $
237+ let ps = takeWhile (\p -> p * p <= n) primes
238+ in if all (\p -> n `mod` p /= 0) ps
239+ then (primes ++ [n], Just n)
240+ else (primes, Nothing)) (return ([2], Just 2))
241+
242+o_n_space_mapping :: Int -> [Benchmark]
243+o_n_space_mapping value =
244+ [ bgroup "mapping"
245+ [ benchIO "naive prime sieve"
246+ (\n -> S.sum $ sieveScan $ S.enumerateFromTo 2 (value + n))
247+ ]
248+ ]
249+
250+-------------------------------------------------------------------------------
251+-- Functor
252+-------------------------------------------------------------------------------
253+
254+o_1_space_functor :: Int -> [Benchmark]
255+o_1_space_functor value =
256+ [ bgroup "Functor"
257+ [ benchIOSink value "fmap" (fmapN serially 1)
258+ , benchIOSink value "fmap x 4" (fmapN serially 4)
259+ ]
260+ ]
261+
262+-------------------------------------------------------------------------------
263+-- Size reducing transformations (filtering)
264+-------------------------------------------------------------------------------
265+
266+{-# INLINE filterEven #-}
267+filterEven :: MonadIO m => Int -> SerialT m Int -> m ()
268+filterEven n = composeN n $ S.filter even
269+
270+{-# INLINE filterAllOut #-}
271+filterAllOut :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
272+filterAllOut value n = composeN n $ S.filter (> (value + 1))
273+
274+{-# INLINE filterAllIn #-}
275+filterAllIn :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
276+filterAllIn value n = composeN n $ S.filter (<= (value + 1))
277+
278+{-# INLINE filterMEven #-}
279+filterMEven :: MonadIO m => Int -> SerialT m Int -> m ()
280+filterMEven n = composeN n $ S.filterM (return . even)
281+
282+{-# INLINE filterMAllOut #-}
283+filterMAllOut :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
284+filterMAllOut value n = composeN n $ S.filterM (\x -> return $ x > (value + 1))
285+
286+{-# INLINE filterMAllIn #-}
287+filterMAllIn :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
288+filterMAllIn value n = composeN n $ S.filterM (\x -> return $ x <= (value + 1))
289+
290+{-# INLINE _takeOne #-}
291+_takeOne :: MonadIO m => Int -> SerialT m Int -> m ()
292+_takeOne n = composeN n $ S.take 1
293+
294+{-# INLINE takeAll #-}
295+takeAll :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
296+takeAll value n = composeN n $ S.take (value + 1)
297+
298+{-# INLINE takeWhileTrue #-}
299+takeWhileTrue :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
300+takeWhileTrue value n = composeN n $ S.takeWhile (<= (value + 1))
301+
302+{-# INLINE takeWhileMTrue #-}
303+takeWhileMTrue :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
304+takeWhileMTrue value n = composeN n $ S.takeWhileM (return . (<= (value + 1)))
305+
306+{-# INLINE takeByTime #-}
307+takeByTime :: NanoSecond64 -> Int -> SerialT IO Int -> IO ()
308+takeByTime i n = composeN n (Internal.takeByTime i)
309+
310+#ifdef INSPECTION
311+-- inspect $ hasNoType 'takeByTime ''SPEC
312+inspect $ hasNoTypeClasses 'takeByTime
313+-- inspect $ 'takeByTime `hasNoType` ''D.Step
314+#endif
315+
316+{-# INLINE dropOne #-}
317+dropOne :: MonadIO m => Int -> SerialT m Int -> m ()
318+dropOne n = composeN n $ S.drop 1
319+
320+{-# INLINE dropAll #-}
321+dropAll :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
322+dropAll value n = composeN n $ S.drop (value + 1)
323+
324+{-# INLINE dropWhileTrue #-}
325+dropWhileTrue :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
326+dropWhileTrue value n = composeN n $ S.dropWhile (<= (value + 1))
327+
328+{-# INLINE dropWhileMTrue #-}
329+dropWhileMTrue :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
330+dropWhileMTrue value n = composeN n $ S.dropWhileM (return . (<= (value + 1)))
331+
332+{-# INLINE dropWhileFalse #-}
333+dropWhileFalse :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
334+dropWhileFalse value n = composeN n $ S.dropWhile (> (value + 1))
335+
336+-- XXX Decide on the time interval
337+{-# INLINE _intervalsOfSum #-}
338+_intervalsOfSum :: MonadAsync m => Double -> Int -> SerialT m Int -> m ()
339+_intervalsOfSum i n = composeN n (S.intervalsOf i FL.sum)
340+
341+{-# INLINE dropByTime #-}
342+dropByTime :: NanoSecond64 -> Int -> SerialT IO Int -> IO ()
343+dropByTime i n = composeN n (Internal.dropByTime i)
344+
345+#ifdef INSPECTION
346+inspect $ hasNoTypeClasses 'dropByTime
347+-- inspect $ 'dropByTime `hasNoType` ''D.Step
348+#endif
349+
350+{-# INLINE findIndices #-}
351+findIndices :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
352+findIndices value n = composeN n $ S.findIndices (== (value + 1))
353+
354+{-# INLINE elemIndices #-}
355+elemIndices :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
356+elemIndices value n = composeN n $ S.elemIndices (value + 1)
357+
358+{-# INLINE deleteBy #-}
359+deleteBy :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
360+deleteBy value n = composeN n $ S.deleteBy (>=) (value + 1)
361+
362+-- uniq . uniq == uniq, composeN 2 ~ composeN 1
363+{-# INLINE uniq #-}
364+uniq :: MonadIO m => Int -> SerialT m Int -> m ()
365+uniq n = composeN n S.uniq
366+
367+{-# INLINE mapMaybe #-}
368+mapMaybe :: MonadIO m => Int -> SerialT m Int -> m ()
369+mapMaybe n =
370+ composeN n $
371+ S.mapMaybe
372+ (\x ->
373+ if odd x
374+ then Nothing
375+ else Just x)
376+
377+{-# INLINE mapMaybeM #-}
378+mapMaybeM :: S.MonadAsync m => Int -> SerialT m Int -> m ()
379+mapMaybeM n =
380+ composeN n $
381+ S.mapMaybeM
382+ (\x ->
383+ if odd x
384+ then return Nothing
385+ else return $ Just x)
386+
387+o_1_space_filtering :: Int -> [Benchmark]
388+o_1_space_filtering value =
389+ [ bgroup "filtering"
390+ [ benchIOSink value "filter-even" (filterEven 1)
391+ , benchIOSink value "filter-all-out" (filterAllOut value 1)
392+ , benchIOSink value "filter-all-in" (filterAllIn value 1)
393+
394+ , benchIOSink value "filterM-even" (filterMEven 1)
395+ , benchIOSink value "filterM-all-out" (filterMAllOut value 1)
396+ , benchIOSink value "filterM-all-in" (filterMAllIn value 1)
397+
398+ -- Trimming
399+ , benchIOSink value "take-all" (takeAll value 1)
400+ , benchIOSink
401+ value
402+ "takeByTime-all"
403+ (takeByTime (NanoSecond64 maxBound) 1)
404+ , benchIOSink value "takeWhile-true" (takeWhileTrue value 1)
405+ -- , benchIOSink value "takeWhileM-true" (_takeWhileMTrue value 1)
406+ , benchIOSink value "drop-one" (dropOne 1)
407+ , benchIOSink value "drop-all" (dropAll value 1)
408+ , benchIOSink
409+ value
410+ "dropByTime-all"
411+ (dropByTime (NanoSecond64 maxBound) 1)
412+ , benchIOSink value "dropWhile-true" (dropWhileTrue value 1)
413+ -- , benchIOSink value "dropWhileM-true" (_dropWhileMTrue value 1)
414+ , benchIOSink
415+ value
416+ "dropWhile-false"
417+ (dropWhileFalse value 1)
418+ , benchIOSink value "deleteBy" (deleteBy value 1)
419+
420+ , benchIOSink value "uniq" (uniq 1)
421+
422+ -- Map and filter
423+ , benchIOSink value "mapMaybe" (mapMaybe 1)
424+ , benchIOSink value "mapMaybeM" (mapMaybeM 1)
425+
426+ -- Searching (stateful map and filter)
427+ , benchIOSink value "findIndices" (findIndices value 1)
428+ , benchIOSink value "elemIndices" (elemIndices value 1)
429+ ]
430+ ]
431+
432+o_1_space_filteringX4 :: Int -> [Benchmark]
433+o_1_space_filteringX4 value =
434+ [ bgroup "filteringX4"
435+ [ benchIOSink value "filter-even" (filterEven 4)
436+ , benchIOSink value "filter-all-out" (filterAllOut value 4)
437+ , benchIOSink value "filter-all-in" (filterAllIn value 4)
438+
439+ , benchIOSink value "filterM-even" (filterMEven 4)
440+ , benchIOSink value "filterM-all-out" (filterMAllOut value 4)
441+ , benchIOSink value "filterM-all-in" (filterMAllIn value 4)
442+
443+ -- trimming
444+ , benchIOSink value "take-all" (takeAll value 4)
445+ , benchIOSink value "takeWhile-true" (takeWhileTrue value 4)
446+ , benchIOSink value "takeWhileM-true" (takeWhileMTrue value 4)
447+ , benchIOSink value "drop-one" (dropOne 4)
448+ , benchIOSink value "drop-all" (dropAll value 4)
449+ , benchIOSink value "dropWhile-true" (dropWhileTrue value 4)
450+ , benchIOSink value "dropWhileM-true" (dropWhileMTrue value 4)
451+ , benchIOSink
452+ value
453+ "dropWhile-false"
454+ (dropWhileFalse value 4)
455+ , benchIOSink value "deleteBy" (deleteBy value 4)
456+
457+ , benchIOSink value "uniq" (uniq 4)
458+
459+ -- map and filter
460+ , benchIOSink value "mapMaybe" (mapMaybe 4)
461+ , benchIOSink value "mapMaybeM" (mapMaybeM 4)
462+
463+ -- searching
464+ , benchIOSink value "findIndices" (findIndices value 4)
465+ , benchIOSink value "elemIndices" (elemIndices value 4)
466+ ]
467+ ]
468+
469+-------------------------------------------------------------------------------
470+-- Size increasing transformations (insertions)
471+-------------------------------------------------------------------------------
472+
473+{-# INLINE intersperse #-}
474+intersperse :: S.MonadAsync m => Int -> Int -> SerialT m Int -> m ()
475+intersperse value n = composeN n $ S.intersperse (value + 1)
476+
477+{-# INLINE intersperseM #-}
478+intersperseM :: S.MonadAsync m => Int -> Int -> SerialT m Int -> m ()
479+intersperseM value n = composeN n $ S.intersperseM (return $ value + 1)
480+
481+{-# INLINE insertBy #-}
482+insertBy :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
483+insertBy value n = composeN n $ S.insertBy compare (value + 1)
484+
485+o_1_space_inserting :: Int -> [Benchmark]
486+o_1_space_inserting value =
487+ [ bgroup "filtering"
488+ [ benchIOSink value "intersperse" (intersperse value 1)
489+ , benchIOSink value "intersperseM" (intersperseM value 1)
490+ , benchIOSink value "insertBy" (insertBy value 1)
491+ ]
492+ ]
493+
494+o_1_space_insertingX4 :: Int -> [Benchmark]
495+o_1_space_insertingX4 value =
496+ [ bgroup "insertingX4"
497+ [ benchIOSink value "intersperse" (intersperse value 4)
498+ , benchIOSink value "insertBy" (insertBy value 4)
499+ ]
500+ ]
501+
502+-------------------------------------------------------------------------------
503+-- Indexing
504+-------------------------------------------------------------------------------
505+
506+{-# INLINE indexed #-}
507+indexed :: MonadIO m => Int -> SerialT m Int -> m ()
508+indexed n = composeN n (S.map snd . S.indexed)
509+
510+{-# INLINE indexedR #-}
511+indexedR :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
512+indexedR value n = composeN n (S.map snd . S.indexedR value)
513+
514+o_1_space_indexing :: Int -> [Benchmark]
515+o_1_space_indexing value =
516+ [ bgroup "indexing"
517+ [ benchIOSink value "indexed" (indexed 1)
518+ , benchIOSink value "indexedR" (indexedR value 1)
519+ ]
520+ ]
521+
522+o_1_space_indexingX4 :: Int -> [Benchmark]
523+o_1_space_indexingX4 value =
524+ [ bgroup "indexingx4"
525+ [ benchIOSink value "indexed" (indexed 4)
526+ , benchIOSink value "indexedR" (indexedR value 4)
527+ ]
528+ ]
529+
530+-------------------------------------------------------------------------------
531+-- Main
532+-------------------------------------------------------------------------------
533+
534+-- In addition to gauge options, the number of elements in the stream can be
535+-- passed using the --stream-size option.
536+--
537+benchmarks :: String -> Int -> [Benchmark]
538+benchmarks moduleName size =
539+ [ bgroup (o_1_space_prefix moduleName) $ Prelude.concat
540+ [ o_1_space_functor size
541+ , o_1_space_mapping size
542+ , o_1_space_mappingX4 size
543+ , o_1_space_filtering size
544+ , o_1_space_filteringX4 size
545+ , o_1_space_inserting size
546+ , o_1_space_insertingX4 size
547+ , o_1_space_indexing size
548+ , o_1_space_indexingX4 size
549+ ]
550+ , bgroup (o_n_space_prefix moduleName) $ Prelude.concat
551+ [ o_n_space_traversable size
552+ , o_n_space_mapping size
553+ ]
554+ ]
benchmark/Streamly/Benchmark/Prelude/Serial/Transformation2.hsnew file+425
@@ -0,0 +1,425 @@
1+-- |
2+-- Module : Serial.Transformation2
3+-- Copyright : (c) 2018 Composewell Technologies
4+-- License : BSD-3-Clause
5+-- Maintainer : streamly@composewell.com
6+
7+{-# LANGUAGE CPP #-}
8+{-# LANGUAGE FlexibleContexts #-}
9+{-# LANGUAGE ScopedTypeVariables #-}
10+{-# LANGUAGE DeriveAnyClass #-}
11+{-# LANGUAGE DeriveGeneric #-}
12+{-# LANGUAGE RankNTypes #-}
13+{-# LANGUAGE TupleSections #-}
14+
15+module Serial.Transformation2 (benchmarks) where
16+
17+import Control.DeepSeq (NFData(..))
18+import Control.Monad (when)
19+import Control.Monad.IO.Class (MonadIO(..))
20+import GHC.Generics (Generic)
21+
22+import qualified Streamly.Prelude as S
23+import qualified Streamly.Internal.Data.Stream.IsStream as Internal
24+import qualified Streamly.Internal.Data.Fold as FL
25+
26+import Gauge
27+import Streamly.Prelude (SerialT, serially)
28+import Streamly.Benchmark.Common
29+import Streamly.Benchmark.Prelude
30+import Prelude hiding (reverse, tail)
31+
32+-------------------------------------------------------------------------------
33+-- Iteration/looping utilities
34+-------------------------------------------------------------------------------
35+
36+{-# INLINE iterateN #-}
37+iterateN :: (Int -> a -> a) -> a -> Int -> a
38+iterateN g initial count = f count initial
39+
40+ where
41+
42+ f (0 :: Int) x = x
43+ f i x = f (i - 1) (g i x)
44+
45+-- Iterate a transformation over a singleton stream
46+{-# INLINE iterateSingleton #-}
47+iterateSingleton :: S.MonadAsync m
48+ => (Int -> SerialT m Int -> SerialT m Int)
49+ -> Int
50+ -> Int
51+ -> SerialT m Int
52+iterateSingleton g count n = iterateN g (return n) count
53+
54+-- XXX need to check why this is slower than the explicit recursion above, even
55+-- if the above code is written in a foldr like head recursive way. We also
56+-- need to try this with foldlM' once #150 is fixed.
57+-- However, it is perhaps best to keep the iteration benchmarks independent of
58+-- foldrM and any related fusion issues.
59+{-# INLINE _iterateSingleton #-}
60+_iterateSingleton ::
61+ S.MonadAsync m
62+ => (Int -> SerialT m Int -> SerialT m Int)
63+ -> Int
64+ -> Int
65+ -> SerialT m Int
66+_iterateSingleton g value n = S.foldrM g (return n) $ sourceIntFromTo value n
67+
68+-- Apply transformation g count times on a stream of length len
69+{-# INLINE iterateSource #-}
70+iterateSource ::
71+ S.MonadAsync m
72+ => (SerialT m Int -> SerialT m Int)
73+ -> Int
74+ -> Int
75+ -> Int
76+ -> SerialT m Int
77+iterateSource g count len n = f count (sourceUnfoldrM len n)
78+
79+ where
80+
81+ f (0 :: Int) stream = stream
82+ f i stream = f (i - 1) (g stream)
83+
84+-------------------------------------------------------------------------------
85+-- Functor
86+-------------------------------------------------------------------------------
87+
88+o_n_space_functor :: Int -> [Benchmark]
89+o_n_space_functor value =
90+ [ bgroup "Functor"
91+ [ benchIO "(+) (n times) (baseline)" $ \i0 ->
92+ iterateN (\i acc -> acc >>= \n -> return $ i + n) (return i0) value
93+ , benchIOSrc serially "(<$) (n times)" $
94+ iterateSingleton (<$) value
95+ , benchIOSrc serially "fmap (n times)" $
96+ iterateSingleton (fmap . (+)) value
97+ {-
98+ , benchIOSrc serially "_(<$) (n times)" $
99+ _iterateSingleton (<$) value
100+ , benchIOSrc serially "_fmap (n times)" $
101+ _iterateSingleton (fmap . (+)) value
102+ -}
103+ ]
104+ ]
105+
106+-------------------------------------------------------------------------------
107+-- Grouping transformations
108+-------------------------------------------------------------------------------
109+
110+{-# INLINE groups #-}
111+groups :: MonadIO m => SerialT m Int -> m ()
112+groups = S.drain . S.groups FL.drain
113+
114+-- XXX Change this test when the order of comparison is later changed
115+{-# INLINE groupsByGT #-}
116+groupsByGT :: MonadIO m => SerialT m Int -> m ()
117+groupsByGT = S.drain . S.groupsBy (>) FL.drain
118+
119+{-# INLINE groupsByEq #-}
120+groupsByEq :: MonadIO m => SerialT m Int -> m ()
121+groupsByEq = S.drain . S.groupsBy (==) FL.drain
122+
123+-- XXX Change this test when the order of comparison is later changed
124+{-# INLINE groupsByRollingLT #-}
125+groupsByRollingLT :: MonadIO m => SerialT m Int -> m ()
126+groupsByRollingLT =
127+ S.drain . S.groupsByRolling (<) FL.drain
128+
129+{-# INLINE groupsByRollingEq #-}
130+groupsByRollingEq :: MonadIO m => SerialT m Int -> m ()
131+groupsByRollingEq =
132+ S.drain . S.groupsByRolling (==) FL.drain
133+
134+o_1_space_grouping :: Int -> [Benchmark]
135+o_1_space_grouping value =
136+ -- Buffering operations using heap proportional to group/window sizes.
137+ [ bgroup "grouping"
138+ [ benchIOSink value "groups" groups
139+ , benchIOSink value "groupsByGT" groupsByGT
140+ , benchIOSink value "groupsByEq" groupsByEq
141+ , benchIOSink value "groupsByRollingLT" groupsByRollingLT
142+ , benchIOSink value "groupsByRollingEq" groupsByRollingEq
143+ ]
144+ ]
145+
146+-------------------------------------------------------------------------------
147+-- Size conserving transformations (reordering, buffering, etc.)
148+-------------------------------------------------------------------------------
149+
150+{-# INLINE reverse #-}
151+reverse :: MonadIO m => Int -> SerialT m Int -> m ()
152+reverse n = composeN n S.reverse
153+
154+{-# INLINE reverse' #-}
155+reverse' :: MonadIO m => Int -> SerialT m Int -> m ()
156+reverse' n = composeN n Internal.reverse'
157+
158+o_n_heap_buffering :: Int -> [Benchmark]
159+o_n_heap_buffering value =
160+ [ bgroup "buffered"
161+ [
162+ -- Reversing/sorting a stream
163+ benchIOSink value "reverse" (reverse 1)
164+ , benchIOSink value "reverse'" (reverse' 1)
165+
166+ , benchIOSink value "mkAsync" (mkAsync serially)
167+ ]
168+ ]
169+
170+-------------------------------------------------------------------------------
171+-- Grouping/Splitting
172+-------------------------------------------------------------------------------
173+
174+{-# INLINE classifySessionsOf #-}
175+classifySessionsOf :: (S.MonadAsync m) => SerialT m Int -> m ()
176+classifySessionsOf =
177+ S.drain
178+ . Internal.classifySessionsOf
179+ 3 (const (return False)) (fmap Right FL.drain)
180+ . S.map (\(ts,(k,a)) -> (k, a, ts))
181+ . Internal.timestamped
182+ . S.concatMap (\x -> S.map (x,) (S.enumerateFromTo 1 (10 :: Int)))
183+
184+o_n_space_grouping :: Int -> [Benchmark]
185+o_n_space_grouping value =
186+ -- Buffering operations using heap proportional to group/window sizes.
187+ [ bgroup "grouping"
188+ [ benchIOSink (value `div` 10) "classifySessionsOf" classifySessionsOf
189+ ]
190+ ]
191+
192+-------------------------------------------------------------------------------
193+-- Mixed Transformation
194+-------------------------------------------------------------------------------
195+
196+{-# INLINE scanMap #-}
197+scanMap :: MonadIO m => Int -> SerialT m Int -> m ()
198+scanMap n = composeN n $ S.map (subtract 1) . S.scanl' (+) 0
199+
200+{-# INLINE dropMap #-}
201+dropMap :: MonadIO m => Int -> SerialT m Int -> m ()
202+dropMap n = composeN n $ S.map (subtract 1) . S.drop 1
203+
204+{-# INLINE dropScan #-}
205+dropScan :: MonadIO m => Int -> SerialT m Int -> m ()
206+dropScan n = composeN n $ S.scanl' (+) 0 . S.drop 1
207+
208+{-# INLINE takeDrop #-}
209+takeDrop :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
210+takeDrop value n = composeN n $ S.drop 1 . S.take (value + 1)
211+
212+{-# INLINE takeScan #-}
213+takeScan :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
214+takeScan value n = composeN n $ S.scanl' (+) 0 . S.take (value + 1)
215+
216+{-# INLINE takeMap #-}
217+takeMap :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
218+takeMap value n = composeN n $ S.map (subtract 1) . S.take (value + 1)
219+
220+{-# INLINE filterDrop #-}
221+filterDrop :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
222+filterDrop value n = composeN n $ S.drop 1 . S.filter (<= (value + 1))
223+
224+{-# INLINE filterTake #-}
225+filterTake :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
226+filterTake value n = composeN n $ S.take (value + 1) . S.filter (<= (value + 1))
227+
228+{-# INLINE filterScan #-}
229+filterScan :: MonadIO m => Int -> SerialT m Int -> m ()
230+filterScan n = composeN n $ S.scanl' (+) 0 . S.filter (<= maxBound)
231+
232+{-# INLINE filterScanl1 #-}
233+filterScanl1 :: MonadIO m => Int -> SerialT m Int -> m ()
234+filterScanl1 n = composeN n $ S.scanl1' (+) . S.filter (<= maxBound)
235+
236+{-# INLINE filterMap #-}
237+filterMap :: MonadIO m => Int -> Int -> SerialT m Int -> m ()
238+filterMap value n = composeN n $ S.map (subtract 1) . S.filter (<= (value + 1))
239+
240+-------------------------------------------------------------------------------
241+-- Scan and fold
242+-------------------------------------------------------------------------------
243+
244+data Pair a b =
245+ Pair !a !b
246+ deriving (Generic, NFData)
247+
248+{-# INLINE sumProductFold #-}
249+sumProductFold :: Monad m => SerialT m Int -> m (Int, Int)
250+sumProductFold = S.foldl' (\(s, p) x -> (s + x, p * x)) (0, 1)
251+
252+{-# INLINE sumProductScan #-}
253+sumProductScan :: Monad m => SerialT m Int -> m (Pair Int Int)
254+sumProductScan =
255+ S.foldl' (\(Pair _ p) (s0, x) -> Pair s0 (p * x)) (Pair 0 1) .
256+ S.scanl' (\(s, _) x -> (s + x, x)) (0, 0)
257+
258+{-# INLINE foldl'ReduceMap #-}
259+foldl'ReduceMap :: Monad m => SerialT m Int -> m Int
260+foldl'ReduceMap = fmap (+ 1) . S.foldl' (+) 0
261+
262+o_1_space_transformations_mixed :: Int -> [Benchmark]
263+o_1_space_transformations_mixed value =
264+ -- scanl-map and foldl-map are equivalent to the scan and fold in the foldl
265+ -- library. If scan/fold followed by a map is efficient enough we may not
266+ -- need monolithic implementations of these.
267+ [ bgroup "mixed"
268+ [ benchIOSink value "scanl-map" (scanMap 1)
269+ , benchIOSink value "foldl-map" foldl'ReduceMap
270+ , benchIOSink value "sum-product-fold" sumProductFold
271+ , benchIOSink value "sum-product-scan" sumProductScan
272+ ]
273+ ]
274+
275+o_1_space_transformations_mixedX4 :: Int -> [Benchmark]
276+o_1_space_transformations_mixedX4 value =
277+ [ bgroup "mixedX4"
278+ [ benchIOSink value "scan-map" (scanMap 4)
279+ , benchIOSink value "drop-map" (dropMap 4)
280+ , benchIOSink value "drop-scan" (dropScan 4)
281+ , benchIOSink value "take-drop" (takeDrop value 4)
282+ , benchIOSink value "take-scan" (takeScan value 4)
283+ , benchIOSink value "take-map" (takeMap value 4)
284+ , benchIOSink value "filter-drop" (filterDrop value 4)
285+ , benchIOSink value "filter-take" (filterTake value 4)
286+ , benchIOSink value "filter-scan" (filterScan 4)
287+ , benchIOSink value "filter-scanl1" (filterScanl1 4)
288+ , benchIOSink value "filter-map" (filterMap value 4)
289+ ]
290+ ]
291+
292+-------------------------------------------------------------------------------
293+-- Iterating a transformation over and over again
294+-------------------------------------------------------------------------------
295+
296+-- this is quadratic
297+{-# INLINE iterateScan #-}
298+iterateScan :: S.MonadAsync m => Int -> Int -> Int -> SerialT m Int
299+iterateScan = iterateSource (S.scanl' (+) 0)
300+
301+-- this is quadratic
302+{-# INLINE iterateScanl1 #-}
303+iterateScanl1 :: S.MonadAsync m => Int -> Int -> Int -> SerialT m Int
304+iterateScanl1 = iterateSource (S.scanl1' (+))
305+
306+{-# INLINE iterateMapM #-}
307+iterateMapM :: S.MonadAsync m => Int -> Int -> Int -> SerialT m Int
308+iterateMapM = iterateSource (S.mapM return)
309+
310+{-# INLINE iterateFilterEven #-}
311+iterateFilterEven :: S.MonadAsync m => Int -> Int -> Int -> SerialT m Int
312+iterateFilterEven = iterateSource (S.filter even)
313+
314+{-# INLINE iterateTakeAll #-}
315+iterateTakeAll :: S.MonadAsync m => Int -> Int -> Int -> Int -> SerialT m Int
316+iterateTakeAll value = iterateSource (S.take (value + 1))
317+
318+{-# INLINE iterateDropOne #-}
319+iterateDropOne :: S.MonadAsync m => Int -> Int -> Int -> SerialT m Int
320+iterateDropOne = iterateSource (S.drop 1)
321+
322+{-# INLINE iterateDropWhileFalse #-}
323+iterateDropWhileFalse :: S.MonadAsync m
324+ => Int -> Int -> Int -> Int -> SerialT m Int
325+iterateDropWhileFalse value = iterateSource (S.dropWhile (> (value + 1)))
326+
327+{-# INLINE iterateDropWhileTrue #-}
328+iterateDropWhileTrue :: S.MonadAsync m
329+ => Int -> Int -> Int -> Int -> SerialT m Int
330+iterateDropWhileTrue value = iterateSource (S.dropWhile (<= (value + 1)))
331+
332+{-# INLINE tail #-}
333+tail :: Monad m => SerialT m a -> m ()
334+tail s = S.tail s >>= mapM_ tail
335+
336+{-# INLINE nullHeadTail #-}
337+nullHeadTail :: Monad m => SerialT m Int -> m ()
338+nullHeadTail s = do
339+ r <- S.null s
340+ when (not r) $ do
341+ _ <- S.head s
342+ S.tail s >>= mapM_ nullHeadTail
343+
344+-- Head recursive operations.
345+o_n_stack_iterated :: Int -> [Benchmark]
346+o_n_stack_iterated value = by10 `seq` by100 `seq`
347+ [ bgroup "iterated"
348+ [ benchIOSrc serially "mapM (n/10 x 10)" $ iterateMapM by10 10
349+ , benchIOSrc serially "scanl' (quadratic) (n/100 x 100)" $
350+ iterateScan by100 100
351+ , benchIOSrc serially "scanl1' (n/10 x 10)" $ iterateScanl1 by10 10
352+ , benchIOSrc serially "filterEven (n/10 x 10)" $
353+ iterateFilterEven by10 10
354+ , benchIOSrc serially "takeAll (n/10 x 10)" $
355+ iterateTakeAll value by10 10
356+ , benchIOSrc serially "dropOne (n/10 x 10)" $ iterateDropOne by10 10
357+ , benchIOSrc serially "dropWhileFalse (n/10 x 10)" $
358+ iterateDropWhileFalse value by10 10
359+ , benchIOSrc serially "dropWhileTrue (n/10 x 10)" $
360+ iterateDropWhileTrue value by10 10
361+ , benchIOSink value "tail" tail
362+ , benchIOSink value "nullHeadTail" nullHeadTail
363+ ]
364+ ]
365+
366+ where
367+
368+ by10 = value `div` 10
369+ by100 = value `div` 100
370+
371+-------------------------------------------------------------------------------
372+-- Pipes
373+-------------------------------------------------------------------------------
374+
375+o_1_space_pipes :: Int -> [Benchmark]
376+o_1_space_pipes value =
377+ [ bgroup "pipes"
378+ [ benchIOSink value "mapM" (transformMapM serially 1)
379+ , benchIOSink value "compose" (transformComposeMapM serially 1)
380+ , benchIOSink value "tee" (transformTeeMapM serially 1)
381+#ifdef DEVBUILD
382+ -- XXX this take 1 GB memory to compile
383+ , benchIOSink value "zip" (transformZipMapM serially 1)
384+#endif
385+ ]
386+ ]
387+
388+o_1_space_pipesX4 :: Int -> [Benchmark]
389+o_1_space_pipesX4 value =
390+ [ bgroup "pipesX4"
391+ [ benchIOSink value "mapM" (transformMapM serially 4)
392+ , benchIOSink value "compose" (transformComposeMapM serially 4)
393+ , benchIOSink value "tee" (transformTeeMapM serially 4)
394+#ifdef DEVBUILD
395+ -- XXX this take 1 GB memory to compile
396+ , benchIOSink value "zip" (transformZipMapM serially 4)
397+#endif
398+ ]
399+ ]
400+
401+-------------------------------------------------------------------------------
402+-- Main
403+-------------------------------------------------------------------------------
404+
405+-- In addition to gauge options, the number of elements in the stream can be
406+-- passed using the --stream-size option.
407+--
408+benchmarks :: String -> Int -> [Benchmark]
409+benchmarks moduleName size =
410+ [ bgroup (o_1_space_prefix moduleName) $ Prelude.concat
411+ [ o_1_space_grouping size
412+ , o_1_space_transformations_mixed size
413+ , o_1_space_transformations_mixedX4 size
414+
415+ -- pipes
416+ , o_1_space_pipes size
417+ , o_1_space_pipesX4 size
418+ ]
419+ , bgroup (o_n_stack_prefix moduleName) (o_n_stack_iterated size)
420+ , bgroup (o_n_heap_prefix moduleName) $ Prelude.concat
421+ [ o_n_space_grouping size
422+ , o_n_space_functor size
423+ , o_n_heap_buffering size
424+ ]
425+ ]
benchmark/Streamly/Benchmark/Prelude/Serial/Transformation3.hsnew file+122
@@ -0,0 +1,122 @@
1+-- |
2+-- Module : Serial.Transformation3
3+-- Copyright : (c) 2018 Composewell Technologies
4+-- License : BSD-3-Clause
5+-- Maintainer : streamly@composewell.com
6+
7+{-# LANGUAGE CPP #-}
8+{-# LANGUAGE FlexibleContexts #-}
9+{-# LANGUAGE ScopedTypeVariables #-}
10+{-# LANGUAGE RankNTypes #-}
11+
12+module Serial.Transformation3 (benchmarks) where
13+
14+import Control.Monad.State.Strict (StateT, get, put, MonadState)
15+import qualified Control.Monad.State.Strict as State
16+import Control.Monad.Trans.Class (lift)
17+
18+import qualified Streamly.Prelude as S
19+import qualified Streamly.Internal.Data.Stream.IsStream as Internal
20+
21+import Gauge
22+import Streamly.Prelude (SerialT, serially)
23+import Streamly.Benchmark.Common
24+import Streamly.Benchmark.Prelude
25+import Prelude hiding (reverse, tail)
26+
27+-------------------------------------------------------------------------------
28+-- Monad transformation (hoisting etc.)
29+-------------------------------------------------------------------------------
30+
31+{-# INLINE sourceUnfoldrState #-}
32+sourceUnfoldrState :: (S.IsStream t, S.MonadAsync m)
33+ => Int -> Int -> t (StateT Int m) Int
34+sourceUnfoldrState value n = S.unfoldrM step n
35+ where
36+ step cnt =
37+ if cnt > n + value
38+ then return Nothing
39+ else do
40+ s <- get
41+ put (s + 1)
42+ return (Just (s, cnt + 1))
43+
44+{-# INLINE evalStateT #-}
45+evalStateT :: S.MonadAsync m => Int -> Int -> SerialT m Int
46+evalStateT value n =
47+ Internal.evalStateT (return 0) (sourceUnfoldrState value n)
48+
49+{-# INLINE withState #-}
50+withState :: S.MonadAsync m => Int -> Int -> SerialT m Int
51+withState value n =
52+ Internal.evalStateT
53+ (return (0 :: Int)) (Internal.liftInner (sourceUnfoldrM value n))
54+
55+o_1_space_hoisting :: Int -> [Benchmark]
56+o_1_space_hoisting value =
57+ [ bgroup "hoisting"
58+ [ benchIOSrc serially "evalState" (evalStateT value)
59+ , benchIOSrc serially "withState" (withState value)
60+ ]
61+ ]
62+
63+{-# INLINE iterateStateIO #-}
64+iterateStateIO ::
65+ (S.MonadAsync m)
66+ => Int
67+ -> StateT Int m Int
68+iterateStateIO n = do
69+ x <- get
70+ if x > n
71+ then do
72+ put (x - 1)
73+ iterateStateIO n
74+ else return x
75+
76+{-# INLINE iterateStateT #-}
77+iterateStateT :: Int -> SerialT (StateT Int IO) Int
78+iterateStateT n = do
79+ x <- lift get
80+ if x > n
81+ then do
82+ lift $ put (x - 1)
83+ iterateStateT n
84+ else return x
85+
86+{-# INLINE iterateState #-}
87+iterateState ::
88+ (S.MonadAsync m, MonadState Int m)
89+ => Int
90+ -> SerialT m Int
91+iterateState n = do
92+ x <- get
93+ if x > n
94+ then do
95+ put (x - 1)
96+ iterateState n
97+ else return x
98+
99+o_n_heap_transformer :: Int -> [Benchmark]
100+o_n_heap_transformer value =
101+ [ bgroup "transformer"
102+ [ benchIO "StateT Int IO (n times) (baseline)" $ \n ->
103+ State.evalStateT (iterateStateIO n) value
104+ , benchIO "SerialT (StateT Int IO) (n times)" $ \n ->
105+ State.evalStateT (S.drain (iterateStateT n)) value
106+ , benchIO "MonadState Int m => SerialT m Int" $ \n ->
107+ State.evalStateT (S.drain (iterateState n)) value
108+ ]
109+ ]
110+
111+-------------------------------------------------------------------------------
112+-- Main
113+-------------------------------------------------------------------------------
114+
115+-- In addition to gauge options, the number of elements in the stream can be
116+-- passed using the --stream-size option.
117+--
118+benchmarks :: String -> Int -> [Benchmark]
119+benchmarks moduleName size =
120+ [ bgroup (o_1_space_prefix moduleName) (o_1_space_hoisting size)
121+ , bgroup (o_n_heap_prefix moduleName) (o_n_heap_transformer size)
122+ ]
benchmark/streamly-benchmarks.cabal+12−2
@@ -136,7 +136,7 @@ library
136
136
137common bench-options
137common bench-options
138 import: compile-options, optimization-options, bench-depends
138 import: compile-options, optimization-options, bench-depends
139− ghc-options: -rtsopts
139+ ghc-options: -rtsopts +RTS -M512M -RTS
140 build-depends: streamly-benchmarks == 0.0.0
140 build-depends: streamly-benchmarks == 0.0.0
141
141
142-- Some benchmarks are threaded some are not
142-- Some benchmarks are threaded some are not
@@ -144,7 +144,7 @@ common bench-options-threaded
144 import: compile-options, optimization-options, bench-depends
144 import: compile-options, optimization-options, bench-depends
145 -- -threaded and -N2 is important because some GC and space leak issues
145 -- -threaded and -N2 is important because some GC and space leak issues
146 -- trigger only with these options.
146 -- trigger only with these options.
147− ghc-options: -threaded -rtsopts -with-rtsopts "-N2"
147+ ghc-options: -threaded -rtsopts -with-rtsopts "-N2" +RTS -M512M -RTS
148 build-depends: streamly-benchmarks == 0.0.0
148 build-depends: streamly-benchmarks == 0.0.0
149
149
150-------------------------------------------------------------------------------
150-------------------------------------------------------------------------------
@@ -156,6 +156,13 @@ benchmark Prelude.Serial
156 type: exitcode-stdio-1.0
156 type: exitcode-stdio-1.0
157 hs-source-dirs: Streamly/Benchmark/Prelude
157 hs-source-dirs: Streamly/Benchmark/Prelude
158 main-is: Serial.hs
158 main-is: Serial.hs
159+ other-modules:
160+ Serial.Generation
161+ , Serial.Elimination
162+ , Serial.Transformation1
163+ , Serial.Transformation2
164+ , Serial.Transformation3
165+ , Serial.Nested
159 if impl(ghcjs)
166 if impl(ghcjs)
160 buildable: False
167 buildable: False
161 else
168 else
@@ -184,6 +191,7 @@ benchmark Prelude.ZipSerial
184benchmark Prelude.ZipAsync
191benchmark Prelude.ZipAsync
185 import: bench-options
192 import: bench-options
186 type: exitcode-stdio-1.0
193 type: exitcode-stdio-1.0
194+ ghc-options: +RTS -M1000M -RTS
187 hs-source-dirs: Streamly/Benchmark/Prelude
195 hs-source-dirs: Streamly/Benchmark/Prelude
188 main-is: ZipAsync.hs
196 main-is: ZipAsync.hs
189 if impl(ghcjs)
197 if impl(ghcjs)
@@ -224,6 +232,7 @@ benchmark Prelude.WAsync
224benchmark Prelude.Parallel
232benchmark Prelude.Parallel
225 import: bench-options-threaded
233 import: bench-options-threaded
226 type: exitcode-stdio-1.0
234 type: exitcode-stdio-1.0
235+ ghc-options: +RTS -M1000M -RTS
227 hs-source-dirs: Streamly/Benchmark/Prelude
236 hs-source-dirs: Streamly/Benchmark/Prelude
228 main-is: Parallel.hs
237 main-is: Parallel.hs
229 if impl(ghcjs)
238 if impl(ghcjs)
@@ -426,6 +435,7 @@ benchmark Memory.Array
426benchmark FileSystem.Handle
435benchmark FileSystem.Handle
427 import: bench-options
436 import: bench-options
428 type: exitcode-stdio-1.0
437 type: exitcode-stdio-1.0
438+ ghc-options: +RTS -M3000M -RTS
429 hs-source-dirs: Streamly/Benchmark/FileSystem
439 hs-source-dirs: Streamly/Benchmark/FileSystem
430 main-is: Handle.hs
440 main-is: Handle.hs
431 other-modules:
441 other-modules:
bin/bench-exec-one.sh+7−1
@@ -50,10 +50,16 @@ bench_rts_opts_specific () {
50 Prelude.Parallel/o-n-heap/monad-outer-product/*) echo -n "-M256M" ;;
50 Prelude.Parallel/o-n-heap/monad-outer-product/*) echo -n "-M256M" ;;
51 Prelude.Parallel/o-n-space/monad-outer-product/*) echo -n "-K4M -M256M" ;;
51 Prelude.Parallel/o-n-space/monad-outer-product/*) echo -n "-K4M -M256M" ;;
52
52
53+ # XXX These should be moved to o-n-space?
54+ Prelude.Serial/o-n-heap/grouping/classifySessionsOf) echo -n "-K1M -M32M" ;;
55+ Prelude.Serial/o-n-heap/Functor/*) echo -n "-K4M -M32M" ;;
56+ Prelude.Serial/o-n-heap/transformer/*) echo -n "-K8M -M64M" ;;
57+
53 Prelude.Serial/o-n-space/Functor/*) echo -n "-K4M -M64M" ;;
58 Prelude.Serial/o-n-space/Functor/*) echo -n "-K4M -M64M" ;;
54 Prelude.Serial/o-n-space/Applicative/*) echo -n "-K8M -M128M" ;;
59 Prelude.Serial/o-n-space/Applicative/*) echo -n "-K8M -M128M" ;;
55 Prelude.Serial/o-n-space/Monad/*) echo -n "-K8M -M64M" ;;
60 Prelude.Serial/o-n-space/Monad/*) echo -n "-K8M -M64M" ;;
56− Prelude.Serial/o-n-heap/transformer/*) echo -n "-K8M -M64M" ;;
61+
62+ # Use -K4M for o-n-space except for grouping
57 Prelude.Serial/o-n-space/grouping/*) echo -n "" ;;
63 Prelude.Serial/o-n-space/grouping/*) echo -n "" ;;
58 Prelude.Serial/o-n-space/*) echo -n "-K4M" ;;
64 Prelude.Serial/o-n-space/*) echo -n "-K4M" ;;
59
65
hie.yaml+18−1
@@ -3,7 +3,24 @@ cradle:
3 - path: "./benchmark"
3 - path: "./benchmark"
4 config:
4 config:
5 cradle:
5 cradle:
6− none:
6+ cabal:
7+ - path: "./benchmark/lib/"
8+ component: "lib:streamly-benchmarks"
9+
10+ - path: "./benchmark/Streamly/Benchmark/Prelude/Serial.hs"
11+ component: "bench:Prelude.Serial"
12+ - path: "./benchmark/Streamly/Benchmark/Prelude/Serial/Elimination.hs"
13+ component: "bench:Prelude.Serial"
14+ - path: "./benchmark/Streamly/Benchmark/Prelude/Serial/Generation.hs"
15+ component: "bench:Prelude.Serial"
16+ - path: "./benchmark/Streamly/Benchmark/Prelude/Serial/Nested.hs"
17+ component: "bench:Prelude.Serial"
18+ - path: "./benchmark/Streamly/Benchmark/Prelude/Serial/Transformation1.hs"
19+ component: "bench:Prelude.Serial"
20+ - path: "./benchmark/Streamly/Benchmark/Prelude/Serial/Transformation2.hs"
21+ component: "bench:Prelude.Serial"
22+ - path: "./benchmark/Streamly/Benchmark/Prelude/Serial/Transformation3.hs"
23+ component: "bench:Prelude.Serial"
7 - path: "./examples"
24 - path: "./examples"
8 config:
25 config:
9 cradle:
26 cradle:
streamly.cabal+1
@@ -115,6 +115,7 @@ extra-source-files:
115 benchmark/Streamly/Benchmark/FileSystem/*.hs
115 benchmark/Streamly/Benchmark/FileSystem/*.hs
116 benchmark/Streamly/Benchmark/FileSystem/Handle/*.hs
116 benchmark/Streamly/Benchmark/FileSystem/Handle/*.hs
117 benchmark/Streamly/Benchmark/Prelude/*.hs
117 benchmark/Streamly/Benchmark/Prelude/*.hs
118+ benchmark/Streamly/Benchmark/Prelude/Serial/*.hs
118 benchmark/lib/Streamly/Benchmark/*.hs
119 benchmark/lib/Streamly/Benchmark/*.hs
119 benchmark/streamly-benchmarks.cabal
120 benchmark/streamly-benchmarks.cabal
120 bin/bench-exec-one.sh
121 bin/bench-exec-one.sh