2#if __GLASGOW_HASKELL__ >= 800
3{-# OPTIONS_GHC -Wno-orphans #-}
7-- Module : Streamly.Benchmark.Common
8-- Copyright : (c) 2019 Composewell Technologies
11-- Maintainer : streamly@composewell.com
13module Streamly.Benchmark.Common
38#ifdef MIN_VERSION_gauge
39import Control.Exception (evaluate)
40import Control.Monad (when)
41import Text.Read (readMaybe)
42import Data.List (scanl')
43import Data.Maybe (mapMaybe)
44import System.Console.GetOpt
45 (OptDescr(..), ArgDescr(..), ArgOrder(..), getOpt')
46import System.Environment (getArgs, lookupEnv, setEnv)
48import Data.Proxy (Proxy(..))
49import Test.Tasty.Ingredients.Basic (includingOptions)
50import Test.Tasty.Options
51 (IsOption(..), OptionDescription(..), lookupOption, safeRead)
52import Test.Tasty.Runners (Ingredient, defaultMainWithIngredients, parseOptions)
54import Control.DeepSeq (NFData(..))
55import Data.Functor.Identity (Identity, runIdentity)
56import System.Random (randomRIO)
58import qualified Streamly.Prelude as S
60import Streamly.Prelude (SerialT)
63-------------------------------------------------------------------------------
65-------------------------------------------------------------------------------
67o_1_space_prefix :: String -> String
68o_1_space_prefix name = name ++ "/o-1-space"
70o_n_space_prefix :: String -> String
71o_n_space_prefix name = name ++ "/o-n-space"
73o_n_heap_prefix :: String -> String
74o_n_heap_prefix name = name ++ "/o-n-heap"
76o_n_stack_prefix :: String -> String
77o_n_stack_prefix name = name ++ "/o-n-stack"
79-------------------------------------------------------------------------------
80-- Benchmarking utilities
81-------------------------------------------------------------------------------
83#if !MIN_VERSION_deepseq(1,4,3)
84instance NFData Ordering where rnf = (`seq` ())
87-- XXX once we convert all the functions to use this we can rename this to
89{-# INLINE benchIOSink1 #-}
90benchIOSink1 :: NFData b => String -> (Int -> IO b) -> Benchmark
91benchIOSink1 name f = bench name $ nfIO $ randomRIO (1,1) >>= f
93{-# INLINE benchIOSrc1 #-}
94benchIOSrc1 :: String -> (Int -> IO ()) -> Benchmark
95benchIOSrc1 name f = bench name $ nfIO $ randomRIO (1,1) >>= f
97-- We need a monadic bind here to make sure that the function f does not get
98-- completely optimized out by the compiler in some cases.
99{-# INLINE benchFold #-}
101 => String -> (t IO Int -> IO b) -> (Int -> t IO Int) -> Benchmark
102benchFold name f src = bench name $ nfIO $ randomRIO (1,1) >>= f . src
104{-# INLINE benchPure #-}
105benchPure :: NFData b => String -> (Int -> a) -> (a -> b) -> Benchmark
106benchPure name src f = bench name $ nfIO $ randomRIO (1,1) >>= return . f . src
108-- XXX once we convert all the functions to use this we can rename this to
110{-# INLINE benchPureSink1 #-}
111benchPureSink1 :: NFData b => String -> (Int -> Identity b) -> Benchmark
112benchPureSink1 name f =
113 bench name $ nfIO $ randomRIO (1,1) >>= return . runIdentity . f
115{-# INLINE benchPureSrc #-}
116benchPureSrc :: String -> (Int -> SerialT Identity a) -> Benchmark
117benchPureSrc name src = benchPure name src (runIdentity . S.drain)
119-------------------------------------------------------------------------------
120-- String/List generation for read instances
121-------------------------------------------------------------------------------
123{-# INLINABLE mkString #-}
124mkString :: Int -> String
125mkString value = "fromList [1" ++ concat (replicate value ",1") ++ "]"
127{-# INLINABLE mkListString #-}
128mkListString :: Int -> String
129mkListString value = "[1" ++ concat (replicate value ",1") ++ "]"
131{-# INLINABLE mkList #-}
132mkList :: Int -> [Int]
133mkList value = [1..value]
135-------------------------------------------------------------------------------
137-------------------------------------------------------------------------------
139defaultStreamSize :: Int
140defaultStreamSize = 100000
142-------------------------------------------------------------------------------
143-- Parse custom CLI options
144-------------------------------------------------------------------------------
146newtype BenchOpts = StreamSize Int deriving Show
148#ifdef MIN_VERSION_gauge
149getStreamSize :: String -> Int
151 case (readMaybe size :: Maybe Int) of
153 Nothing -> error "Stream size must be numeric"
155options :: [OptDescr BenchOpts]
158 System.Console.GetOpt.Option [] ["stream-size"] (ReqArg getSize "COUNT") "Stream element count"
163 getSize = StreamSize . getStreamSize
166 :: (Maybe String, Maybe String) -- (prev, yielded)
168 -> (Maybe String, Maybe String)
169deleteOptArgs (Nothing, _) opt =
170 if opt == "--stream-size"
171 then (Just opt, Nothing)
172 else (Just opt, Just opt)
174deleteOptArgs (Just prev, _) opt =
175 if opt == "--stream-size" || prev == "--stream-size"
176 then (Just opt, Nothing)
177 else (Just opt, Just opt)
179parseCLIOpts :: Int -> IO (Int, Config, [String])
180parseCLIOpts defStreamSize = do
182 -- Parse custom options
183 let (opts, _, _, errs) = getOpt' Permute options args
184 when (not $ null errs) $ error $ concat errs
185 (streamSize, args') <-
187 StreamSize x : _ -> do
188 putStrLn $ "Stream size: " ++ show x
189 -- When using the gauge "--measure-with" option we need to make
190 -- sure that we pass the stream size to child process forked by
191 -- gauge. So we use this env var for that purpose.
192 setEnv "STREAM_SIZE" (show x)
193 -- Hack! remove the option and its argument from args
194 -- getOpt should have a way to return the unconsumed args in
199 $ scanl' deleteOptArgs (Nothing, Nothing) args
202 r <- lookupEnv "STREAM_SIZE"
205 s <- evaluate $ getStreamSize x
208 setEnv "STREAM_SIZE" (show defStreamSize)
209 return (defStreamSize, args)
211 -- Parse gauge options
212 let config = defaultConfig
215 , includeFirstIter = streamSize > defStreamSize
217 let (cfg, benches) = parseWith config args'
218 streamSize `seq` return (streamSize, cfg, benches)
221instance IsOption BenchOpts where
222 defaultValue = StreamSize defaultStreamSize
223 parseValue = fmap StreamSize . safeRead
224 optionName = pure "stream-size"
225 optionHelp = pure "Size of the stream to be used in benchmarks"
227parseCLIOpts :: Int -> Benchmark -> IO (Int, [Ingredient])
228parseCLIOpts defStreamSize benches = do
229 let customOpts = [Test.Tasty.Options.Option (Proxy :: Proxy BenchOpts)]
230 ingredients = includingOptions customOpts : benchIngredients
231 opts <- parseOptions ingredients benches -- (TestGroup "" [])
232 let StreamSize size = lookupOption opts
233 -- putStrLn $ "Stream size: " ++ show size
234 if size == defaultStreamSize
235 then return (defStreamSize, ingredients)
236 else return (size, ingredients)
239runWithCLIOptsEnv :: Int -> (Int -> IO a) -> (a -> Int -> [Benchmark]) -> IO ()
240runWithCLIOptsEnv defStreamSize alloc mkBench = do
242#ifdef MIN_VERSION_gauge
243 (value, cfg, benches) <- parseCLIOpts defStreamSize
245 value `seq` runMode (mode cfg) cfg benches (mkBench r value)
247 (value, ingredients) <-
248 parseCLIOpts defStreamSize $ bgroup "All" (mkBench undefined 0)
250 value `seq` defaultMainWithIngredients ingredients
251 $ bgroup "All" (mkBench r value)
254runWithCLIOpts :: Int -> (Int -> [Benchmark]) -> IO ()
255runWithCLIOpts defStreamSize f =
256 runWithCLIOptsEnv defStreamSize
257 (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