packages feed

streamly-0.11.0: benchmark/Streamly/Benchmark/Data/Stream/Expand.hs

-- |
-- Module      : Stream.Expand
-- Copyright   : (c) 2018 Composewell Technologies
-- License     : BSD-3-Clause
-- Maintainer  : streamly@composewell.com

{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RankNTypes #-}

#ifdef USE_PRELUDE
#endif

#ifdef __HADDOCK_VERSION__
#undef INSPECTION
#endif

#ifdef INSPECTION
{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS_GHC -fplugin Test.Inspection.Plugin #-}
#endif

module Stream.Expand (benchmarks) where

#ifdef INSPECTION
import GHC.Types (SPEC(..))
import Test.Inspection

import qualified Streamly.Internal.Data.Stream as D
#endif

import qualified Stream.Common as Common
import qualified Streamly.Internal.Data.Unfold as UF

#ifdef USE_PRELUDE
import qualified Streamly.Internal.Data.Stream.IsStream as S
import Streamly.Benchmark.Prelude
    ( sourceFoldMapM, sourceFoldMapWith, sourceFoldMapWithM
    , sourceFoldMapWithStream, concatFoldableWith, concatForFoldableWith)
#else
import Streamly.Data.Stream (Stream)
import Streamly.Data.Unfold (Unfold)
import qualified Streamly.Internal.Data.Stream as S
import qualified Streamly.Internal.Data.Unfold as Unfold
import qualified Streamly.Internal.Data.Fold as Fold
import qualified Streamly.Internal.Data.Stream as Stream
import qualified Streamly.Internal.Data.StreamK as StreamK
#endif

import Test.Tasty.Bench
import Stream.Common
import Streamly.Benchmark.Common
import Prelude hiding (concatMap)

-------------------------------------------------------------------------------
-- Multi-Stream
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- Appending
-------------------------------------------------------------------------------

{-# INLINE serial2 #-}
serial2 :: Int -> Int -> IO ()
serial2 count n =
    drain $
        Common.append
            (sourceUnfoldrM count n)
            (sourceUnfoldrM count (n + 1))

{-# INLINE serial4 #-}
serial4 :: Int -> Int -> IO ()
serial4 count n =
    drain $
    Common.append
        (Common.append
            (sourceUnfoldrM count n)
            (sourceUnfoldrM count (n + 1)))
        (Common.append
              (sourceUnfoldrM count (n + 2))
              (sourceUnfoldrM count (n + 3)))

o_1_space_joining :: Int -> [Benchmark]
o_1_space_joining value =
    [ bgroup "joining"
        [ benchIOSrc1 "serial (2,x/2)" (serial2 (value `div` 2))
        , benchIOSrc1 "serial (2,2,x/4)" (serial4 (value `div` 4))
        ]
    ]

-------------------------------------------------------------------------------
-- Concat Foldable containers
-------------------------------------------------------------------------------

#ifdef USE_PRELUDE
o_1_space_concatFoldable :: Int -> [Benchmark]
o_1_space_concatFoldable value =
    [ bgroup "concat-foldable"
        [ benchIOSrc "foldMapWith (<>) (List)"
            (sourceFoldMapWith value)
        , benchIOSrc "foldMapWith (<>) (Stream)"
            (sourceFoldMapWithStream value)
        , benchIOSrc "foldMapWithM (<>) (List)"
            (sourceFoldMapWithM value)
        , benchIOSrc "S.concatFoldableWith (<>) (List)"
            (concatFoldableWith value)
        , benchIOSrc "S.concatForFoldableWith (<>) (List)"
            (concatForFoldableWith value)
        , benchIOSrc "foldMapM (List)" (sourceFoldMapM value)
        ]
    ]
#endif

-------------------------------------------------------------------------------
-- Concat
-------------------------------------------------------------------------------

-- concatMap unfoldrM/unfoldrM

{-# INLINE concatMap #-}
concatMap :: Int -> Int -> Int -> IO ()
concatMap outer inner n =
    drain $ S.concatMap
        (\_ -> sourceUnfoldrM inner n)
        (sourceUnfoldrM outer n)

{-# INLINE concatMapViaUnfoldEach #-}
concatMapViaUnfoldEach :: Int -> Int -> Int -> IO ()
concatMapViaUnfoldEach outer inner n =
    drain $ cmap
        (\_ -> sourceUnfoldrM inner n)
        (sourceUnfoldrM outer n)

    where

    cmap f = Stream.unfoldEach (UF.lmap f UF.fromStream)

{-# INLINE concatMapM #-}
concatMapM :: Int -> Int -> Int -> IO ()
concatMapM outer inner n =
    drain $ S.concatMapM
        (\_ -> return $ sourceUnfoldrM inner n)
        (sourceUnfoldrM outer n)

#ifdef INSPECTION
inspect $ hasNoTypeClasses 'concatMap
inspect $ 'concatMap `hasNoType` ''SPEC
#endif

-- concatMap unfoldr/unfoldr

{-# INLINE concatMapPure #-}
concatMapPure :: Int -> Int -> Int -> IO ()
concatMapPure outer inner n =
    drain $ S.concatMap
        (\_ -> sourceUnfoldr inner n)
        (sourceUnfoldr outer n)

#ifdef INSPECTION
#if __GLASGOW_HASKELL__ >= 906
inspect $ hasNoTypeClassesExcept 'concatMapPure [''Applicative]
#else
inspect $ hasNoTypeClasses 'concatMapPure
#endif
inspect $ 'concatMapPure `hasNoType` ''SPEC
#endif

{-# INLINE sourceUnfoldrMUnfold #-}
sourceUnfoldrMUnfold :: Monad m => Int -> Int -> Unfold m Int Int
sourceUnfoldrMUnfold size start = UF.unfoldrM step

    where

    step i =
        return
            $ if i < start + size
              then Just (i, i + 1)
              else Nothing

{-# INLINE unfoldEach #-}
unfoldEach :: Int -> Int -> Int -> IO ()
unfoldEach outer inner start = drain $
     -- XXX the replicateM takes much more time compared to unfoldrM, is there
     -- a perf issue or this is just because of accessing outer loop variables?
     -- S.unfoldEach (UF.lmap ((inner,) . return) UF.replicateM)
     S.unfoldEach (sourceUnfoldrMUnfold inner start)
        $ sourceUnfoldrM outer start

#ifdef INSPECTION
inspect $ hasNoTypeClasses 'unfoldEach
inspect $ 'unfoldEach `hasNoType` ''D.ConcatMapUState
inspect $ 'unfoldEach `hasNoType` ''SPEC
#endif

{-# INLINE unfoldEach2 #-}
unfoldEach2 :: Int -> Int -> Int -> IO ()
unfoldEach2 outer inner start = drain $
     S.unfoldEach (UF.carry (sourceUnfoldrMUnfold inner start))
        $ sourceUnfoldrM outer start

{-# INLINE unfoldEach3 #-}
unfoldEach3 :: Int -> Int -> IO ()
unfoldEach3 linearCount start = drain $ do
    S.unfoldEach (UF.carry (UF.lmap snd (sourceUnfoldrMUnfold nestedCount3 start)))
         $ S.unfoldEach (UF.carry (sourceUnfoldrMUnfold nestedCount3 start))
            $ sourceUnfoldrM nestedCount3 start
    where

    nestedCount3 = round (fromIntegral linearCount**(1/3::Double))

{-# INLINE unfoldCross #-}
unfoldCross :: Int -> Int -> Int -> IO ()
unfoldCross outer inner start = drain $
    Stream.unfoldCross
        UF.identity
        (sourceUnfoldrM outer start)
        (sourceUnfoldrM inner start)

o_1_space_concat :: Int -> [Benchmark]
o_1_space_concat value = sqrtVal `seq`
    [ bgroup "concat"
        [ benchIOSrc1 "concatMapPure outer=Max inner=1"
            (concatMapPure value 1)
        , benchIOSrc1 "concatMapPure outer=inner=(sqrt Max)"
            (concatMapPure sqrtVal sqrtVal)
        , benchIOSrc1 "concatMapPure outer=1 inner=Max"
            (concatMapPure 1 value)

        , benchIOSrc1 "concatMap outer=max inner=1"
            (concatMap value 1)
        , benchIOSrc1 "concatMap outer=inner=(sqrt Max)"
            (concatMap sqrtVal sqrtVal)
        , benchIOSrc1 "concatMap outer=1 inner=Max"
            (concatMap 1 value)

        -- This is for comparison with foldMapWith
        , benchIOSrc "concatMapId outer=max inner=1 (fromFoldable)"
            (S.concatMap id . sourceConcatMapId value)

        , benchIOSrc1 "concatMapM outer=max inner=1"
            (concatMapM value 1)
        , benchIOSrc1 "concatMapM outer=inner=(sqrt Max)"
            (concatMapM sqrtVal sqrtVal)
        , benchIOSrc1 "concatMapM outer=1 inner=Max"
            (concatMapM 1 value)

        , benchIOSrc1 "concatMapViaUnfoldEach outer=max inner=1"
            (concatMapViaUnfoldEach value 1)
        , benchIOSrc1 "concatMapViaUnfoldEach outer=inner=(sqrt Max)"
            (concatMapViaUnfoldEach sqrtVal sqrtVal)
        , benchIOSrc1 "concatMapViaUnfoldEach outer=1 inner=Max"
            (concatMapViaUnfoldEach 1 value)

        , benchIOSrc1 "unfoldCross outer=max inner=1"
            (unfoldCross value 1)
        , benchIOSrc1 "unfoldCross outer=inner=(sqrt Max)"
            (unfoldCross sqrtVal sqrtVal)
        , benchIOSrc1 "unfoldCross outer=1 inner=Max"
            (unfoldCross 1 value)

        -- concatMap vs unfoldEach
        , benchIOSrc1 "unfoldEach outer=Max inner=1"
            (unfoldEach value 1)
        , benchIOSrc1 "unfoldEach outer=inner=(sqrt Max)"
            (unfoldEach sqrtVal sqrtVal)
        , benchIOSrc1 "unfoldEach outer=1 inner=Max"
            (unfoldEach 1 value)

        , benchIOSrc1 "unfoldEach2 outer=Max inner=1"
            (unfoldEach2 value 1)
        , benchIOSrc1 "unfoldEach2 outer=inner=(sqrt Max)"
            (unfoldEach2 sqrtVal sqrtVal)
        , benchIOSrc1 "unfoldEach2 outer=1 inner=Max"
            (unfoldEach2 1 value)

        , benchIOSrc1 "unfoldEach3 outer=inner=(cubert Max)"
            (unfoldEach3 value)
        ]
    ]

    where

    sqrtVal = round $ sqrt (fromIntegral value :: Double)

-------------------------------------------------------------------------------
-- Applicative
-------------------------------------------------------------------------------

{-# INLINE cross2 #-}
cross2 :: MonadAsync m => Int -> Int -> m ()
cross2 linearCount start = drain $
    Stream.crossWith (+)
        (sourceUnfoldr nestedCount2 start)
        (sourceUnfoldr nestedCount2 start)

    where

    nestedCount2 = round (fromIntegral linearCount**(1/2::Double))

o_1_space_applicative :: Int -> [Benchmark]
o_1_space_applicative value =
    [ bgroup "Applicative"
        [ benchIO "(*>)" $ apDiscardFst value
        , benchIO "(<*)" $ apDiscardSnd value
        , benchIO "(<*>)" $ toNullAp value
        , benchIO "liftA2" $ apLiftA2 value
        , benchIO "pureDrain2" $ toNullApPure value
        , benchIO "pureCross2" $ cross2 value
        ]
    ]

-------------------------------------------------------------------------------
-- Monad
-------------------------------------------------------------------------------

o_1_space_monad :: Int -> [Benchmark]
o_1_space_monad value =
    [ bgroup "Monad"
        [ benchIO "then2" $ monadThen value
        , benchIO "drain2" $ toNullM value
        , benchIO "drain3" $ toNullM3 value
        , benchIO "filterAllOut2" $ filterAllOutM value
        , benchIO "filterAllIn2" $ filterAllInM value
        , benchIO "filterSome2" $ filterSome value
        , benchIO "breakAfterSome2" $ breakAfterSome value
        , benchIO "pureDrain2" $ toNullMPure value
        , benchIO "pureDrain3" $ toNullM3Pure value
        , benchIO "pureFilterAllIn2" $ filterAllInMPure value
        , benchIO "pureFilterAllOut2" $ filterAllOutMPure value
        ]
    ]

o_n_space_monad :: Int -> [Benchmark]
o_n_space_monad value =
    [ bgroup "Monad"
        [ benchIO "toList2" $ toListM value
        , benchIO "toListSome2" $ toListSome value
        ]
    ]

{-# INLINE drainConcatFor1 #-}
drainConcatFor1 :: Monad m => Stream m Int -> m ()
drainConcatFor1 s = drain $ do
    Stream.concatFor s $ \x ->
            Stream.fromPure $ x + 1

{-# INLINE drainConcatFor #-}
drainConcatFor :: Monad m => Stream m Int -> m ()
drainConcatFor s = drain $ do
    Stream.concatFor s $ \x ->
        Stream.concatFor s $ \y ->
            Stream.fromPure $ x + y

{-# INLINE drainConcatForM #-}
drainConcatForM :: Monad m => Stream m Int -> m ()
drainConcatForM s = drain $ do
    Stream.concatForM s $ \x ->
        pure $ Stream.concatForM s $ \y ->
            pure $ Stream.fromPure $ x + y

{-# INLINE drainConcatFor3 #-}
drainConcatFor3 :: Monad m => Stream m Int -> m ()
drainConcatFor3 s = drain $ do
    Stream.concatFor s $ \x ->
        Stream.concatFor s $ \y ->
            Stream.concatFor s $ \z ->
                Stream.fromPure $ x + y + z

{-# INLINE drainConcatFor4 #-}
drainConcatFor4 :: Monad m => Stream m Int -> m ()
drainConcatFor4 s = drain $ do
    Stream.concatFor s $ \x ->
        Stream.concatFor s $ \y ->
            Stream.concatFor s $ \z ->
                Stream.concatFor s $ \w ->
                    Stream.fromPure $ x + y + z + w

{-# INLINE drainConcatFor5 #-}
drainConcatFor5 :: Monad m => Stream m Int -> m ()
drainConcatFor5 s = drain $ do
    Stream.concatFor s $ \x ->
        Stream.concatFor s $ \y ->
            Stream.concatFor s $ \z ->
                Stream.concatFor s $ \w ->
                    Stream.concatFor s $ \u ->
                        Stream.fromPure $ x + y + z + w + u

{-# INLINE drainConcatFor3M #-}
drainConcatFor3M :: Monad m => Stream m Int -> m ()
drainConcatFor3M s = drain $ do
    Stream.concatForM s $ \x ->
        pure $ Stream.concatForM s $ \y ->
            pure $ Stream.concatForM s $ \z ->
                pure $ Stream.fromPure $ x + y + z

{-# INLINE filterAllInConcatFor #-}
filterAllInConcatFor
    :: Monad m
    => Stream m Int -> m ()
filterAllInConcatFor s = drain $ do
    Stream.concatFor s $ \x ->
        Stream.concatFor s $ \y ->
            let s1 = x + y
             in if s1 > 0
                then Stream.fromPure s1
                else Stream.nil

{-# INLINE filterAllOutConcatFor #-}
filterAllOutConcatFor
    :: Monad m
    => Stream m Int -> m ()
filterAllOutConcatFor s = drain $ do
    Stream.concatFor s $ \x ->
        Stream.concatFor s $ \y ->
            let s1 = x + y
             in if s1 < 0
                then Stream.fromPure s1
                else Stream.nil

o_1_space_bind :: Int -> [Benchmark]
o_1_space_bind streamLen =
    [ bgroup "concatFor"
        [ benchFold "drain1" drainConcatFor1   (sourceUnfoldrM streamLen)
        , benchFold "drain2" drainConcatFor   (sourceUnfoldrM streamLen2)
        , benchFold "drain3" drainConcatFor3   (sourceUnfoldrM streamLen3)
        , benchFold "drain4" drainConcatFor4   (sourceUnfoldrM streamLen4)
        , benchFold "drain5" drainConcatFor5   (sourceUnfoldrM streamLen5)
        , benchFold "drainM2" drainConcatForM   (sourceUnfoldrM streamLen2)
        , benchFold "drainM3" drainConcatFor3M   (sourceUnfoldrM streamLen3)
        , benchFold "filterAllIn2"  filterAllInConcatFor  (sourceUnfoldrM streamLen2)
        , benchFold "filterAllOut2" filterAllOutConcatFor (sourceUnfoldrM streamLen2)
        ]
    ]

    where

    streamLen2 = round (fromIntegral streamLen**(1/2::Double)) -- double nested loop
    streamLen3 = round (fromIntegral streamLen**(1/3::Double)) -- triple nested loop
    streamLen4 = round (fromIntegral streamLen**(1/4::Double)) -- 4 times nested loop
    streamLen5 = round (fromIntegral streamLen**(1/5::Double)) -- 5 times nested loop

-- search space |x| = 1000, |y| = 1000
{-# INLINE boundedInts #-}
boundedInts :: Monad m => Int -> Int -> Stream m Int
boundedInts n _ =
    Stream.interleave
        (Stream.enumerateFromTo (0 :: Int) n)
        (Stream.enumerateFromThenTo (-1) (-2) (-n))

{-# INLINE infiniteInts #-}
infiniteInts :: Monad m => Int -> Int -> Stream m Int
infiniteInts _ _ =
    Stream.interleave
        (Stream.enumerateFrom (0 :: Int))
        (Stream.enumerateFromThen (-1) (-2))

{-# INLINE boundedIntsUnfold #-}
boundedIntsUnfold :: Monad m => Int -> Int -> Unfold m ((), ()) Int
boundedIntsUnfold n _ =
    Unfold.interleave
        (Unfold.supply (0 :: Int, n) Unfold.enumerateFromTo)
        (Unfold.supply (-1, -2, -n) Unfold.enumerateFromThenTo)

{-# INLINE infiniteIntsUnfold #-}
infiniteIntsUnfold :: Monad m => Int -> Int -> Unfold m ((), ()) Int
infiniteIntsUnfold _ _ =
    Unfold.interleave
        (Unfold.supply (0 :: Int) Unfold.enumerateFrom)
        (Unfold.supply (-1, -2) Unfold.enumerateFromThen)

{-# INLINE checkStream #-}
checkStream :: Applicative m =>
    Int -> Int -> Stream m (Maybe (Maybe (Int, Int)))
checkStream x y =
    let eq1 = x + y == 0
        eq2 = x - y == 1994
     in if eq1 && eq2
        then Stream.fromPure (Just (Just (x,y)))
        else if abs x > 1000 && abs y > 1000
        then Stream.fromPure (Just Nothing)
        else Stream.fromPure Nothing

{-# INLINE checkStreamK #-}
checkStreamK :: Int -> Int -> StreamK.StreamK m (Maybe (Maybe (Int, Int)))
checkStreamK x y =
    let eq1 = x + y == 0
        eq2 = x - y == 1994
     in if eq1 && eq2
        then StreamK.fromPure (Just (Just (x,y)))
        else if abs x > 1000 && abs y > 1000
        then StreamK.fromPure (Just Nothing)
        else StreamK.fromPure Nothing

{-# INLINE checkPair #-}
checkPair :: Monad m => (Int, Int) -> m (Maybe (Maybe (Int, Int)))
checkPair (x, y) =
    let eq1 = x + y == 0
        eq2 = x - y == 1994
     in if eq1 && eq2
        then pure (Just (Just (x,y)))
        else if abs x > 1000 && abs y > 1000
        then pure (Just Nothing)
        else pure Nothing

result :: Monad m => Stream m (Maybe a) -> m ()
result = Stream.fold (Fold.take 1 Fold.drain) . Stream.catMaybes

fairConcatForEqn :: Monad m => Stream m Int -> m ()
fairConcatForEqn input =
    result
        $ Stream.fairConcatFor input $ \x ->
              Stream.fairConcatForM input $ \y -> do
                return $ checkStream x y

fairConcatForEqnK :: Monad m => Stream m Int -> m ()
fairConcatForEqnK input =
    let inputK = StreamK.fromStream input
    in result
        $ StreamK.toStream
        $ StreamK.fairConcatFor inputK $ \x ->
              StreamK.fairConcatForM inputK $ \y -> do
                return $ checkStreamK x y

concatForEqn :: Monad m => Stream m Int -> m ()
concatForEqn input =
    result
        $ Stream.concatFor input $ \x ->
              Stream.concatForM input $ \y -> do
                return $ checkStream x y

fairSchedForEqn :: Monad m => Stream m Int -> m ()
fairSchedForEqn input =
    result
        $ Stream.fairSchedFor input $ \x ->
              Stream.fairSchedForM input $ \y -> do
                return $ checkStream x y

_schedForEqn :: Monad m => Stream m Int -> m ()
_schedForEqn input =
    result
        $ Stream.schedFor input $ \x ->
              Stream.schedForM input $ \y -> do
                return $ checkStream x y

streamCrossEqn :: Monad m => Stream m Int -> m ()
streamCrossEqn input =
    result
        $ Stream.mapM checkPair
        $ Stream.cross input input

fairStreamCrossEqn :: Monad m => Stream m Int -> m ()
fairStreamCrossEqn input =
    result
        $ Stream.mapM checkPair
        $ Stream.fairCross input input

unfoldCrossEqn :: Monad m => Unfold m ((), ()) Int -> m ()
unfoldCrossEqn input =
    result
        $ Stream.mapM checkPair
        $ Stream.unfold (Unfold.cross input input) (undefined, undefined)

fairUnfoldCrossEqn :: Monad m => Unfold m ((), ()) Int -> m ()
fairUnfoldCrossEqn input =
    result
        $ Stream.mapM checkPair
        $ Stream.unfold (Unfold.fairCross input input) (undefined, undefined)

unfoldEachEqn :: Monad m => Unfold m ((), ()) Int -> Stream m Int -> m ()
unfoldEachEqn input ints =
    let intu = Unfold.carry $ Unfold.lmap (const (undefined, undefined)) input
     in result
        $ Stream.mapM checkPair
        $ Stream.unfoldEach intu ints

fairUnfoldEachEqn :: Monad m => Unfold m ((), ()) Int -> Stream m Int -> m ()
fairUnfoldEachEqn input ints =
    let intu = Unfold.carry $ Unfold.lmap (const (undefined, undefined)) input
     in result
        $ Stream.mapM checkPair
        $ Stream.fairUnfoldEach intu ints

unfoldSchedEqn :: Monad m => Unfold m ((), ()) Int -> Stream m Int -> m ()
unfoldSchedEqn input ints =
    let intu = Unfold.carry $ Unfold.lmap (const (undefined, undefined)) input
     in result
        $ Stream.mapM checkPair
        $ Stream.unfoldSched intu ints

fairUnfoldSchedEqn :: Monad m => Unfold m ((), ()) Int -> Stream m Int -> m ()
fairUnfoldSchedEqn input ints =
    let intu = Unfold.carry $ Unfold.lmap (const (undefined, undefined)) input
     in result
        $ Stream.mapM checkPair
        $ Stream.fairUnfoldSched intu ints

-- Solve simultaneous equations by exploring all possibilities
o_1_space_equations :: Int -> [Benchmark]
o_1_space_equations _ =
    [ bgroup "equations"
        [ benchFold "concatFor (bounded)" concatForEqn (boundedInts 1000)
        , benchFold "fairConcatFor (bounded)"
            fairConcatForEqn (boundedInts 1000)
        , benchFold "fairConcatForK (bounded)"
            fairConcatForEqnK (boundedInts 1000)
        , benchFold "fairConcatFor (infinite)"
            fairConcatForEqn (infiniteInts 1000)
        , benchFold "fairSchedFor (bounded)"
            fairSchedForEqn (boundedInts 1000)
        , benchFold "fairSchedFor (infinite)"
            fairSchedForEqn (infiniteInts 1000)
        , benchFold "streamCross (bounded)"
            streamCrossEqn (boundedInts 1000)
        , benchFold "fairStreamCross (bounded)"
            fairStreamCrossEqn (boundedInts 1000)
        , benchFold "fairStreamCross (infinite)"
            fairStreamCrossEqn (infiniteInts 1000)
        , bench "unfoldCross (bounded)"
            $ nfIO $ unfoldCrossEqn (boundedIntsUnfold 1000 0)
        , bench "fairUnfoldCross (bounded)"
            $ nfIO $ fairUnfoldCrossEqn (boundedIntsUnfold 1000 0)
        , bench "fairUnfoldCross (infinite)"
            $ nfIO $ fairUnfoldCrossEqn (infiniteIntsUnfold 1000 0)
        , benchFold "unfoldEach (bounded)"
            (unfoldEachEqn (boundedIntsUnfold 1000 0)) (boundedInts 1000)
        , benchFold "fairUnfoldEach (bounded)"
            (fairUnfoldEachEqn (boundedIntsUnfold 1000 0)) (boundedInts 1000)
        , benchFold "fairUnfoldEach (infinite)"
            (fairUnfoldEachEqn (infiniteIntsUnfold 1000 0)) (infiniteInts 1000)
        , benchFold "unfoldSched (bounded)"
            (unfoldSchedEqn (boundedIntsUnfold 1000 0)) (boundedInts 1000)
        , benchFold "fairUnfoldSched (bounded)"
            (fairUnfoldSchedEqn (boundedIntsUnfold 1000 0)) (boundedInts 1000)
        , benchFold "fairUnfoldSched (infinite)"
            (fairUnfoldSchedEqn (infiniteIntsUnfold 1000 0)) (infiniteInts 1000)
        ]
    ]

-------------------------------------------------------------------------------
-- Joining
-------------------------------------------------------------------------------

{-
toKv :: Int -> (Int, Int)
toKv p = (p, p)

{-# INLINE joinWith #-}
joinWith :: Common.MonadAsync m =>
       ((Int -> Int -> Bool) -> Stream m Int -> Stream m Int -> Stream m b)
    -> Int
    -> Int
    -> m ()
joinWith j val i =
    drain $ j (==) (sourceUnfoldrM val i) (sourceUnfoldrM val (val `div` 2))

{-# INLINE joinMapWith #-}
joinMapWith :: Common.MonadAsync m =>
       (Stream m (Int, Int) -> Stream m (Int, Int) -> Stream m b)
    -> Int
    -> Int
    -> m ()
joinMapWith j val i =
    drain
        $ j
            (fmap toKv (sourceUnfoldrM val i))
            (fmap toKv (sourceUnfoldrM val (val `div` 2)))

o_n_heap_buffering :: Int -> [Benchmark]
o_n_heap_buffering value =
    [ bgroup "buffered"
        [
          benchIOSrc1 "joinInnerGeneric (sqrtVal)"
            $ joinWith S.joinInnerGeneric sqrtVal
        , benchIOSrc1 "joinInner"
            $ joinMapWith S.joinInner halfVal
        , benchIOSrc1 "joinLeftGeneric (sqrtVal)"
            $ joinWith S.joinLeftGeneric sqrtVal
        , benchIOSrc1 "joinLeft "
            $ joinMapWith S.joinLeft halfVal
        , benchIOSrc1 "joinOuterGeneric (sqrtVal)"
            $ joinWith S.joinOuterGeneric sqrtVal
        , benchIOSrc1 "joinOuter"
            $ joinMapWith S.joinOuter halfVal
        , benchIOSrc1 "filterInStreamGenericBy (sqrtVal)"
            $ joinWith S.filterInStreamGenericBy sqrtVal
        , benchIOSrc1 "filterInStreamAscBy"
            $ joinMapWith (S.filterInStreamAscBy compare) halfVal
        -- Note: schedFor does a bfs scheduling, therefore, can take a lot of
        -- memory.
        , benchFold "schedFor (bounded)" schedForEqn (boundedInts 1000)
        ]
    ]

    where

    halfVal = value `div` 2
    sqrtVal = round $ sqrt (fromIntegral value :: Double)
-}

-------------------------------------------------------------------------------
-- Main
-------------------------------------------------------------------------------

-- In addition to gauge options, the number of elements in the stream can be
-- passed using the --stream-size option.
--
{-# ANN benchmarks "HLint: ignore" #-}
benchmarks :: String -> Int -> [Benchmark]
benchmarks moduleName size =
        [ bgroup (o_1_space_prefix moduleName) $ Prelude.concat
            [
            -- multi-stream
              o_1_space_joining size
#ifdef USE_PRELUDE
            , o_1_space_concatFoldable size
#endif
            , o_1_space_concat size
            , o_1_space_applicative size
            , o_1_space_monad size
            , o_1_space_bind size
            , o_1_space_equations size
            ]
        , bgroup (o_n_space_prefix moduleName) $ Prelude.concat
            [
            -- multi-stream
              o_n_space_monad size
            ]
       {-
       , bgroup (o_n_heap_prefix moduleName) $
            -- multi-stream
            o_n_heap_buffering size
       -}
        ]