on master help sign in
Accept stream size via a CLI option
8ec2ebeHarendra Kumar
benchmark/Common.hsnew file+82
@@ -0,0 +1,82 @@
1+-- |
2+-- Module : Main
3+-- Copyright : (c) 2019 Composewell Technologies
4+--
5+-- License : BSD3
6+-- Maintainer : streamly@composewell.com
7+
8+module Common (parseCLIOpts) where
9+
10+import Control.Exception (evaluate)
11+import Control.Monad (when)
12+import Data.List (scanl')
13+import Data.Maybe (catMaybes)
14+import System.Console.GetOpt
15+ (OptDescr(..), ArgDescr(..), ArgOrder(..), getOpt')
16+import System.Environment (getArgs)
17+import Text.Read (readMaybe)
18+
19+import Gauge
20+
21+-------------------------------------------------------------------------------
22+-- Parse custom CLI options
23+-------------------------------------------------------------------------------
24+
25+data BenchOpts = StreamSize Int deriving Show
26+
27+options :: [OptDescr BenchOpts]
28+options =
29+ [
30+ Option [] ["stream-size"] (ReqArg getSize "COUNT") "Stream element count"
31+ ]
32+
33+ where
34+
35+ getSize :: String -> BenchOpts
36+ getSize size =
37+ case (readMaybe size :: Maybe Int) of
38+ Just x -> StreamSize x
39+ Nothing -> error "Stream size must be numeric"
40+
41+deleteOptArgs
42+ :: (Maybe String, Maybe String) -- (prev, yielded)
43+ -> String
44+ -> (Maybe String, Maybe String)
45+deleteOptArgs (Nothing, _) opt =
46+ if opt == "--stream-size"
47+ then (Just opt, Nothing)
48+ else (Just opt, Just opt)
49+
50+deleteOptArgs (Just prev, _) opt =
51+ if opt == "--stream-size" || prev == "--stream-size"
52+ then (Just opt, Nothing)
53+ else (Just opt, Just opt)
54+
55+parseCLIOpts :: Int -> IO (Int, Config, [String])
56+parseCLIOpts defaultStreamSize = do
57+ args <- getArgs
58+
59+ -- Parse custom options
60+ let (opts, _, _, errs) = getOpt' Permute options args
61+ when (not $ null errs) $ error $ concat errs
62+ (streamSize, args') <- evaluate $
63+ case opts of
64+ StreamSize x : _ ->
65+ -- Hack! remove the option and its argument from args
66+ -- getOpt should have a way to return the unconsumed args in
67+ -- correct order.
68+ let newArgs =
69+ catMaybes
70+ $ map snd
71+ $ scanl' deleteOptArgs (Nothing, Nothing) args
72+ in (x, newArgs)
73+ _ -> (defaultStreamSize, args)
74+
75+ -- Parse gauge options
76+ let config = defaultConfig
77+ { timeLimit = Just 1
78+ , minDuration = 0
79+ , includeFirstIter = streamSize > defaultStreamSize
80+ }
81+ let (cfg, benches) = parseWith config args'
82+ return (streamSize, cfg, benches)
benchmark/Linear.hs+9−15
@@ -12,10 +12,10 @@
12
12
13import Control.DeepSeq (NFData(..), deepseq)
13import Control.DeepSeq (NFData(..), deepseq)
14import Data.Functor.Identity (Identity, runIdentity)
14import Data.Functor.Identity (Identity, runIdentity)
15−import System.Random (randomRIO)
16import Data.Monoid (Last(..))
15import Data.Monoid (Last(..))
17−import System.Environment (getArgs)
16+import System.Random (randomRIO)
18−import Text.Read (readMaybe)
17+
18+import Common (parseCLIOpts)
19
19
20import qualified GHC.Exts as GHC
20import qualified GHC.Exts as GHC
21import qualified Streamly.Benchmark.Prelude as Ops
21import qualified Streamly.Benchmark.Prelude as Ops
@@ -121,13 +121,14 @@ mkListString value = "[1" ++ concat (replicate value ",1") ++ "]"
121mkList :: Int -> [Int]
121mkList :: Int -> [Int]
122mkList value = [1..value]
122mkList value = [1..value]
123
123
124+defaultStreamSize :: Int
125+defaultStreamSize = 100000
126+
124main :: IO ()
127main :: IO ()
125main = do
128main = do
126− -- Basement.Terminal.initialize required?
129+ -- XXX Fix indentation
127− args <- getArgs
130+ (value, cfg, benches) <- parseCLIOpts defaultStreamSize
128− let (value, args') = parseValue args
129− (cfg, extra) = parseWith defaultConfig args'
130− runMode (mode cfg) cfg extra
131+ runMode (mode cfg) cfg benches
131 [ bgroup "serially"
132 [ bgroup "serially"
132 [ bgroup "pure"
133 [ bgroup "pure"
133 [ benchPureSink value "id" id
134 [ benchPureSink value "id" id
@@ -474,10 +475,3 @@ main = do
474 ]
475 ]
475 ]
476 ]
476 ]
477 ]
477− where
478− defaultValue = 100000
479− parseValue [] = (defaultValue, [])
480− parseValue a@(x:xs) =
481− case (readMaybe x :: Maybe Int) of
482− Just value -> (value, xs)
483− Nothing -> (defaultValue, a)
benchmark/LinearAsync.hs+10−21
@@ -9,13 +9,14 @@
9import Control.DeepSeq (NFData)
9import Control.DeepSeq (NFData)
10-- import Data.Functor.Identity (Identity, runIdentity)
10-- import Data.Functor.Identity (Identity, runIdentity)
11import System.Random (randomRIO)
11import System.Random (randomRIO)
12−import Text.Read (readMaybe)
12+
13−import System.Environment (getArgs)
13+import Common (parseCLIOpts)
14−import qualified Streamly.Benchmark.Prelude as Ops
15
14
16import Streamly
15import Streamly
17import Gauge
16import Gauge
18
17
18+import qualified Streamly.Benchmark.Prelude as Ops
19+
19-- We need a monadic bind here to make sure that the function f does not get
20-- We need a monadic bind here to make sure that the function f does not get
20-- completely optimized out by the compiler in some cases.
21-- completely optimized out by the compiler in some cases.
21--
22--
@@ -43,13 +44,14 @@ _benchId :: NFData b => String -> (Ops.Stream m Int -> Identity b) -> Benchmark
43_benchId name f = bench name $ nf (runIdentity . f) (Ops.source 10)
44_benchId name f = bench name $ nf (runIdentity . f) (Ops.source 10)
44-}
45-}
45
46
47+defaultStreamSize :: Int
48+defaultStreamSize = 10000
49+
46main :: IO ()
50main :: IO ()
47main = do
51main = do
48− -- Basement.Terminal.initialize required?
52+ -- XXX Fix indentation
49− args <- getArgs
53+ (value, cfg, benches) <- parseCLIOpts defaultStreamSize
50− let (value, args') = parseValue args
51− (cfg, extra) = parseWith (newConfig value) args'
52− runMode (mode cfg) cfg extra
54+ runMode (mode cfg) cfg benches
53 [ bgroup "asyncly"
55 [ bgroup "asyncly"
54 [ benchSrcIO asyncly "unfoldr" (Ops.sourceUnfoldr value)
56 [ benchSrcIO asyncly "unfoldr" (Ops.sourceUnfoldr value)
55 , benchSrcIO asyncly "unfoldrM" (Ops.sourceUnfoldrM value)
57 , benchSrcIO asyncly "unfoldrM" (Ops.sourceUnfoldrM value)
@@ -133,16 +135,3 @@ main = do
133 (Ops.concatStreamsWith parallel (value `div` 10) 10)
135 (Ops.concatStreamsWith parallel (value `div` 10) 10)
134 ]
136 ]
135 ]
137 ]
136− where
137− defaultValue = 10000
138− parseValue [] = (defaultValue, [])
139− parseValue a@(x:xs) =
140− case (readMaybe x :: Maybe Int) of
141− Just value -> (value, xs)
142− Nothing -> (defaultValue, a)
143− newConfig value =
144− defaultConfig
145− { timeLimit = Just 1
146− , minDuration = 0
147− , includeFirstIter = value >= 10000000
148− }
benchmark/LinearRate.hs+10−16
@@ -1,4 +1,3 @@
1−{-# LANGUAGE CPP #-}
2-- |
1-- |
3-- Module : Main
2-- Module : Main
4-- Copyright : (c) 2018 Harendra Kumar
3-- Copyright : (c) 2018 Harendra Kumar
@@ -11,13 +10,14 @@
11
10
12-- import Data.Functor.Identity (Identity, runIdentity)
11-- import Data.Functor.Identity (Identity, runIdentity)
13import System.Random (randomRIO)
12import System.Random (randomRIO)
14−import qualified Streamly.Benchmark.Prelude as Ops
13+
15−import System.Environment (getArgs)
14+import Common (parseCLIOpts)
16−import Text.Read (readMaybe)
17
15
18import Streamly
16import Streamly
19import Gauge
17import Gauge
20
18
19+import qualified Streamly.Benchmark.Prelude as Ops
20+
21-- | Takes a source, and uses it with a default drain/fold method.
21-- | Takes a source, and uses it with a default drain/fold method.
22{-# INLINE benchSrcIO #-}
22{-# INLINE benchSrcIO #-}
23benchSrcIO
23benchSrcIO
@@ -33,13 +33,14 @@ _benchId :: NFData b => String -> (Ops.Stream m Int -> Identity b) -> Benchmark
33_benchId name f = bench name $ nf (runIdentity . f) (Ops.source 10)
33_benchId name f = bench name $ nf (runIdentity . f) (Ops.source 10)
34-}
34-}
35
35
36+defaultStreamSize :: Int
37+defaultStreamSize = 100000
38+
36main :: IO ()
39main :: IO ()
37main = do
40main = do
38− -- Basement.Terminal.initialize required?
41+ -- XXX Fix indentation
39− args <- getArgs
42+ (value, cfg, benches) <- parseCLIOpts defaultStreamSize
40− let (value, args') = parseValue args
41− (cfg, extra) = parseWith defaultConfig args'
42− runMode (mode cfg) cfg extra
43+ runMode (mode cfg) cfg benches
43 -- XXX arbitrarily large rate should be the same as rate Nothing
44 -- XXX arbitrarily large rate should be the same as rate Nothing
44 [ bgroup "avgrate"
45 [ bgroup "avgrate"
45 [ bgroup "asyncly"
46 [ bgroup "asyncly"
@@ -65,10 +66,3 @@ main = do
65 ]
66 ]
66 ]
67 ]
67 ]
68 ]
68− where
69− defaultValue = 100000
70− parseValue [] = (defaultValue, [])
71− parseValue a@(x:xs) =
72− case (readMaybe x :: Maybe Int) of
73− Just value -> (value, xs)
74− Nothing -> (defaultValue, a)
benchmark/Nested.hs+11−16
@@ -8,11 +8,13 @@
8import Control.DeepSeq (NFData)
8import Control.DeepSeq (NFData)
9import Data.Functor.Identity (Identity, runIdentity)
9import Data.Functor.Identity (Identity, runIdentity)
10import System.Random (randomRIO)
10import System.Random (randomRIO)
11−import qualified NestedOps as Ops
11+
12+import Common (parseCLIOpts)
13+
12import Streamly
14import Streamly
13import Gauge
15import Gauge
14−import System.Environment (getArgs)
16+
15−import Text.Read (readMaybe)
17+import qualified NestedOps as Ops
16
18
17benchIO :: (NFData b) => String -> (Int -> IO b) -> Benchmark
19benchIO :: (NFData b) => String -> (Int -> IO b) -> Benchmark
18benchIO name f = bench name $ nfIO $ randomRIO (1,1) >>= f
20benchIO name f = bench name $ nfIO $ randomRIO (1,1) >>= f
@@ -20,14 +22,14 @@ benchIO name f = bench name $ nfIO $ randomRIO (1,1) >>= f
20_benchId :: (NFData b) => String -> (Int -> Identity b) -> Benchmark
22_benchId :: (NFData b) => String -> (Int -> Identity b) -> Benchmark
21_benchId name f = bench name $ nf (\g -> runIdentity (g 1)) f
23_benchId name f = bench name $ nf (\g -> runIdentity (g 1)) f
22
24
25+defaultStreamSize :: Int
26+defaultStreamSize = 100000
27+
23main :: IO ()
28main :: IO ()
24main = do
29main = do
25− -- TBD Study scaling with 10, 100, 1000 loop iterations
30+ -- XXX Fix indentation
26− -- Basement.Terminal.initialize required?
31+ (linearCount, cfg, benches) <- parseCLIOpts defaultStreamSize
27− args <- getArgs
28− let (linearCount, args') = parseValue args
29− (cfg, extra) = parseWith defaultConfig args'
30− runMode (mode cfg) cfg extra
32+ runMode (mode cfg) cfg benches
31 [ bgroup "serially"
33 [ bgroup "serially"
32 [ benchIO "toNullAp" $ Ops.toNullAp linearCount serially
34 [ benchIO "toNullAp" $ Ops.toNullAp linearCount serially
33 , benchIO "toNull" $ Ops.toNull linearCount serially
35 , benchIO "toNull" $ Ops.toNull linearCount serially
@@ -56,10 +58,3 @@ main = do
56 [ benchIO "toNullAp" $ Ops.toNullAp linearCount zipSerially
58 [ benchIO "toNullAp" $ Ops.toNullAp linearCount zipSerially
57 ]
59 ]
58 ]
60 ]
59− where
60− defaultValue = 100000
61− parseValue [] = (defaultValue, [])
62− parseValue a@(x:xs) =
63− case (readMaybe x :: Maybe Int) of
64− Just value -> (value, xs)
65− Nothing -> (defaultValue, a)
benchmark/NestedConcurrent.hs+11−16
@@ -8,11 +8,13 @@
8import Control.DeepSeq (NFData)
8import Control.DeepSeq (NFData)
9import Data.Functor.Identity (Identity, runIdentity)
9import Data.Functor.Identity (Identity, runIdentity)
10import System.Random (randomRIO)
10import System.Random (randomRIO)
11−import qualified NestedOps as Ops
11+
12+import Common (parseCLIOpts)
13+
12import Streamly
14import Streamly
13import Gauge
15import Gauge
14−import System.Environment (getArgs)
16+
15−import Text.Read (readMaybe)
17+import qualified NestedOps as Ops
16
18
17benchIO :: (NFData b) => String -> (Int -> IO b) -> Benchmark
19benchIO :: (NFData b) => String -> (Int -> IO b) -> Benchmark
18benchIO name f = bench name $ nfIO $ randomRIO (1,1) >>= f
20benchIO name f = bench name $ nfIO $ randomRIO (1,1) >>= f
@@ -20,14 +22,14 @@ benchIO name f = bench name $ nfIO $ randomRIO (1,1) >>= f
20_benchId :: (NFData b) => String -> (Int -> Identity b) -> Benchmark
22_benchId :: (NFData b) => String -> (Int -> Identity b) -> Benchmark
21_benchId name f = bench name $ nf (\g -> runIdentity (g 1)) f
23_benchId name f = bench name $ nf (\g -> runIdentity (g 1)) f
22
24
25+defaultStreamSize :: Int
26+defaultStreamSize = 100000
27+
23main :: IO ()
28main :: IO ()
24main = do
29main = do
25− -- TBD Study scaling with 10, 100, 1000 loop iterations
30+ -- XXX Fix indentation
26− -- Basement.Terminal.initialize required?
31+ (linearCount, cfg, benches) <- parseCLIOpts defaultStreamSize
27− args <- getArgs
28− let (linearCount, args') = parseValue args
29− (cfg, extra) = parseWith defaultConfig args'
30− runMode (mode cfg) cfg extra
32+ runMode (mode cfg) cfg benches
31 [
33 [
32 bgroup "aheadly"
34 bgroup "aheadly"
33 [ benchIO "toNullAp" $ Ops.toNullAp linearCount aheadly
35 [ benchIO "toNullAp" $ Ops.toNullAp linearCount aheadly
@@ -81,10 +83,3 @@ main = do
81 [ benchIO "toNullAp" $ Ops.toNullAp linearCount zipAsyncly
83 [ benchIO "toNullAp" $ Ops.toNullAp linearCount zipAsyncly
82 ]
84 ]
83 ]
85 ]
84− where
85− defaultValue = 100000
86− parseValue [] = (defaultValue, [])
87− parseValue a@(x:xs) =
88− case (readMaybe x :: Maybe Int) of
89− Just value -> (value, xs)
90− Nothing -> (defaultValue, a)
streamly.cabal+9−13
@@ -584,21 +584,17 @@ test-suite parallel-loops
584-- Benchmarks
584-- Benchmarks
585-------------------------------------------------------------------------------
585-------------------------------------------------------------------------------
586
586
587−-- NOTE :
588−-- For linear, linear-async, linear-rate, nested and nested-concurrent if you
587+-- For linear, linear-async, linear-rate, nested and nested-concurrent
589−-- want to pass the number of stream elements for benchmarking, you can pass it
588+-- you can pass the number of elements in the stream using the
590−-- as the first argument after `--`, as a gauge argument. If you don't give the
589+-- --stream-size option:
591−-- argument, the number of stream elements default to 100000.
592−-- Examples:
593−-- $ cabal run linear -- 1000000 [additional gauge options here...]
590+-- $ cabal run linear -- --stream-size 1000000
594−-- $ cabal run linear -- [additional gauge options here...]
595
591
596benchmark linear
592benchmark linear
597 import: bench-options
593 import: bench-options
598 type: exitcode-stdio-1.0
594 type: exitcode-stdio-1.0
599 hs-source-dirs: benchmark
595 hs-source-dirs: benchmark
600 main-is: Linear.hs
596 main-is: Linear.hs
601− other-modules: Streamly.Benchmark.Prelude
597+ other-modules: Streamly.Benchmark.Prelude, Common
602 build-depends:
598 build-depends:
603 streamly
599 streamly
604 , base >= 4.8 && < 5
600 , base >= 4.8 && < 5
@@ -617,7 +613,7 @@ benchmark nested
617 type: exitcode-stdio-1.0
613 type: exitcode-stdio-1.0
618 hs-source-dirs: benchmark
614 hs-source-dirs: benchmark
619 main-is: Nested.hs
615 main-is: Nested.hs
620− other-modules: NestedOps
616+ other-modules: NestedOps, Common
621 build-depends:
617 build-depends:
622 streamly
618 streamly
623 , base >= 4.8 && < 5
619 , base >= 4.8 && < 5
@@ -721,7 +717,7 @@ benchmark linear-async
721 type: exitcode-stdio-1.0
717 type: exitcode-stdio-1.0
722 hs-source-dirs: benchmark
718 hs-source-dirs: benchmark
723 main-is: LinearAsync.hs
719 main-is: LinearAsync.hs
724− other-modules: Streamly.Benchmark.Prelude
720+ other-modules: Streamly.Benchmark.Prelude, Common
725 build-depends:
721 build-depends:
726 streamly
722 streamly
727 , base >= 4.8 && < 5
723 , base >= 4.8 && < 5
@@ -740,7 +736,7 @@ benchmark nested-concurrent
740 type: exitcode-stdio-1.0
736 type: exitcode-stdio-1.0
741 hs-source-dirs: benchmark
737 hs-source-dirs: benchmark
742 main-is: NestedConcurrent.hs
738 main-is: NestedConcurrent.hs
743− other-modules: NestedOps
739+ other-modules: NestedOps, Common
744 build-depends:
740 build-depends:
745 streamly
741 streamly
746 , base >= 4.8 && < 5
742 , base >= 4.8 && < 5
@@ -756,7 +752,7 @@ benchmark linear-rate
756 type: exitcode-stdio-1.0
752 type: exitcode-stdio-1.0
757 hs-source-dirs: benchmark
753 hs-source-dirs: benchmark
758 main-is: LinearRate.hs
754 main-is: LinearRate.hs
759− other-modules: Streamly.Benchmark.Prelude
755+ other-modules: Streamly.Benchmark.Prelude, Common
760 build-depends:
756 build-depends:
761 streamly
757 streamly
762 , base >= 4.8 && < 5
758 , base >= 4.8 && < 5