2-- Module : Stream.Transform
3-- Copyright : (c) 2018 Composewell Technologies
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
8{-# LANGUAGE FlexibleContexts #-}
9{-# LANGUAGE ScopedTypeVariables #-}
10{-# LANGUAGE RankNTypes #-}
12#ifdef __HADDOCK_VERSION__
17{-# LANGUAGE TemplateHaskell #-}
18{-# OPTIONS_GHC -fplugin Test.Inspection.Plugin #-}
21module Stream.Transform (benchmarks) where
23import Control.DeepSeq (NFData(..))
24import Control.Monad.IO.Class (MonadIO(..))
25import Data.Functor.Identity (Identity)
27import System.Random (randomRIO)
29import qualified Streamly.Internal.Data.Fold as FL
31import qualified Prelude
32import qualified Stream.Common as Common
33import qualified Streamly.Internal.Data.Unfold as Unfold
35import qualified Streamly.Internal.Data.Stream.IsStream as Stream
36import Streamly.Internal.Data.Time.Units
38import qualified Streamly.Internal.Data.Stream.IsStream.Parallel as Stream
39import qualified Streamly.Internal.Data.Stream as Stream
43import Streamly.Internal.Data.Stream (Stream)
44import Stream.Common hiding (scanl')
45import Streamly.Benchmark.Common
46import Prelude hiding (sequence, mapM)
48-------------------------------------------------------------------------------
49-- Pipelines (stream-to-stream transformations)
50-------------------------------------------------------------------------------
52-------------------------------------------------------------------------------
53-- one-to-one transformations
54-------------------------------------------------------------------------------
56-------------------------------------------------------------------------------
57-- Traversable Instance
58-------------------------------------------------------------------------------
60{-# INLINE traversableTraverse #-}
61traversableTraverse :: Stream Identity Int -> IO (Stream Identity Int)
62traversableTraverse = traverse return
64{-# INLINE traversableSequenceA #-}
65traversableSequenceA :: Stream Identity Int -> IO (Stream Identity Int)
66traversableSequenceA = sequenceA . Prelude.fmap return
68{-# INLINE traversableMapM #-}
69traversableMapM :: Stream Identity Int -> IO (Stream Identity Int)
70traversableMapM = Prelude.mapM return
72{-# INLINE traversableSequence #-}
73traversableSequence :: Stream Identity Int -> IO (Stream Identity Int)
74traversableSequence = Prelude.sequence . Prelude.fmap return
76{-# INLINE benchPureSinkIO #-}
79 => Int -> String -> (Stream Identity Int -> IO b) -> Benchmark
80benchPureSinkIO value name f =
81 bench name $ nfIO $ randomRIO (1, 1) >>= f . sourceUnfoldr value
83o_n_space_traversable :: Int -> [Benchmark]
84o_n_space_traversable value =
85 -- Buffering operations using heap proportional to number of elements.
86 [ bgroup "traversable"
87 -- Traversable instance
88 [ benchPureSinkIO value "traverse" traversableTraverse
89 , benchPureSinkIO value "sequenceA" traversableSequenceA
90 , benchPureSinkIO value "mapM" traversableMapM
91 , benchPureSinkIO value "sequence" traversableSequence
95-------------------------------------------------------------------------------
97-------------------------------------------------------------------------------
101scanl' :: MonadIO m => Int -> Stream m Int -> m ()
102scanl' n = composeN n $ Stream.scanl' (+) 0
104{-# INLINE scanlM' #-}
105scanlM' :: MonadIO m => Int -> Stream m Int -> m ()
106scanlM' n = composeN n $ Stream.scanlM' (\b a -> return $ b + a) (return 0)
108{-# INLINE scanl1' #-}
109scanl1' :: MonadIO m => Int -> Stream m Int -> m ()
110scanl1' n = composeN n $ Stream.scanl1' (+)
112{-# INLINE scanl1M' #-}
113scanl1M' :: MonadIO m => Int -> Stream m Int -> m ()
114scanl1M' n = composeN n $ Stream.scanl1M' (\b a -> return $ b + a)
118scan :: MonadIO m => Int -> Stream m Int -> m ()
119scan n = composeN n $ Stream.scan FL.sum
122{-# INLINE postscanl' #-}
123postscanl' :: MonadIO m => Int -> Stream m Int -> m ()
124postscanl' n = composeN n $ Stream.postscanl' (+) 0
126{-# INLINE postscanlM' #-}
127postscanlM' :: MonadIO m => Int -> Stream m Int -> m ()
128postscanlM' n = composeN n $ Stream.postscanlM' (\b a -> return $ b + a) (return 0)
131{-# INLINE postscan #-}
132postscan :: MonadIO m => Int -> Stream m Int -> m ()
133postscan n = composeN n $ Stream.postscan FL.sum
135{-# INLINE sequence #-}
136sequence :: MonadAsync m => Stream m (m Int) -> m ()
137sequence = Common.drain . Stream.sequence
140tap :: MonadIO m => Int -> Stream m Int -> m ()
141tap n = composeN n $ Stream.tap FL.sum
144{-# INLINE pollCounts #-}
145pollCounts :: Int -> Stream IO Int -> IO ()
147 composeN n (Stream.pollCounts (const True) f)
151 f = Stream.drain . Stream.rollingMap2 (-) . Stream.delayPost 1
153{-# INLINE timestamped #-}
154timestamped :: (MonadAsync m) => Stream m Int -> m ()
155timestamped = Stream.drain . Stream.timestamped
159foldrS :: MonadIO m => Int -> Stream m Int -> m ()
160foldrS n = composeN n $ Stream.foldrS Stream.cons Stream.nil
162{-# INLINE foldrSMap #-}
163foldrSMap :: MonadIO m => Int -> Stream m Int -> m ()
164foldrSMap n = composeN n $ Stream.foldrS (\x xs -> x + 1 `Stream.cons` xs) Stream.nil
167foldrT :: MonadIO m => Int -> Stream m Int -> m ()
168foldrT n = composeN n $ Stream.foldrT Stream.cons Stream.nil
170{-# INLINE foldrTMap #-}
171foldrTMap :: MonadIO m => Int -> Stream m Int -> m ()
172foldrTMap n = composeN n $ Stream.foldrT (\x xs -> x + 1 `Stream.cons` xs) Stream.nil
175trace :: MonadAsync m => Int -> Stream m Int -> m ()
176trace n = composeN n $ Stream.trace return
178o_1_space_mapping :: Int -> [Benchmark]
179o_1_space_mapping value =
184 benchIOSink value "foldrS" (foldrS 1)
185 , benchIOSink value "foldrSMap" (foldrSMap 1)
186 , benchIOSink value "foldrT" (foldrT 1)
187 , benchIOSink value "foldrTMap" (foldrTMap 1)
190 , benchIOSink value "map" (mapN 1)
191 , bench "sequence" $ nfIO $ randomRIO (1, 1000) >>= \n ->
192 sequence (sourceUnfoldrAction value n)
193 , benchIOSink value "mapM" (mapM 1)
194 , benchIOSink value "tap" (tap 1)
196 , benchIOSink value "pollCounts 1 second" (pollCounts 1)
197 , benchIOSink value "timestamped" timestamped
200 , benchIOSink value "scanl'" (scanl' 1)
201 , benchIOSink value "scanl1'" (scanl1' 1)
202 , benchIOSink value "scanlM'" (scanlM' 1)
203 , benchIOSink value "scanl1M'" (scanl1M' 1)
204 , benchIOSink value "postscanl'" (postscanl' 1)
205 , benchIOSink value "postscanlM'" (postscanlM' 1)
207 , benchIOSink value "scan" (scan 1)
208 , benchIOSink value "postscan" (postscan 1)
212o_1_space_mappingX4 :: Int -> [Benchmark]
213o_1_space_mappingX4 value =
215 [ benchIOSink value "map" (mapN 4)
216 , benchIOSink value "mapM" (mapM 4)
217 , benchIOSink value "trace" (trace 4)
220 , benchIOSink value "scanl'" (scanl' 4)
221 , benchIOSink value "scanl1'" (scanl1' 4)
222 , benchIOSink value "scanlM'" (scanlM' 4)
223 , benchIOSink value "scanl1M'" (scanl1M' 4)
224 , benchIOSink value "postscanl'" (postscanl' 4)
225 , benchIOSink value "postscanlM'" (postscanlM' 4)
230{-# INLINE sieveScan #-}
231sieveScan :: Monad m => Stream m Int -> Stream m Int
234 . Stream.scan (FL.foldlM' (\(primes, _) n -> do
236 let ps = takeWhile (\p -> p * p <= n) primes
237 in if all (\p -> n `mod` p /= 0) ps
238 then (primes ++ [n], Just n)
239 else (primes, Nothing)) (return ([2], Just 2)))
241o_n_space_mapping :: Int -> [Benchmark]
242o_n_space_mapping value =
244 [ benchIO "naive prime sieve"
245 (\n -> Stream.fold FL.sum $ sieveScan $ Stream.enumerateFromTo 2 (value + n))
249-------------------------------------------------------------------------------
251-------------------------------------------------------------------------------
253o_1_space_functor :: Int -> [Benchmark]
254o_1_space_functor value =
256 [ benchIOSink value "fmap" (mapN 1)
257 , benchIOSink value "fmap x 4" (mapN 4)
261-------------------------------------------------------------------------------
262-- Size reducing transformations (filtering)
263-------------------------------------------------------------------------------
265{-# INLINE filterEven #-}
266filterEven :: MonadIO m => Int -> Stream m Int -> m ()
267filterEven n = composeN n $ Stream.filter even
269{-# INLINE filterAllOut #-}
270filterAllOut :: MonadIO m => Int -> Int -> Stream m Int -> m ()
271filterAllOut value n = composeN n $ Stream.filter (> (value + 1))
273{-# INLINE filterAllIn #-}
274filterAllIn :: MonadIO m => Int -> Int -> Stream m Int -> m ()
275filterAllIn value n = composeN n $ Stream.filter (<= (value + 1))
277{-# INLINE filterMEven #-}
278filterMEven :: MonadIO m => Int -> Stream m Int -> m ()
279filterMEven n = composeN n $ Stream.filterM (return . even)
281{-# INLINE filterMAllOut #-}
282filterMAllOut :: MonadIO m => Int -> Int -> Stream m Int -> m ()
283filterMAllOut value n = composeN n $ Stream.filterM (\x -> return $ x > (value + 1))
285{-# INLINE filterMAllIn #-}
286filterMAllIn :: MonadIO m => Int -> Int -> Stream m Int -> m ()
287filterMAllIn value n = composeN n $ Stream.filterM (\x -> return $ x <= (value + 1))
289{-# INLINE _takeOne #-}
290_takeOne :: MonadIO m => Int -> Stream m Int -> m ()
291_takeOne n = composeN n $ Stream.take 1
293{-# INLINE takeAll #-}
294takeAll :: MonadIO m => Int -> Int -> Stream m Int -> m ()
295takeAll value n = composeN n $ Stream.take (value + 1)
297{-# INLINE takeWhileTrue #-}
298takeWhileTrue :: MonadIO m => Int -> Int -> Stream m Int -> m ()
299takeWhileTrue value n = composeN n $ Stream.takeWhile (<= (value + 1))
301{-# INLINE takeWhileMTrue #-}
302takeWhileMTrue :: MonadIO m => Int -> Int -> Stream m Int -> m ()
303takeWhileMTrue value n = composeN n $ Stream.takeWhileM (return . (<= (value + 1)))
305{-# INLINE takeInterval #-}
306takeInterval :: Double -> Int -> Stream IO Int -> IO ()
307takeInterval i n = composeN n (Stream.takeInterval i)
309-- Inspection testing is disabled for takeInterval
310-- Enable it when looking at it throughly
312-- inspect $ hasNoType 'takeInterval ''SPEC
313-- inspect $ hasNoTypeClasses 'takeInterval
314-- inspect $ 'takeInterval `hasNoType` ''D.Step
317{-# INLINE dropOne #-}
318dropOne :: MonadIO m => Int -> Stream m Int -> m ()
319dropOne n = composeN n $ Stream.drop 1
321{-# INLINE dropAll #-}
322dropAll :: MonadIO m => Int -> Int -> Stream m Int -> m ()
323dropAll value n = composeN n $ Stream.drop (value + 1)
325{-# INLINE dropWhileTrue #-}
326dropWhileTrue :: MonadIO m => Int -> Int -> Stream m Int -> m ()
327dropWhileTrue value n = composeN n $ Stream.dropWhile (<= (value + 1))
329{-# INLINE dropWhileMTrue #-}
330dropWhileMTrue :: MonadIO m => Int -> Int -> Stream m Int -> m ()
331dropWhileMTrue value n = composeN n $ Stream.dropWhileM (return . (<= (value + 1)))
333{-# INLINE dropWhileFalse #-}
334dropWhileFalse :: MonadIO m => Int -> Int -> Stream m Int -> m ()
335dropWhileFalse value n = composeN n $ Stream.dropWhile (> (value + 1))
338-- XXX Decide on the time interval
339{-# INLINE _intervalsOfSum #-}
340_intervalsOfSum :: MonadAsync m => Double -> Int -> Stream m Int -> m ()
341_intervalsOfSum i n = composeN n (Stream.intervalsOf i FL.sum)
344{-# INLINE dropInterval #-}
345dropInterval :: Double -> Int -> Stream IO Int -> IO ()
346dropInterval i n = composeN n (Stream.dropInterval i)
348-- Inspection testing is disabled for dropInterval
349-- Enable it when looking at it throughly
351-- inspect $ hasNoTypeClasses 'dropInterval
352-- inspect $ 'dropInterval `hasNoType` ''D.Step
355{-# INLINE findIndices #-}
356findIndices :: MonadIO m => Int -> Int -> Stream m Int -> m ()
357findIndices value n = composeN n $ Stream.findIndices (== (value + 1))
359{-# INLINE elemIndices #-}
360elemIndices :: MonadIO m => Int -> Int -> Stream m Int -> m ()
361elemIndices value n = composeN n $ Stream.elemIndices (value + 1)
363{-# INLINE deleteBy #-}
364deleteBy :: MonadIO m => Int -> Int -> Stream m Int -> m ()
365deleteBy value n = composeN n $ Stream.deleteBy (>=) (value + 1)
367-- uniq . uniq == uniq, composeN 2 ~ composeN 1
369uniq :: MonadIO m => Int -> Stream m Int -> m ()
370uniq n = composeN n Stream.uniq
372{-# INLINE mapMaybe #-}
373mapMaybe :: MonadIO m => Int -> Stream m Int -> m ()
382{-# INLINE mapMaybeM #-}
383mapMaybeM :: MonadAsync m => Int -> Stream m Int -> m ()
390 else return $ Just x)
392o_1_space_filtering :: Int -> [Benchmark]
393o_1_space_filtering value =
395 [ benchIOSink value "filter-even" (filterEven 1)
396 , benchIOSink value "filter-all-out" (filterAllOut value 1)
397 , benchIOSink value "filter-all-in" (filterAllIn value 1)
399 , benchIOSink value "filterM-even" (filterMEven 1)
400 , benchIOSink value "filterM-all-out" (filterMAllOut value 1)
401 , benchIOSink value "filterM-all-in" (filterMAllIn value 1)
404 , benchIOSink value "take-all" (takeAll value 1)
405 , benchIOSink value "takeWhile-true" (takeWhileTrue value 1)
406 -- , benchIOSink value "takeWhileM-true" (_takeWhileMTrue value 1)
407 , benchIOSink value "drop-one" (dropOne 1)
408 , benchIOSink value "drop-all" (dropAll value 1)
409 , benchIOSink value "takeInterval-all" (takeInterval 10000 1)
410 , benchIOSink value "dropInterval-all" (dropInterval 10000 1)
411 , benchIOSink value "dropWhile-true" (dropWhileTrue value 1)
412 -- , benchIOSink value "dropWhileM-true" (_dropWhileMTrue value 1)
416 (dropWhileFalse value 1)
417 , benchIOSink value "deleteBy" (deleteBy value 1)
419 , benchIOSink value "uniq" (uniq 1)
422 , benchIOSink value "mapMaybe" (mapMaybe 1)
423 , benchIOSink value "mapMaybeM" (mapMaybeM 1)
425 -- Searching (stateful map and filter)
426 , benchIOSink value "findIndices" (findIndices value 1)
427 , benchIOSink value "elemIndices" (elemIndices value 1)
431o_1_space_filteringX4 :: Int -> [Benchmark]
432o_1_space_filteringX4 value =
433 [ bgroup "filteringX4"
434 [ benchIOSink value "filter-even" (filterEven 4)
435 , benchIOSink value "filter-all-out" (filterAllOut value 4)
436 , benchIOSink value "filter-all-in" (filterAllIn value 4)
438 , benchIOSink value "filterM-even" (filterMEven 4)
439 , benchIOSink value "filterM-all-out" (filterMAllOut value 4)
440 , benchIOSink value "filterM-all-in" (filterMAllIn value 4)
443 , benchIOSink value "take-all" (takeAll value 4)
444 , benchIOSink value "takeWhile-true" (takeWhileTrue value 4)
445 , benchIOSink value "takeWhileM-true" (takeWhileMTrue value 4)
446 , benchIOSink value "drop-one" (dropOne 4)
447 , benchIOSink value "drop-all" (dropAll value 4)
448 , benchIOSink value "dropWhile-true" (dropWhileTrue value 4)
449 , benchIOSink value "dropWhileM-true" (dropWhileMTrue value 4)
453 (dropWhileFalse value 4)
454 , benchIOSink value "deleteBy" (deleteBy value 4)
456 , benchIOSink value "uniq" (uniq 4)
459 , benchIOSink value "mapMaybe" (mapMaybe 4)
460 , benchIOSink value "mapMaybeM" (mapMaybeM 4)
463 , benchIOSink value "findIndices" (findIndices value 4)
464 , benchIOSink value "elemIndices" (elemIndices value 4)
468-------------------------------------------------------------------------------
469-- Size increasing transformations (insertions)
470-------------------------------------------------------------------------------
472{-# INLINE intersperse #-}
473intersperse :: MonadAsync m => Int -> Int -> Stream m Int -> m ()
474intersperse value n = composeN n $ Stream.intersperse (value + 1)
476{-# INLINE intersperseM #-}
477intersperseM :: MonadAsync m => Int -> Int -> Stream m Int -> m ()
478intersperseM value n = composeN n $ Stream.intersperseM (return $ value + 1)
480{-# INLINE insertBy #-}
481insertBy :: MonadIO m => Int -> Int -> Stream m Int -> m ()
482insertBy value n = composeN n $ Stream.insertBy compare (value + 1)
484{-# INLINE interposeSuffix #-}
485interposeSuffix :: Monad m => Int -> Int -> Stream m Int -> m ()
486interposeSuffix value n =
487 composeN n $ Stream.interposeSuffix (value + 1) Unfold.identity
489{-# INLINE intercalateSuffix #-}
490intercalateSuffix :: Monad m => Int -> Int -> Stream m Int -> m ()
491intercalateSuffix value n =
492 composeN n $ Stream.intercalateSuffix Unfold.identity (value + 1)
494o_1_space_inserting :: Int -> [Benchmark]
495o_1_space_inserting value =
497 [ benchIOSink value "intersperse" (intersperse value 1)
498 , benchIOSink value "intersperseM" (intersperseM value 1)
499 , benchIOSink value "insertBy" (insertBy value 1)
500 , benchIOSink value "interposeSuffix" (interposeSuffix value 1)
501 , benchIOSink value "intercalateSuffix" (intercalateSuffix value 1)
505o_1_space_insertingX4 :: Int -> [Benchmark]
506o_1_space_insertingX4 value =
507 [ bgroup "insertingX4"
508 [ benchIOSink value "intersperse" (intersperse value 4)
509 , benchIOSink value "insertBy" (insertBy value 4)
513-------------------------------------------------------------------------------
515-------------------------------------------------------------------------------
517{-# INLINE indexed #-}
518indexed :: MonadIO m => Int -> Stream m Int -> m ()
519indexed n = composeN n (fmap snd . Stream.indexed)
521{-# INLINE indexedR #-}
522indexedR :: MonadIO m => Int -> Int -> Stream m Int -> m ()
523indexedR value n = composeN n (fmap snd . Stream.indexedR value)
525o_1_space_indexing :: Int -> [Benchmark]
526o_1_space_indexing value =
528 [ benchIOSink value "indexed" (indexed 1)
529 , benchIOSink value "indexedR" (indexedR value 1)
533o_1_space_indexingX4 :: Int -> [Benchmark]
534o_1_space_indexingX4 value =
535 [ bgroup "indexingx4"
536 [ benchIOSink value "indexed" (indexed 4)
537 , benchIOSink value "indexedR" (indexedR value 4)
541-------------------------------------------------------------------------------
543-------------------------------------------------------------------------------
545-- In addition to gauge options, the number of elements in the stream can be
546-- passed using the --stream-size option.
548benchmarks :: String -> Int -> [Benchmark]
549benchmarks moduleName size =
550 [ bgroup (o_1_space_prefix moduleName) $ Prelude.concat
551 [ o_1_space_functor size
552 , o_1_space_mapping size
553 , o_1_space_mappingX4 size
554 , o_1_space_filtering size
555 , o_1_space_filteringX4 size
556 , o_1_space_inserting size
557 , o_1_space_insertingX4 size
558 , o_1_space_indexing size
559 , o_1_space_indexingX4 size
561 , bgroup (o_n_space_prefix moduleName) $ Prelude.concat
562 [ o_n_space_traversable size
563 , o_n_space_mapping size
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list