2{-# OPTIONS_GHC -Wno-orphans #-}
5-- Module : Streamly.Benchmark.Common
6-- Copyright : (c) 2019 Composewell Technologies
9-- Maintainer : streamly@composewell.com
11module Streamly.Benchmark.Common
36#ifdef MIN_VERSION_gauge
37import Control.Exception (evaluate)
38import Control.Monad (when)
39import Text.Read (readMaybe)
40import Data.List (scanl')
41import Data.Maybe (mapMaybe)
42import System.Console.GetOpt
43 (OptDescr(..), ArgDescr(..), ArgOrder(..), getOpt')
44import System.Environment (getArgs, lookupEnv, setEnv)
46import Data.Proxy (Proxy(..))
47import Test.Tasty.Ingredients.Basic (includingOptions)
48import Test.Tasty.Options
49 (IsOption(..), OptionDescription(..), lookupOption, safeRead)
50import Test.Tasty.Runners (Ingredient, defaultMainWithIngredients, parseOptions)
52import Control.DeepSeq (NFData(..))
53import Data.Functor.Identity (Identity, runIdentity)
54import System.Random (randomRIO)
56import qualified Streamly.Prelude as S
58import Streamly.Prelude (SerialT)
61-------------------------------------------------------------------------------
63-------------------------------------------------------------------------------
65o_1_space_prefix :: String -> String
66o_1_space_prefix name = name ++ "/o-1-space"
68o_n_space_prefix :: String -> String
69o_n_space_prefix name = name ++ "/o-n-space"
71o_n_heap_prefix :: String -> String
72o_n_heap_prefix name = name ++ "/o-n-heap"
74o_n_stack_prefix :: String -> String
75o_n_stack_prefix name = name ++ "/o-n-stack"
77-------------------------------------------------------------------------------
78-- Benchmarking utilities
79-------------------------------------------------------------------------------
81-- XXX once we convert all the functions to use this we can rename this to
83{-# INLINE benchIOSink1 #-}
84benchIOSink1 :: NFData b => String -> (Int -> IO b) -> Benchmark
85benchIOSink1 name f = bench name $ nfIO $ randomRIO (1,1) >>= f
87{-# INLINE benchIOSrc1 #-}
88benchIOSrc1 :: String -> (Int -> IO ()) -> Benchmark
89benchIOSrc1 name f = bench name $ nfIO $ randomRIO (1,1) >>= f
91-- We need a monadic bind here to make sure that the function f does not get
92-- completely optimized out by the compiler in some cases.
93{-# INLINE benchFold #-}
95 => String -> (t IO Int -> IO b) -> (Int -> t IO Int) -> Benchmark
96benchFold name f src = bench name $ nfIO $ randomRIO (1,1) >>= f . src
98{-# INLINE benchPure #-}
99benchPure :: NFData b => String -> (Int -> a) -> (a -> b) -> Benchmark
100benchPure name src f = bench name $ nfIO $ randomRIO (1,1) >>= return . f . src
102-- XXX once we convert all the functions to use this we can rename this to
104{-# INLINE benchPureSink1 #-}
105benchPureSink1 :: NFData b => String -> (Int -> Identity b) -> Benchmark
106benchPureSink1 name f =
107 bench name $ nfIO $ randomRIO (1,1) >>= return . runIdentity . f
109{-# INLINE benchPureSrc #-}
110benchPureSrc :: String -> (Int -> SerialT Identity a) -> Benchmark
111benchPureSrc name src = benchPure name src (runIdentity . S.drain)
113-------------------------------------------------------------------------------
114-- String/List generation for read instances
115-------------------------------------------------------------------------------
117{-# INLINABLE mkString #-}
118mkString :: Int -> String
119mkString value = "fromList [1" ++ concat (replicate value ",1") ++ "]"
121{-# INLINABLE mkListString #-}
122mkListString :: Int -> String
123mkListString value = "[1" ++ concat (replicate value ",1") ++ "]"
125{-# INLINABLE mkList #-}
126mkList :: Int -> [Int]
127mkList value = [1..value]
129-------------------------------------------------------------------------------
131-------------------------------------------------------------------------------
133defaultStreamSize :: Int
134defaultStreamSize = 100000
136-------------------------------------------------------------------------------
137-- Parse custom CLI options
138-------------------------------------------------------------------------------
140newtype BenchOpts = StreamSize Int deriving Show
142#ifdef MIN_VERSION_gauge
143getStreamSize :: String -> Int
145 case (readMaybe size :: Maybe Int) of
147 Nothing -> error "Stream size must be numeric"
149options :: [OptDescr BenchOpts]
152 System.Console.GetOpt.Option [] ["stream-size"] (ReqArg getSize "COUNT") "Stream element count"
157 getSize = StreamSize . getStreamSize
160 :: (Maybe String, Maybe String) -- (prev, yielded)
162 -> (Maybe String, Maybe String)
163deleteOptArgs (Nothing, _) opt =
164 if opt == "--stream-size"
165 then (Just opt, Nothing)
166 else (Just opt, Just opt)
168deleteOptArgs (Just prev, _) opt =
169 if opt == "--stream-size" || prev == "--stream-size"
170 then (Just opt, Nothing)
171 else (Just opt, Just opt)
173parseCLIOpts :: Int -> IO (Int, Config, [String])
174parseCLIOpts defStreamSize = do
176 -- Parse custom options
177 let (opts, _, _, errs) = getOpt' Permute options args
178 when (not $ null errs) $ error $ concat errs
179 (streamSize, args') <-
181 StreamSize x : _ -> do
182 putStrLn $ "Stream size: " ++ show x
183 -- When using the gauge "--measure-with" option we need to make
184 -- sure that we pass the stream size to child process forked by
185 -- gauge. So we use this env var for that purpose.
186 setEnv "STREAM_SIZE" (show x)
187 -- Hack! remove the option and its argument from args
188 -- getOpt should have a way to return the unconsumed args in
193 $ scanl' deleteOptArgs (Nothing, Nothing) args
196 r <- lookupEnv "STREAM_SIZE"
199 s <- evaluate $ getStreamSize x
202 setEnv "STREAM_SIZE" (show defStreamSize)
203 return (defStreamSize, args)
205 -- Parse gauge options
206 let config = defaultConfig
209 , includeFirstIter = streamSize > defStreamSize
211 let (cfg, benches) = parseWith config args'
212 streamSize `seq` return (streamSize, cfg, benches)
215instance IsOption BenchOpts where
216 defaultValue = StreamSize defaultStreamSize
217 parseValue = fmap StreamSize . safeRead
218 optionName = pure "stream-size"
219 optionHelp = pure "Size of the stream to be used in benchmarks"
221parseCLIOpts :: Int -> Benchmark -> IO (Int, [Ingredient])
222parseCLIOpts defStreamSize benches = do
223 let customOpts = [Test.Tasty.Options.Option (Proxy :: Proxy BenchOpts)]
224 ingredients = includingOptions customOpts : benchIngredients
225 opts <- parseOptions ingredients benches -- (TestGroup "" [])
226 let StreamSize size = lookupOption opts
227 -- putStrLn $ "Stream size: " ++ show size
228 if size == defaultStreamSize
229 then return (defStreamSize, ingredients)
230 else return (size, ingredients)
233runWithCLIOptsEnv :: Int -> (Int -> IO a) -> (a -> Int -> [Benchmark]) -> IO ()
234runWithCLIOptsEnv defStreamSize alloc mkBench = do
236#ifdef MIN_VERSION_gauge
237 (value, cfg, benches) <- parseCLIOpts defStreamSize
239 value `seq` runMode (mode cfg) cfg benches (mkBench r value)
241 (value, ingredients) <-
242 parseCLIOpts defStreamSize $ bgroup "All" (mkBench undefined 0)
244 value `seq` defaultMainWithIngredients ingredients
245 $ bgroup "All" (mkBench r value)
248runWithCLIOpts :: Int -> (Int -> [Benchmark]) -> IO ()
249runWithCLIOpts defStreamSize f =
250 runWithCLIOptsEnv defStreamSize
251 (const $ return undefined)
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