2-- Module : Streamly.Benchmark.Data.Fold
3-- Copyright : (c) 2018 Composewell
5-- Maintainer : streamly@composewell.com
7{-# LANGUAGE FlexibleContexts #-}
9{-# LANGUAGE ScopedTypeVariables #-}
11#ifdef __HADDOCK_VERSION__
16{-# LANGUAGE TemplateHaskell #-}
17{-# OPTIONS_GHC -fplugin Test.Inspection.Plugin #-}
20module Main (main) where
22import Control.DeepSeq (NFData(..))
23import Control.Exception (SomeException)
24import Streamly.Internal.Data.Unfold (Unfold)
25import System.IO (Handle, hClose)
26import System.Random (randomRIO)
28import qualified Prelude
29import qualified Streamly.Benchmark.Data.NestedUnfoldOps as Nested
30import qualified Streamly.FileSystem.Handle as FH
31import qualified Streamly.Internal.Data.Fold as FL
32import qualified Streamly.Internal.Data.Unfold as UF
33import qualified Streamly.Internal.Data.Stream.IsStream as S
34import qualified Streamly.Internal.Data.Stream.StreamD as D
35import qualified Streamly.Internal.Data.Stream.StreamK as K
36import qualified Streamly.Prelude as SP
38import Gauge hiding (env)
39import Prelude hiding (concat, take, filter, zipWith, map, mapM, takeWhile)
40import Streamly.Benchmark.Common
41import Streamly.Benchmark.Common.Handle
48benchIO :: (NFData b) => String -> (Int -> IO b) -> Benchmark
49benchIO name f = bench name $ nfIO $ randomRIO (1,1) >>= f
51-------------------------------------------------------------------------------
53-------------------------------------------------------------------------------
55{-# INLINE drainGeneration #-}
56drainGeneration :: Monad m => Unfold m a b -> a -> m ()
57drainGeneration unf seed = UF.fold unf FL.drain seed
59{-# INLINE drainTransformation #-}
61 Monad m => Unfold m a b -> (Unfold m a b -> Unfold m c d) -> c -> m ()
62drainTransformation unf f seed = drainGeneration (f unf) seed
64{-# INLINE drainTransformationDefault #-}
65drainTransformationDefault ::
66 Monad m => Int -> (Unfold m Int Int -> Unfold m c d) -> c -> m ()
67drainTransformationDefault to =
68 drainTransformation (UF.enumerateFromToIntegral to)
70{-# INLINE drainProduct #-}
75 -> (Unfold m a b -> Unfold m c d -> Unfold m e f)
78drainProduct unf1 unf2 f seed = drainGeneration (f unf1 unf2) seed
80{-# INLINE drainProductDefault #-}
84 -> (Unfold m Int Int -> Unfold m Int Int -> Unfold m e f)
87drainProductDefault to = drainProduct src src
91 src = UF.enumerateFromToIntegral to
93-------------------------------------------------------------------------------
95-------------------------------------------------------------------------------
98lmap :: Monad m => Int -> Int -> m ()
100 drainTransformationDefault (size + start) (UF.lmap (+ 1)) start
103lmapM :: Monad m => Int -> Int -> m ()
105 drainTransformationDefault (size + start) (UF.lmapM (return . (+) 1)) start
108supply :: Monad m => Int -> Int -> m ()
110 drainTransformationDefault (size + start) (flip UF.supply start) undefined
113{-# INLINE supplyFirst #-}
114supplyFirst :: Monad m => Int -> Int -> m ()
115supplyFirst size start =
117 (UF.take size UF.enumerateFromStepIntegral)
118 (flip UF.supplyFirst start)
121{-# INLINE supplySecond #-}
122supplySecond :: Monad m => Int -> Int -> m ()
123supplySecond size start =
125 (UF.take size UF.enumerateFromStepIntegral)
126 (flip UF.supplySecond 1)
129{-# INLINE discardFirst #-}
130discardFirst :: Monad m => Int -> Int -> m ()
131discardFirst size start =
132 drainTransformationDefault (size + start) UF.discardFirst (start, start)
134{-# INLINE discardSecond #-}
135discardSecond :: Monad m => Int -> Int -> m ()
136discardSecond size start =
137 drainTransformationDefault (size + start) UF.discardSecond (start, start)
140swap :: Monad m => Int -> Int -> m ()
143 (UF.take size UF.enumerateFromStepIntegral)
147-------------------------------------------------------------------------------
149-------------------------------------------------------------------------------
151{-# INLINE fromStream #-}
152fromStream :: Int -> Int -> IO ()
153fromStream size start =
154 drainGeneration UF.fromStream (S.replicate size start :: S.SerialT IO Int)
156-- XXX INVESTIGATE: Although the performance of this should be equivalant to
157-- fromStream, this is considerably worse. More than 4x worse.
158{-# INLINE fromStreamK #-}
159fromStreamK :: Monad m => Int -> Int -> m ()
160fromStreamK size start = drainGeneration UF.fromStreamK (K.replicate size start)
162{-# INLINE fromStreamD #-}
163fromStreamD :: Monad m => Int -> Int -> m ()
164fromStreamD size start =
165 drainGeneration UF.fromStreamD (D.replicate size start)
168_nilM :: Monad m => Int -> Int -> m ()
169_nilM _ start = drainGeneration (UF.nilM return) start
172consM :: Monad m => Int -> Int -> m ()
174 drainTransformationDefault (size + start) (UF.consM return) start
176{-# INLINE _effect #-}
177_effect :: Monad m => Int -> Int -> m ()
179 drainGeneration (UF.effect (return start)) undefined
181{-# INLINE _singletonM #-}
182_singletonM :: Monad m => Int -> Int -> m ()
183_singletonM _ start = drainGeneration (UF.singletonM return) start
185{-# INLINE _singleton #-}
186_singleton :: Monad m => Int -> Int -> m ()
187_singleton _ start = drainGeneration (UF.singleton id) start
189{-# INLINE _identity #-}
190_identity :: Monad m => Int -> Int -> m ()
191_identity _ start = drainGeneration UF.identity start
194_const :: Monad m => Int -> Int -> m ()
196 drainGeneration (UF.take size (UF.const (return start))) undefined
198{-# INLINE unfoldrM #-}
199unfoldrM :: Monad m => Int -> Int -> m ()
200unfoldrM size start = drainGeneration (UF.unfoldrM step) start
206 $ if i < start + size
210{-# INLINE fromList #-}
211fromList :: Monad m => Int -> Int -> m ()
212fromList size start = drainGeneration UF.fromList [start .. start + size]
214{-# INLINE fromListM #-}
215fromListM :: Monad m => Int -> Int -> m ()
216fromListM size start =
217 drainGeneration UF.fromListM (Prelude.map return [start .. start + size])
219{-# INLINE _fromSVar #-}
220_fromSVar :: Int -> Int -> m ()
223{-# INLINE _fromProducer #-}
224_fromProducer :: Int -> Int -> m ()
225_fromProducer = undefined
227{-# INLINE replicateM #-}
228replicateM :: Monad m => Int -> Int -> m ()
229replicateM size start = drainGeneration (UF.replicateM size) (return start)
231{-# INLINE repeatM #-}
232repeatM :: Monad m => Int -> Int -> m ()
233repeatM size start = drainGeneration (UF.take size UF.repeatM) (return start)
235{-# INLINE iterateM #-}
236iterateM :: Monad m => Int -> Int -> m ()
238 drainGeneration (UF.take size (UF.iterateM return)) (return start)
240{-# INLINE fromIndicesM #-}
241fromIndicesM :: Monad m => Int -> Int -> m ()
242fromIndicesM size start =
243 drainGeneration (UF.take size (UF.fromIndicesM return)) start
245{-# INLINE enumerateFromStepIntegral #-}
246enumerateFromStepIntegral :: Monad m => Int -> Int -> m ()
247enumerateFromStepIntegral size start =
248 drainGeneration (UF.take size UF.enumerateFromStepIntegral) (start, 1)
250{-# INLINE enumerateFromToIntegral #-}
251enumerateFromToIntegral :: Monad m => Int -> Int -> m ()
252enumerateFromToIntegral size start =
253 drainGeneration (UF.enumerateFromToIntegral (size + start)) start
255{-# INLINE enumerateFromIntegral #-}
256enumerateFromIntegral :: Monad m => Int -> Int -> m ()
257enumerateFromIntegral size start =
258 drainGeneration (UF.take size UF.enumerateFromIntegral) start
260{-# INLINE enumerateFromStepNum #-}
261enumerateFromStepNum :: Monad m => Int -> Int -> m ()
262enumerateFromStepNum size start =
263 drainGeneration (UF.take size (UF.enumerateFromStepNum 1)) start
265{-# INLINE numFrom #-}
266numFrom :: Monad m => Int -> Int -> m ()
267numFrom size start = drainGeneration (UF.take size UF.numFrom) start
269{-# INLINE enumerateFromToFractional #-}
270enumerateFromToFractional :: Monad m => Int -> Int -> m ()
271enumerateFromToFractional size start =
272 let intToDouble x = (fromInteger (fromIntegral x)) :: Double
274 (UF.enumerateFromToFractional (intToDouble $ start + size))
277-------------------------------------------------------------------------------
278-- Stream transformation
279-------------------------------------------------------------------------------
282map :: Monad m => Int -> Int -> m ()
283map size start = drainTransformationDefault (size + start) (UF.map (+1)) start
286mapM :: Monad m => Int -> Int -> m ()
288 drainTransformationDefault (size + start) (UF.mapM (return . (+) 1)) start
290{-# INLINE mapMWithInput #-}
291mapMWithInput :: Monad m => Int -> Int -> m ()
292mapMWithInput size start =
293 drainTransformationDefault
295 (UF.mapMWithInput (\a b -> return $ a + b))
298-------------------------------------------------------------------------------
300-------------------------------------------------------------------------------
302{-# INLINE takeWhileM #-}
303takeWhileM :: Monad m => Int -> Int -> m ()
304takeWhileM size start =
305 drainTransformationDefault
307 (UF.takeWhileM (\b -> return (b <= size + start)))
310{-# INLINE takeWhile #-}
311takeWhile :: Monad m => Int -> Int -> m ()
312takeWhile size start =
313 drainTransformationDefault
315 (UF.takeWhile (\b -> b <= size + start))
319take :: Monad m => Int -> Int -> m ()
320take size start = drainTransformationDefault (size + start) (UF.take size) start
323filter :: Monad m => Int -> Int -> m ()
325 drainTransformationDefault (size + start) (UF.filter (\_ -> True)) start
327{-# INLINE filterM #-}
328filterM :: Monad m => Int -> Int -> m ()
330 drainTransformationDefault
332 (UF.filterM (\_ -> (return True)))
335{-# INLINE _dropOne #-}
336_dropOne :: Monad m => Int -> Int -> m ()
338 drainTransformationDefault (size + start) (UF.drop 1) start
340{-# INLINE dropAll #-}
341dropAll :: Monad m => Int -> Int -> m ()
343 drainTransformationDefault (size + start) (UF.drop (size + 1)) start
345{-# INLINE dropWhileTrue #-}
346dropWhileTrue :: Monad m => Int -> Int -> m ()
347dropWhileTrue size start =
348 drainTransformationDefault
350 (UF.dropWhileM (\_ -> return True))
353{-# INLINE dropWhileFalse #-}
354dropWhileFalse :: Monad m => Int -> Int -> m ()
355dropWhileFalse size start =
356 drainTransformationDefault
358 (UF.dropWhileM (\_ -> return False))
361{-# INLINE dropWhileMTrue #-}
362dropWhileMTrue :: Monad m => Int -> Int -> m ()
363dropWhileMTrue size start =
364 drainTransformationDefault
366 (UF.dropWhileM (\_ -> return True))
369{-# INLINE dropWhileMFalse #-}
370dropWhileMFalse :: Monad m => Int -> Int -> m ()
371dropWhileMFalse size start =
372 drainTransformationDefault
374 (UF.dropWhileM (\_ -> return False))
377-------------------------------------------------------------------------------
379-------------------------------------------------------------------------------
381{-# INLINE zipWith #-}
382zipWith :: Monad m => Int -> Int -> m ()
384 drainProductDefault (size + start) (UF.zipWith (+)) (start, start + 1)
386{-# INLINE zipWithM #-}
387zipWithM :: Monad m => Int -> Int -> m ()
391 (UF.zipWithM (\a b -> return $ a + b))
394{-# INLINE teeZipWith #-}
395teeZipWith :: Monad m => Int -> Int -> m ()
396teeZipWith size start =
397 drainProductDefault (size + start) (UF.teeZipWith (+)) start
399-------------------------------------------------------------------------------
401-------------------------------------------------------------------------------
403{-# INLINE concatMapM #-}
404concatMapM :: Monad m => Int -> Int -> m ()
405concatMapM size start =
406 drainGeneration (UF.concatMapM unfoldInGen unfoldOut) start
411 sizeInner = size `div` sizeOuter
415 $ UF.supply (UF.enumerateFromToIntegral (i + sizeInner)) i
417 unfoldOut = UF.enumerateFromToIntegral (start + sizeOuter)
419{-# INLINE toNullAp #-}
420toNullAp :: Monad m => Int -> Int -> m ()
421toNullAp linearCount start =
422 let end = start + Nested.nestedCount2 linearCount
423 s = Nested.source end
424 in UF.fold ((+) <$> s <*> s) FL.drain start
426{-# INLINE _apDiscardFst #-}
427_apDiscardFst :: Int -> Int -> m ()
428_apDiscardFst = undefined
430{-# INLINE _apDiscardSnd #-}
431_apDiscardSnd :: Int -> Int -> m ()
432_apDiscardSnd = undefined
434-------------------------------------------------------------------------------
436-------------------------------------------------------------------------------
439moduleName = "Data.Unfold"
441o_1_space_transformation_input :: Int -> [Benchmark]
442o_1_space_transformation_input size =
444 "transformation/input"
445 [ benchIO "lmap" $ lmap size
446 , benchIO "lmapM" $ lmapM size
447 , benchIO "supply" $ supply size
448 , benchIO "supplyFirst" $ supplyFirst size
449 , benchIO "supplySecond" $ supplySecond size
450 , benchIO "discardFirst" $ discardFirst size
451 , benchIO "discardSecond" $ discardSecond size
452 , benchIO "swap" $ swap size
456o_1_space_generation :: Int -> [Benchmark]
457o_1_space_generation size =
460 [ benchIO "fromStream" $ fromStream size
461 , benchIO "fromStreamK" $ fromStreamK size
462 , benchIO "fromStreamD (n/100)" $ fromStreamD (size `div` 100)
463 -- Very small benchmarks, reporting in ns
464 -- , benchIO "nilM" $ nilM size
465 , benchIO "consM" $ consM size
466 -- , benchIO "effect" $ effect size
467 -- , benchIO "singletonM" $ singletonM size
468 -- , benchIO "singleton" $ singleton size
469 -- , benchIO "identity" $ identity size
470 -- , benchIO "const" $ const size
471 , benchIO "unfoldrM" $ unfoldrM size
472 , benchIO "fromList" $ fromList size
473 , benchIO "fromListM" $ fromListM size
475 -- , benchIO "fromSVar" $ fromSVar size
476 -- , benchIO "fromProducer" $ fromProducer size
477 , benchIO "replicateM" $ replicateM size
478 , benchIO "repeatM" $ repeatM size
479 , benchIO "iterateM" $ iterateM size
480 , benchIO "fromIndicesM" $ fromIndicesM size
481 , benchIO "enumerateFromStepIntegral" $ enumerateFromStepIntegral size
482 , benchIO "enumerateFromToIntegral" $ enumerateFromToIntegral size
483 , benchIO "enumerateFromIntegral" $ enumerateFromIntegral size
484 , benchIO "enumerateFromStepNum" $ enumerateFromStepNum size
485 , benchIO "numFrom" $ numFrom size
486 , benchIO "enumerateFromToFractional" $ enumerateFromToFractional size
490o_1_space_transformation :: Int -> [Benchmark]
491o_1_space_transformation size =
494 [ benchIO "map" $ map size
495 , benchIO "mapM" $ mapM size
496 , benchIO "mapMWithInput" $ mapMWithInput size
500o_1_space_filtering :: Int -> [Benchmark]
501o_1_space_filtering size =
504 [ benchIO "takeWhileM" $ takeWhileM size
505 , benchIO "takeWhile" $ takeWhile size
506 , benchIO "take" $ take size
507 , benchIO "filter" $ filter size
508 , benchIO "filterM" $ filterM size
509 -- Very small benchmark, reporting in ns
510 -- , benchIO "dropOne" $ dropOne size
511 , benchIO "dropAll" $ dropAll size
512 , benchIO "dropWhileTrue" $ dropWhileTrue size
513 , benchIO "dropWhileFalse" $ dropWhileFalse size
514 , benchIO "dropWhileMTrue" $ dropWhileMTrue size
515 , benchIO "dropWhileMFalse" $ dropWhileMFalse size
519o_1_space_zip :: Int -> [Benchmark]
523 [ benchIO "zipWithM" $ zipWithM size
524 , benchIO "zipWith" $ zipWith size
525 , benchIO "teeZipWith" $ teeZipWith size
529o_1_space_nested :: Int -> [Benchmark]
530o_1_space_nested size =
533 [ benchIO "toNullAp" $ toNullAp size
534 , benchIO "toNull" $ Nested.toNull size
535 , benchIO "toNull3" $ Nested.toNull3 size
536 , benchIO "concat" $ Nested.concat size
537 , benchIO "breakAfterSome" $ Nested.breakAfterSome size
538 , benchIO "filterAllOut" $ Nested.filterAllOut size
539 , benchIO "filterAllIn" $ Nested.filterAllIn size
540 , benchIO "filterSome" $ Nested.filterSome size
541 , benchIO "concatMapM (100 x n/100)" $ concatMapM size
543 -- , benchIO "ap" $ ap size
544 -- , benchIO "apDiscardFst" $ apDiscardFst size
545 -- , benchIO "apDiscardSnd" $ apDiscardSnd size
549o_n_space_nested :: Int -> [Benchmark]
550o_n_space_nested size =
553 [ benchIO "toList" $ Nested.toList size
554 , benchIO "toListSome" $ Nested.toListSome size
558-------------------------------------------------------------------------------
559-- Unfold Exception Benchmarks
560-------------------------------------------------------------------------------
561-- | Send the file contents to /dev/null with exception handling
562readWriteOnExceptionUnfold :: Handle -> Handle -> IO ()
563readWriteOnExceptionUnfold inh devNull =
564 let readEx = UF.onException (\_ -> hClose inh) FH.read
565 in SP.fold (FH.write devNull) $ SP.unfold readEx inh
568inspect $ hasNoTypeClasses 'readWriteOnExceptionUnfold
569-- inspect $ 'readWriteOnExceptionUnfold `hasNoType` ''Step
572-- | Send the file contents to /dev/null with exception handling
573readWriteHandleExceptionUnfold :: Handle -> Handle -> IO ()
574readWriteHandleExceptionUnfold inh devNull =
575 let handler (_e :: SomeException) = hClose inh >> return 10
576 readEx = UF.handle (UF.singletonM handler) FH.read
577 in SP.fold (FH.write devNull) $ SP.unfold readEx inh
580inspect $ hasNoTypeClasses 'readWriteHandleExceptionUnfold
581-- inspect $ 'readWriteHandleExceptionUnfold `hasNoType` ''Step
584-- | Send the file contents to /dev/null with exception handling
585readWriteFinally_Unfold :: Handle -> Handle -> IO ()
586readWriteFinally_Unfold inh devNull =
587 let readEx = UF.finally_ (\_ -> hClose inh) FH.read
588 in SP.fold (FH.write devNull) $ SP.unfold readEx inh
591inspect $ hasNoTypeClasses 'readWriteFinally_Unfold
592-- inspect $ 'readWriteFinallyUnfold `hasNoType` ''Step
595readWriteFinallyUnfold :: Handle -> Handle -> IO ()
596readWriteFinallyUnfold inh devNull =
597 let readEx = UF.finally (\_ -> hClose inh) FH.read
598 in SP.fold (FH.write devNull) $ SP.unfold readEx inh
600-- | Send the file contents to /dev/null with exception handling
601readWriteBracket_Unfold :: Handle -> Handle -> IO ()
602readWriteBracket_Unfold inh devNull =
603 let readEx = UF.bracket_ return (\_ -> hClose inh) FH.read
604 in SP.fold (FH.write devNull) $ SP.unfold readEx inh
607inspect $ hasNoTypeClasses 'readWriteBracket_Unfold
608-- inspect $ 'readWriteBracketUnfold `hasNoType` ''Step
611readWriteBracketUnfold :: Handle -> Handle -> IO ()
612readWriteBracketUnfold inh devNull =
613 let readEx = UF.bracket return (\_ -> hClose inh) FH.read
614 in SP.fold (FH.write devNull) $ S.unfold readEx inh
616o_1_space_copy_read_exceptions :: BenchEnv -> [Benchmark]
617o_1_space_copy_read_exceptions env =
618 [ bgroup "exceptions"
619 [ mkBenchSmall "UF.onException" env $ \inh _ ->
620 readWriteOnExceptionUnfold inh (nullH env)
621 , mkBenchSmall "UF.handle" env $ \inh _ ->
622 readWriteHandleExceptionUnfold inh (nullH env)
623 , mkBenchSmall "UF.finally_" env $ \inh _ ->
624 readWriteFinally_Unfold inh (nullH env)
625 , mkBenchSmall "UF.finally" env $ \inh _ ->
626 readWriteFinallyUnfold inh (nullH env)
627 , mkBenchSmall "UF.bracket_" env $ \inh _ ->
628 readWriteBracket_Unfold inh (nullH env)
629 , mkBenchSmall "UF.bracket" env $ \inh _ ->
630 readWriteBracketUnfold inh (nullH env)
635-------------------------------------------------------------------------------
637-------------------------------------------------------------------------------
641 (size, cfg, benches) <- parseCLIOpts defaultStreamSize
642 env <- mkHandleBenchEnv
643 size `seq` runMode (mode cfg) cfg benches (allBenchmarks size env)
647 allBenchmarks size env =
648 [ bgroup (o_1_space_prefix moduleName)
650 [ o_1_space_transformation_input size
651 , o_1_space_generation size
652 , o_1_space_transformation size
653 , o_1_space_filtering size
655 , o_1_space_nested size
656 , o_1_space_copy_read_exceptions env
658 , bgroup (o_n_space_prefix moduleName)
659 $ Prelude.concat [o_n_space_nested 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