content: {-# OPTIONS_GHC -Wno-deprecations #-}
  
  -- |
  -- Module      : Main
  -- Copyright   : (c) 2018 Composewell Technologies
  --
  -- License     : BSD3
  -- Maintainer  : streamly@composewell.com
  
  import Prelude hiding (mapM)
  
  import Streamly.Prelude (fromAsync, async, maxBuffer, maxThreads, fromSerial)
  import qualified Streamly.Prelude as S
  import qualified Streamly.Internal.Data.Stream.IsStream.Transform as Transform
  
  import Streamly.Benchmark.Common
  import Streamly.Benchmark.Prelude
  
  import Test.Tasty.Bench
  
  moduleName :: String
  moduleName = "Prelude.Async"
  
  -------------------------------------------------------------------------------
  -- Generation
  -------------------------------------------------------------------------------
  
  o_1_space_generation :: Int -> [Benchmark]
  o_1_space_generation value =
      -- These basically test the performance of consMAsync
      [ bgroup "generation"
          [ benchIOSrc fromAsync "unfoldr" (sourceUnfoldr value)
          , benchIOSrc fromAsync "unfoldrM" (sourceUnfoldrM value)
          , benchIOSrc fromAsync "fromListM" (sourceFromListM value)
          , benchIOSrc fromAsync "fromFoldable (List)" (sourceFromFoldable value)
          , benchIOSrc fromAsync "fromFoldableM (List)" (sourceFromFoldableM value)
          , benchIOSrc fromAsync "unfoldrM maxThreads 1"
              (maxThreads 1 . sourceUnfoldrM value)
          , benchIOSrc fromAsync "unfoldrM maxBuffer 1 (x/10 ops)"
              (maxBuffer 1 . sourceUnfoldrM (value `div` 10))
          ]
      ]
  
  -------------------------------------------------------------------------------
  -- Mapping
  -------------------------------------------------------------------------------
  
  {-# INLINE foldrSShared #-}
  foldrSShared :: Int -> Int -> IO ()
  foldrSShared count n =
        S.drain
      $ fromAsync
      $ Transform.foldrSShared (\x xs -> S.consM (return x) xs) S.nil
      $ fromSerial
      $ sourceUnfoldrM count n
  
  o_1_space_mapping :: Int -> [Benchmark]
  o_1_space_mapping value =
      [ bgroup "mapping"
          [ benchIOSink value "map" $ mapN fromAsync 1
          , benchIOSink value "fmap" $ fmapN fromAsync 1
          , benchIOSrc1 "foldrSShared" (foldrSShared value)
          -- This basically tests the performance of consMAsync
          , benchIOSink value "mapM" $ mapM fromAsync 1 . fromSerial
          ]
      ]
  
  -------------------------------------------------------------------------------
  -- Size conserving transformations (reordering, buffering, etc.)
  -------------------------------------------------------------------------------
  
  o_n_heap_buffering :: Int -> [Benchmark]
  o_n_heap_buffering value =
      [bgroup "buffered" [benchIOSink value "mkAsync" (mkAsync fromAsync)]]
  
  -------------------------------------------------------------------------------
  -- Joining
  -------------------------------------------------------------------------------
  
  {-# INLINE async2 #-}
  async2 :: Int -> Int -> IO ()
  async2 count n =
      S.drain $
          (sourceUnfoldrM count n) `async` (sourceUnfoldrM count (n + 1))
  
  {-# INLINE async4 #-}
  async4 :: Int -> Int -> IO ()
  async4 count n =
      S.drain $
                    (sourceUnfoldrM count (n + 0))
          `async` (sourceUnfoldrM count (n + 1))
          `async` (sourceUnfoldrM count (n + 2))
          `async` (sourceUnfoldrM count (n + 3))
  
  {-# INLINE async2n2 #-}
  async2n2 :: Int -> Int -> IO ()
  async2n2 count n =
      S.drain $
          ((sourceUnfoldrM count (n + 0))
              `async` (sourceUnfoldrM count (n + 1)))
          `async` ((sourceUnfoldrM count (n + 2))
              `async` (sourceUnfoldrM count (n + 3)))
  
  o_1_space_joining :: Int -> [Benchmark]
  o_1_space_joining value =
      [ bgroup "joining"
          [ benchIOSrc1 "async (2 of n/2)" (async2 (value `div` 2))
          , benchIOSrc1 "async (4 of n/4)" (async4 (value `div` 4))
          , benchIOSrc1 "async (2 of (2 of n/4)" (async2n2 (value `div` 4))
          ]
      ]
  
  -------------------------------------------------------------------------------
  -- Concat
  -------------------------------------------------------------------------------
  
  -- These basically test the performance of folding streams with `async`
  o_1_space_concatFoldable :: Int -> [Benchmark]
  o_1_space_concatFoldable value =
      [ bgroup "concat-foldable"
          [ benchIOSrc fromAsync "foldMapWith (<>) (List)"
              (sourceFoldMapWith value)
          , benchIOSrc fromAsync "foldMapWith (<>) (Stream)"
              (sourceFoldMapWithStream value)
          , benchIOSrc fromAsync "foldMapWithM (<>) (List)"
              (sourceFoldMapWithM value)
          , benchIOSrc fromAsync "S.concatFoldableWith (<>) (List)"
              (concatFoldableWith value)
          , benchIOSrc fromAsync "S.concatForFoldableWith (<>) (List)"
              (concatForFoldableWith value)
          , benchIOSrc fromAsync "foldMapM (List)" (sourceFoldMapM value)
          ]
      ]
  
  -- These basically test the performance of concating streams with `async`
  o_1_space_concatMap :: Int -> [Benchmark]
  o_1_space_concatMap value =
      value2 `seq`
          [ bgroup "concat"
              -- This is for comparison with foldMapWith
              [ benchIOSrc fromSerial "concatMapWithId (n of 1) (fromFoldable)"
                  (S.concatMapWith async id . sourceConcatMapId value)
  
              , benchIO "concatMapWith (n of 1)"
                    (concatStreamsWith async value 1)
              , benchIO "concatMapWith (sqrt x of sqrt x)"
                    (concatStreamsWith async value2 value2)
              , benchIO "concatMapWith (1 of n)"
                    (concatStreamsWith async 1 value)
              ]
          ]
  
      where
  
      value2 = round $ sqrt (fromIntegral value :: Double)
  
  -------------------------------------------------------------------------------
  -- Monadic outer product
  -------------------------------------------------------------------------------
  
  o_1_space_outerProduct :: Int -> [Benchmark]
  o_1_space_outerProduct value =
      [ bgroup "monad-outer-product"
          [ benchIO "toNullAp"       $ toNullAp value fromAsync
          , benchIO "toNull"         $ toNullM value fromAsync
          , benchIO "toNull3"        $ toNullM3 value fromAsync
          , benchIO "filterAllOut"   $ filterAllOutM value fromAsync
          , benchIO "filterAllIn"    $ filterAllInM value fromAsync
          , benchIO "filterSome"     $ filterSome value fromAsync
          , benchIO "breakAfterSome" $ breakAfterSome value fromAsync
  
          ]
      ]
  
  o_n_space_outerProduct :: Int -> [Benchmark]
  o_n_space_outerProduct value =
      [ bgroup "monad-outer-product"
          [ benchIO "toList"         $ toListM value fromAsync
          , benchIO "toListSome"     $ toListSome value fromAsync
          ]
      ]
  
  -------------------------------------------------------------------------------
  -- Main
  -------------------------------------------------------------------------------
  
  main :: IO ()
  main = runWithCLIOpts defaultStreamSize allBenchmarks
  
  
      where
  
      allBenchmarks value =
          [ bgroup (o_1_space_prefix moduleName) $ concat
              [ o_1_space_generation value
              , o_1_space_mapping value
              , o_1_space_concatFoldable value
              , o_1_space_concatMap value
              , o_1_space_outerProduct value
              , o_1_space_joining value
              ]
          , bgroup (o_n_heap_prefix moduleName) (o_n_heap_buffering value)
          , bgroup (o_n_space_prefix moduleName) (o_n_space_outerProduct value)
          ]
  