on master help sign in
Add support for benchmarking with tasty-bench

tasty-bench has fewer dependencies and is agile to keep up with new GHC versions. This change is especially motivated by support for GHC 9.0.1. gauge depends on foundation/basement which lagging much behind and seem to be unmaintained.

commitb68baf3c51a4b2f086bf268d48d495bf19f12bc6 authorRanjeet Kumar Ranjan <ranjeetr@composewell.com> dateMon 19 Apr 2021, 21:23 (5 years ago) parent86b3a50
26 files changed+292−181
.github/workflows/haskell.yml+8−9 benchmark/Streamly/Benchmark/Data/Array.hs+1−3 benchmark/Streamly/Benchmark/Data/Array/Stream/Foreign.hs+2−4 benchmark/Streamly/Benchmark/Data/Fold.hs+1−3 benchmark/Streamly/Benchmark/Data/Parser.hs+14−4 benchmark/Streamly/Benchmark/Data/Parser/ParserD.hs+15−5 benchmark/Streamly/Benchmark/Data/Parser/ParserK.hs+1−3 benchmark/Streamly/Benchmark/Data/Unfold.hs+2−3 benchmark/Streamly/Benchmark/FileSystem/Handle.hs+2−3 benchmark/Streamly/Benchmark/Prelude/Ahead.hs+1−3 benchmark/Streamly/Benchmark/Prelude/Async.hs+2−3 benchmark/Streamly/Benchmark/Prelude/Concurrent.hs+3−8 benchmark/Streamly/Benchmark/Prelude/Parallel.hs+1−3 benchmark/Streamly/Benchmark/Prelude/Rate.hs+9−11 benchmark/Streamly/Benchmark/Prelude/Serial.hs+3−4 benchmark/Streamly/Benchmark/Prelude/WAsync.hs+4−4 benchmark/Streamly/Benchmark/Prelude/WSerial.hs+2−3 benchmark/Streamly/Benchmark/Prelude/ZipAsync.hs+2−3 benchmark/Streamly/Benchmark/Prelude/ZipSerial.hs+5−7 benchmark/Streamly/Benchmark/Unicode/Stream.hs+1−2 benchmark/lib/Streamly/Benchmark/Common.hs+52−5 benchmark/lib/Streamly/Benchmark/Common/Handle.hs+28−24 benchmark/streamly-benchmarks.cabal+15−1 bin/bench-exec-one.sh+53−12 bin/bench.sh+62−50 stack.yaml+3−1
.github/workflows/haskell.yml+8−9
@@ -41,16 +41,15 @@ jobs:
4141 strategy:
4242 fail-fast: false
4343 matrix:
44− name: [8.10.4+stack+lts-17.8, 8.10.2+macOS, 8.8.3+inspection+fusion-plugin+Werror, 8.8.4+stack+lts-16.31, 8.6.5+fusion-plugin, 8.6.5+streamk, 8.4.4+debug, doctests]
45− cabal_version: ["3.2"]
44+ name: [9.0.1, 8.10.4+stack+lts-17.8, 8.10.2+macOS, 8.8.3+inspection+fusion-plugin+Werror, 8.8.4+stack+lts-16.31, 8.6.5+fusion-plugin, 8.6.5+streamk, 8.4.4+debug, doctests]
45+ cabal_version: ["3.4"]
4646 include:
47− #- name: 9.0.1+no-bench
48− # ghc_version: 9.0.1
49− # build: cabal-v2
50− # cabal_build_options: "--allow-newer=hsc2hs"
51− # cabal_build_targets: "streamly streamly-tests"
52− # disable_sdist_build: "y"
53− # runner: ubuntu-latest
47+ - name: 9.0.1
48+ ghc_version: 9.0.1
49+ build: cabal-v2
50+ cabal_build_options: "--allow-newer=hsc2hs"
51+ disable_sdist_build: "y"
52+ runner: ubuntu-latest
5453 - name: 8.10.4+stack+lts-17.8
5554 build: stack
5655 resolver: lts-17.8
benchmark/Streamly/Benchmark/Data/Array.hs+1−3
@@ -183,9 +183,7 @@ defStreamSize = defaultStreamSize
183183#endif
184184
185185main :: IO ()
186−main = do
187− (value, cfg, benches) <- parseCLIOpts defStreamSize
188− value `seq` runMode (mode cfg) cfg benches (allBenchmarks value)
186+main = runWithCLIOpts defStreamSize allBenchmarks
189187
190188 where
191189
benchmark/Streamly/Benchmark/Data/Array/Stream/Foreign.hs+2−4
@@ -192,14 +192,12 @@ moduleName = "Data.Array.Stream.Foreign"
192192
193193main :: IO ()
194194main = do
195− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
196195 env <- mkHandleBenchEnv
197− value `seq` runMode (mode cfg) cfg benches
198− (allBenchmarks env)
196+ runWithCLIOpts defaultStreamSize (allBenchmarks env)
199197
200198 where
201199
202− allBenchmarks env =
200+ allBenchmarks env _ =
203201 [ bgroup (o_1_space_prefix moduleName)
204202 ( o_1_space_read_chunked env
205203 ++ o_1_space_copy_toChunks_group_ungroup env
benchmark/Streamly/Benchmark/Data/Fold.hs+1−3
@@ -319,9 +319,7 @@ o_n_heap_serial value =
319319-------------------------------------------------------------------------------
320320
321321main :: IO ()
322−main = do
323− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
324− value `seq` runMode (mode cfg) cfg benches (allBenchmarks value)
322+main = runWithCLIOpts defaultStreamSize allBenchmarks
325323
326324 where
327325
benchmark/Streamly/Benchmark/Data/Parser.hs+14−4
@@ -395,16 +395,26 @@ o_n_heap_serial value =
395395-- Driver
396396-------------------------------------------------------------------------------
397397
398+getArray :: (Int -> [Benchmark]) -> IO [Array.Array Int]
399+#ifndef MIN_VERSION_gauge
400+getArray f = do
401+ (value, _) <- parseCLIOpts defaultStreamSize $ bgroup "All" (f 0)
402+ IP.toList $ IP.arraysOf 100 $ sourceUnfoldrM value 0
403+#else
404+getArray _ = do
405+ (value, _, _) <- parseCLIOpts defaultStreamSize
406+ IP.toList $ IP.arraysOf 100 $ sourceUnfoldrM value 0
407+#endif
408+
398409main :: IO ()
399410main = do
400− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
401411 env <- mkHandleBenchEnv
402− arrays <- IP.toList $ IP.arraysOf 100 $ sourceUnfoldrM value 0
403− value `seq` runMode (mode cfg) cfg benches (allBenchmarks env value arrays)
412+ arrays <- getArray (allBenchmarks env [])
413+ runWithCLIOpts defaultStreamSize (allBenchmarks env arrays)
404414
405415 where
406416
407− allBenchmarks env value arrays =
417+ allBenchmarks env arrays value =
408418 [ bgroup (o_1_space_prefix moduleName) (o_1_space_serial value)
409419 , bgroup
410420 (o_1_space_prefix moduleName ++ "/filesystem")
benchmark/Streamly/Benchmark/Data/Parser/ParserD.hs+15−5
@@ -5,6 +5,7 @@
55-- License : BSD-3-Clause
66-- Maintainer : streamly@composewell.com
77
8+{-# LANGUAGE CPP #-}
89{-# LANGUAGE FlexibleContexts #-}
910{-# LANGUAGE ScopedTypeVariables #-}
1011{-# OPTIONS_GHC -fspec-constr-recursive=4 #-}
@@ -358,16 +359,25 @@ o_n_space_serial value =
358359-- Driver
359360-------------------------------------------------------------------------------
360361
362+getArray :: (Int -> [Benchmark]) -> IO [Array.Array Int]
363+#ifndef MIN_VERSION_gauge
364+getArray f = do
365+ (value, _) <- parseCLIOpts defaultStreamSize $ bgroup "All" (f 0)
366+ IP.toList $ IP.arraysOf 100 $ sourceUnfoldrM value 0
367+#else
368+getArray _ = do
369+ (value, _, _) <- parseCLIOpts defaultStreamSize
370+ IP.toList $ IP.arraysOf 100 $ sourceUnfoldrM value 0
371+#endif
372+
361373main :: IO ()
362374main = do
363− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
364− arraysSmall <- IP.toList $ IP.arraysOf 100 $ sourceUnfoldrM value 0
365− value `seq` runMode (mode cfg) cfg benches
366− (allBenchmarks value arraysSmall)
375+ arrays <- getArray (allBenchmarks [])
376+ runWithCLIOpts defaultStreamSize (allBenchmarks arrays)
367377
368378 where
369379
370− allBenchmarks value arraysSmall =
380+ allBenchmarks arraysSmall value =
371381 [ bgroup (o_1_space_prefix moduleName) (o_1_space_serial value)
372382 , bgroup (o_1_space_prefix moduleName) (o_1_space_serial_spanning value)
373383 , bgroup (o_1_space_prefix moduleName) (o_1_space_serial_nested value)
benchmark/Streamly/Benchmark/Data/Parser/ParserK.hs+1−3
@@ -153,9 +153,7 @@ o_n_heap_serial value =
153153-------------------------------------------------------------------------------
154154
155155main :: IO ()
156−main = do
157− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
158− value `seq` runMode (mode cfg) cfg benches (allBenchmarks value)
156+main = runWithCLIOpts defaultStreamSize allBenchmarks
159157
160158 where
161159
benchmark/Streamly/Benchmark/Data/Unfold.hs+2−3
@@ -799,13 +799,12 @@ o_1_space_copy_read_exceptions env =
799799
800800main :: IO ()
801801main = do
802− (size, cfg, benches) <- parseCLIOpts defaultStreamSize
803802 env <- mkHandleBenchEnv
804− size `seq` runMode (mode cfg) cfg benches (allBenchmarks size env)
803+ runWithCLIOpts defaultStreamSize (allBenchmarks env)
805804
806805 where
807806
808− allBenchmarks size env =
807+ allBenchmarks env size =
809808 [ bgroup (o_1_space_prefix moduleName)
810809 $ Prelude.concat
811810 [ o_1_space_transformation_input size
benchmark/Streamly/Benchmark/FileSystem/Handle.hs+2−3
@@ -43,13 +43,12 @@ moduleName = "FileSystem.Handle"
4343
4444main :: IO ()
4545main = do
46− (_, cfg, benches) <- parseCLIOpts defaultStreamSize
4746 env <- mkHandleBenchEnv
48− runMode (mode cfg) cfg benches (allBenchmarks env)
47+ runWithCLIOpts defaultStreamSize (allBenchmarks env)
4948
5049 where
5150
52− allBenchmarks env =
51+ allBenchmarks env _ =
5352 [ bgroup (o_1_space_prefix moduleName) $ Prelude.concat
5453 [ RO.allBenchmarks env
5554 , RW.allBenchmarks env
benchmark/Streamly/Benchmark/Prelude/Ahead.hs+1−3
@@ -117,9 +117,7 @@ o_n_space_outerProduct value =
117117-------------------------------------------------------------------------------
118118
119119main :: IO ()
120−main = do
121− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
122− value `seq` runMode (mode cfg) cfg benches (allBenchmarks value)
120+main = runWithCLIOpts defaultStreamSize allBenchmarks
123121
124122 where
125123
benchmark/Streamly/Benchmark/Prelude/Async.hs+2−3
@@ -183,9 +183,8 @@ o_n_space_outerProduct value =
183183-------------------------------------------------------------------------------
184184
185185main :: IO ()
186−main = do
187− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
188− value `seq` runMode (mode cfg) cfg benches (allBenchmarks value)
186+main = runWithCLIOpts defaultStreamSize allBenchmarks
187+
189188
190189 where
191190
benchmark/Streamly/Benchmark/Prelude/Concurrent.hs+3−8
@@ -1,4 +1,3 @@
1−{-# LANGUAGE RankNTypes #-}
21-- |
32-- Module : Main
43-- Copyright : (c) 2018 Composewell Technologies
@@ -6,6 +5,8 @@
65-- License : BSD3
76-- Maintainer : streamly@composewell.com
87
8+{-# LANGUAGE RankNTypes #-}
9+
910import Control.Concurrent
1011import Control.Monad (when, replicateM)
1112import Streamly.Prelude
@@ -77,13 +78,7 @@ concatGroup buflen threads usec n =
7778
7879main :: IO ()
7980main =
80− defaultMainWith (defaultConfig
81− { timeLimit = Just 0
82− , minSamples = Just 1
83− , minDuration = 0
84− , includeFirstIter = True
85− , quickMode = True
86− })
81+ defaultMain
8782
8883 [ -- bgroup "append/buf-1-threads-10k-0sec" (appendGroup 1 10000 0)
8984 -- , bgroup "append/buf-100-threads-100k-0sec" (appendGroup 100 100000 0)
benchmark/Streamly/Benchmark/Prelude/Parallel.hs+1−3
@@ -219,9 +219,7 @@ o_n_space_outerProduct value =
219219-------------------------------------------------------------------------------
220220
221221main :: IO ()
222−main = do
223− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
224− value `seq` runMode (mode cfg) cfg benches (allBenchmarks value)
222+main = runWithCLIOpts defaultStreamSize allBenchmarks
225223
226224 where
227225
benchmark/Streamly/Benchmark/Prelude/Rate.hs+9−11
@@ -1,4 +1,5 @@
11{-# LANGUAGE FlexibleContexts #-}
2+{-# LANGUAGE CPP #-}
23
34-- |
45-- Module : Main
@@ -87,12 +88,12 @@ o_1_space_async value =
8788o_1_space_ahead :: Int -> [Benchmark]
8889o_1_space_ahead value =
8990 [ bgroup
90− "aheadly"
91− [ benchIOSrc fromAhead "avgRate/1M" $ avgRate value 1000000
92− , benchIOSrc fromAhead "minRate/1M" $ minRate value 1000000
93− , benchIOSrc fromAhead "maxRate/1M" $ maxRate value 1000000
94− , benchIOSrc fromAsync "constRate/1M" $ constRate value 1000000
95− ]
91+ "aheadly"
92+ [ benchIOSrc fromAhead "avgRate/1M" $ avgRate value 1000000
93+ , benchIOSrc fromAhead "minRate/1M" $ minRate value 1000000
94+ , benchIOSrc fromAhead "maxRate/1M" $ maxRate value 1000000
95+ , benchIOSrc fromAsync "constRate/1M" $ constRate value 1000000
96+ ]
9697 ]
9798
9899-------------------------------------------------------------------------------
@@ -100,13 +101,10 @@ o_1_space_ahead value =
100101-------------------------------------------------------------------------------
101102
102103main :: IO ()
103−main = do
104− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
105− value `seq` runMode (mode cfg) cfg benches (allBenchmarks value)
104+main = runWithCLIOpts defaultStreamSize allBenchmarks
106105
107106 where
108107
109108 allBenchmarks value =
110109 [ bgroup (o_1_space_prefix moduleName)
111− $ concat [o_1_space_async value, o_1_space_ahead value]
112− ]
110+ $ concat [o_1_space_async value, o_1_space_ahead value]]
benchmark/Streamly/Benchmark/Prelude/Serial.hs+3−4
@@ -22,7 +22,6 @@ import qualified Serial.Transformation1 as Transformation1
2222import qualified Serial.Transformation2 as Transformation2
2323import qualified Serial.Transformation3 as Transformation3
2424
25−import Gauge hiding (env)
2625import Streamly.Benchmark.Common
2726
2827moduleName :: String
@@ -37,12 +36,12 @@ moduleName = "Prelude.Serial"
3736--
3837main :: IO ()
3938main = do
40− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
4139 env <- mkHandleBenchEnv
42− value `seq` runMode (mode cfg) cfg benches (allBenchmarks value env)
40+ runWithCLIOpts defaultStreamSize (allBenchmarks env)
41+
4342 where
4443
45− allBenchmarks size env = Prelude.concat
44+ allBenchmarks env size = Prelude.concat
4645 [ Generation.benchmarks moduleName size
4746 , Elimination.benchmarks moduleName size
4847 , Exceptions.benchmarks moduleName env
benchmark/Streamly/Benchmark/Prelude/WAsync.hs+4−4
@@ -5,6 +5,8 @@
55-- License : BSD3
66-- Maintainer : streamly@composewell.com
77
8+{-# LANGUAGE CPP #-}
9+
810import Prelude hiding (mapM)
911
1012import Streamly.Prelude (fromWAsync, fromSerial, wAsync, maxBuffer, maxThreads)
@@ -167,10 +169,8 @@ o_n_space_outerProduct value =
167169-------------------------------------------------------------------------------
168170
169171main :: IO ()
170−main = do
171− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
172− value `seq` runMode (mode cfg) cfg benches (allBenchmarks value)
173−
172+main = runWithCLIOpts defaultStreamSize allBenchmarks
173+
174174 where
175175
176176 allBenchmarks value =
benchmark/Streamly/Benchmark/Prelude/WSerial.hs+2−3
@@ -201,9 +201,8 @@ o_n_space_outerProduct value =
201201-- passed using the --stream-size option.
202202--
203203main :: IO ()
204−main = do
205− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
206− value `seq` runMode (mode cfg) cfg benches (allBenchmarks value)
204+main = runWithCLIOpts defaultStreamSize allBenchmarks
205+
207206
208207 where
209208
benchmark/Streamly/Benchmark/Prelude/ZipAsync.hs+2−3
@@ -6,6 +6,7 @@
66-- Maintainer : streamly@composewell.com
77
88{-# LANGUAGE FlexibleContexts #-}
9+{-# LANGUAGE CPP #-}
910
1011import Streamly.Prelude (fromSerial)
1112import qualified Streamly.Prelude as S
@@ -69,9 +70,7 @@ o_1_space_outerProduct value =
6970-------------------------------------------------------------------------------
7071
7172main :: IO ()
72−main = do
73− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
74− value `seq` runMode (mode cfg) cfg benches (allBenchmarks value)
73+main = runWithCLIOpts defaultStreamSize allBenchmarks
7574
7675 where
7776
benchmark/Streamly/Benchmark/Prelude/ZipSerial.hs+5−7
@@ -60,8 +60,8 @@ zipWith count n =
6060 S.drain $
6161 S.zipWith
6262 (,)
63− (S.fromSerial $ sourceUnfoldrM count n)
64− (S.fromSerial $ sourceUnfoldrM count (n + 1))
63+ (S.fromSerial $ Main.sourceUnfoldrM count n)
64+ (S.fromSerial $ Main.sourceUnfoldrM count (n + 1))
6565
6666#ifdef INSPECTION
6767inspect $ hasNoTypeClasses 'zipWith
@@ -75,8 +75,8 @@ zipWithM count n =
7575 S.drain $
7676 S.zipWithM
7777 (curry return)
78− (sourceUnfoldrM count n)
79− (sourceUnfoldrM count (n + 1))
78+ (Main.sourceUnfoldrM count n)
79+ (Main.sourceUnfoldrM count (n + 1))
8080
8181#ifdef INSPECTION
8282inspect $ hasNoTypeClasses 'zipWithM
@@ -125,9 +125,7 @@ o_1_space_outerProduct value =
125125-- passed using the --stream-size option.
126126--
127127main :: IO ()
128−main = do
129− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
130− value `seq` runMode (mode cfg) cfg benches (allBenchmarks value)
128+main = runWithCLIOpts defaultStreamSize allBenchmarks
131129
132130 where
133131
benchmark/Streamly/Benchmark/Unicode/Stream.hs+1−2
@@ -288,9 +288,8 @@ o_1_space_decode_encode_read env =
288288
289289main :: IO ()
290290main = do
291− (_, cfg, benches) <- parseCLIOpts defaultStreamSize
292291 env <- mkHandleBenchEnv
293− runMode (mode cfg) cfg benches (allBenchmarks env)
292+ defaultMain (allBenchmarks env)
294293
295294 where
296295
benchmark/lib/Streamly/Benchmark/Common.hs+52−5
@@ -16,7 +16,9 @@ module Streamly.Benchmark.Common
1616 , o_n_heap_prefix
1717 , o_n_stack_prefix
1818
19+ -- , parseEnvOpts
1920 , parseCLIOpts
21+ , runWithCLIOpts
2022
2123 , benchIOSink1
2224 , benchPure
@@ -31,19 +33,34 @@ module Streamly.Benchmark.Common
3133 , mkListString
3234
3335 , defaultStreamSize
36+ , BenchOpts(..)
37+#ifndef MIN_VERSION_gauge
38+ , OptionDescription(..)
39+ , includingOptions
40+ , lookupOption
41+ , defaultMainWithIngredients
42+ , parseOptions
43+#endif
3444 )
3545where
3646
37−import Control.DeepSeq (NFData(..))
47+#ifdef MIN_VERSION_gauge
3848import Control.Exception (evaluate)
3949import Control.Monad (when)
40−import Data.Functor.Identity (Identity, runIdentity)
50+import Text.Read (readMaybe)
4151import Data.List (scanl')
4252import Data.Maybe (mapMaybe)
4353import System.Console.GetOpt
4454 (OptDescr(..), ArgDescr(..), ArgOrder(..), getOpt')
4555import System.Environment (getArgs, lookupEnv, setEnv)
46−import Text.Read (readMaybe)
56+#else
57+import Data.Proxy
58+import Test.Tasty.Ingredients.Basic
59+import Test.Tasty.Options
60+import Test.Tasty.Runners
61+#endif
62+import Control.DeepSeq (NFData(..))
63+import Data.Functor.Identity (Identity, runIdentity)
4764import System.Random (randomRIO)
4865
4966import qualified Streamly.Prelude as S
@@ -136,6 +153,7 @@ defaultStreamSize = 100000
136153
137154newtype BenchOpts = StreamSize Int deriving Show
138155
156+#ifdef MIN_VERSION_gauge
139157getStreamSize :: String -> Int
140158getStreamSize size =
141159 case (readMaybe size :: Maybe Int) of
@@ -145,7 +163,7 @@ getStreamSize size =
145163options :: [OptDescr BenchOpts]
146164options =
147165 [
148− Option [] ["stream-size"] (ReqArg getSize "COUNT") "Stream element count"
166+ System.Console.GetOpt.Option [] ["stream-size"] (ReqArg getSize "COUNT") "Stream element count"
149167 ]
150168
151169 where
@@ -169,7 +187,6 @@ deleteOptArgs (Just prev, _) opt =
169187parseCLIOpts :: Int -> IO (Int, Config, [String])
170188parseCLIOpts defStreamSize = do
171189 args <- getArgs
172−
173190 -- Parse custom options
174191 let (opts, _, _, errs) = getOpt' Permute options args
175192 when (not $ null errs) $ error $ concat errs
@@ -207,3 +224,33 @@ parseCLIOpts defStreamSize = do
207224 }
208225 let (cfg, benches) = parseWith config args'
209226 streamSize `seq` return (streamSize, cfg, benches)
227+
228+#else
229+instance IsOption BenchOpts where
230+ defaultValue = StreamSize defaultStreamSize
231+ parseValue = fmap StreamSize . safeRead
232+ optionName = pure "stream-size"
233+ optionHelp = pure "StreamSize used in benchmarks"
234+
235+parseCLIOpts :: Int -> Benchmark -> IO (Int, [Ingredient])
236+parseCLIOpts cDefSize benches = do
237+ let customOpts = [Test.Tasty.Options.Option (Proxy :: Proxy BenchOpts)]
238+ ingredients = includingOptions customOpts : benchIngredients
239+ opts <- parseOptions ingredients benches
240+ let StreamSize size = lookupOption opts
241+ print $ "Stream-Size = " ++ show size
242+ if size == defaultStreamSize -- LONG option is not set
243+ then return (cDefSize, ingredients) -- use custom defaut size of Benchmark
244+ else return (size, ingredients) -- LONG option is set use large stream size
245+#endif
246+
247+runWithCLIOpts :: Int -> (Int -> [Benchmark]) -> IO ()
248+runWithCLIOpts cDefSize f = do
249+
250+#ifdef MIN_VERSION_gauge
251+ (value, cfg, benches) <- parseCLIOpts cDefSize
252+ value `seq` runMode (mode cfg) cfg benches (f value)
253+#else
254+ (value, ingredients) <- parseCLIOpts cDefSize $ bgroup "All" (f 0)
255+ value `seq` defaultMainWithIngredients ingredients $ bgroup "All" (f value)
256+#endif
benchmark/lib/Streamly/Benchmark/Common/Handle.hs+28−24
@@ -86,6 +86,29 @@ withScaling env str =
8686 then str
8787 else str ++ " (1/" ++ show factor ++ ")"
8888
89+getHandles :: BenchEnv -> (RefHandles -> Handles) -> IO Handles
90+getHandles env mkHandles = do
91+ r <- readIORef $ href env
92+ -- close old handles
93+ hClose $ smallInH r
94+ hClose $ bigInH r
95+ hClose $ outputH r
96+
97+ -- reopen
98+ smallInHandle <- openFile (smallInFile env) ReadMode
99+ bigInHandle <- openFile (bigInFile env) ReadMode
100+ outHandle <- openFile outfile WriteMode
101+
102+ let refHandles = RefHandles
103+ { smallInH = smallInHandle
104+ , bigInH = bigInHandle
105+ , outputH = outHandle
106+ }
107+
108+ -- update
109+ writeIORef (href env) $ refHandles
110+ return $ mkHandles refHandles
111+
89112mkBenchCommon ::
90113 NFData b
91114 => (RefHandles -> Handles)
@@ -94,30 +117,11 @@ mkBenchCommon ::
94117 -> (Handle -> Handle -> IO b)
95118 -> Benchmark
96119mkBenchCommon mkHandles name env action =
97− bench name $ perRunEnv (do
98− r <- readIORef $ href env
99−
100− -- close old handles
101− hClose $ smallInH r
102− hClose $ bigInH r
103− hClose $ outputH r
104−
105− -- reopen
106− smallInHandle <- openFile (smallInFile env) ReadMode
107− bigInHandle <- openFile (bigInFile env) ReadMode
108− outHandle <- openFile outfile WriteMode
109−
110− let refHandles = RefHandles
111− { smallInH = smallInHandle
112− , bigInH = bigInHandle
113− , outputH = outHandle
114− }
115−
116− -- update
117− writeIORef (href env) $ refHandles
118− return $ mkHandles refHandles
119− )
120− (\(Handles h1 h2) -> action h1 h2)
120+ bench name $ nfIO $ do
121+ -- XXX adds significant cpu time to the benchmarks
122+ -- tasty-bench should provide a better way to do this
123+ (Handles h1 h2) <- getHandles env mkHandles
124+ action h1 h2
121125
122126mkBench ::
123127 NFData b => String -> BenchEnv -> (Handle -> Handle -> IO b) -> Benchmark
benchmark/streamly-benchmarks.cabal+15−1
@@ -54,6 +54,11 @@ flag opt
5454 manual: True
5555 default: True
5656
57+flag use-gauge
58+ description: Use gauge instead of tasty-bench for benchmarking
59+ manual: True
60+ default: False
61+
5762-------------------------------------------------------------------------------
5863-- Common stanzas
5964-------------------------------------------------------------------------------
@@ -113,13 +118,22 @@ common bench-depends
113118 -- other libraries
114119 , streamly >= 0.7.0
115120 , random >= 1.0 && < 2.0
116− , gauge >= 0.2.4 && < 0.3
117121 , transformers >= 0.4 && < 0.6
118122 , containers >= 0.5 && < 0.7
119123 , typed-process >= 0.2.3 && < 0.3
120124 , directory >= 1.2.2 && < 1.4
121125 , ghc-prim >= 0.4 && < 0.8
122126
127+ if flag(use-gauge)
128+ build-depends: gauge >= 0.2.4 && < 0.3
129+ else
130+ build-depends: tasty-bench >= 0.2.5 && < 0.3
131+ , tasty >= 1.4.1
132+ mixins: tasty-bench
133+ (Test.Tasty.Bench as Gauge
134+ , Test.Tasty.Bench as Gauge.Main
135+ )
136+
123137 if flag(fusion-plugin) && !impl(ghcjs) && !impl(ghc < 8.6)
124138 build-depends:
125139 fusion-plugin >= 0.2 && < 0.3
bin/bench-exec-one.sh+53−12
@@ -100,9 +100,15 @@ bench_rts_opts_specific () {
100100# Speed options
101101#------------------------------------------------------------------------------
102102
103−# Do not keep time limit as 0 otherwise GC stats may remain 0 in some cases.
104−SUPER_QUICK_OPTIONS="--quick --min-duration 0 --time-limit 0.01 --include-first-iter"
105−QUICKER_OPTIONS="--min-samples 3 --time-limit 1"
103+if test "$USE_GAUGE" -eq 0
104+then
105+ SUPER_QUICK_OPTIONS="--stdev 1000000"
106+ QUICKER_OPTIONS="--stdev 1000"
107+else
108+ # Do not keep time limit as 0 otherwise GC stats may remain 0 in some cases.
109+ SUPER_QUICK_OPTIONS="--quick --min-duration 0 --time-limit 0.01 --include-first-iter"
110+ QUICKER_OPTIONS="--min-samples 3 --time-limit 1"
111+fi
106112
107113# For certain long benchmarks if the user has not requested super quick
108114# mode we anyway use a slightly quicker mode.
@@ -152,13 +158,38 @@ bench_quick_opts () {
152158 esac
153159}
154160
155−last=""
161+bench_output_file() {
162+ local bench_name=$1
163+ echo "charts/$bench_name/results.csv"
164+}
165+
166+BENCH_NAME_ORIG=""
156167for i in "$@"
157168do
158− BENCH_NAME="$last"
159− last="$i"
169+ BENCH_NAME_ORIG="$i"
160170done
161171
172+if test "$USE_GAUGE" -eq 0
173+then
174+ # XXX this is a hack to make the "/" separated names used in the functions
175+ # determining options based on benchmark name. For tasty-bench the benchmark
176+ # names are separated by "." instead of "/".
177+ BENCH_NAME=$(echo $BENCH_NAME_ORIG | sed -e s/^All\.//)
178+ BENCH_NAME1=$(echo $BENCH_NAME | cut -f1 -d '/')
179+ BENCH_NAME2=$(echo $BENCH_NAME | cut -f2- -d '/' | sed -e 's/\./\//g')
180+ BENCH_NAME="$BENCH_NAME1/$BENCH_NAME2"
181+ JOB_OPT=" -j 1"
182+else
183+ BENCH_NAME=$BENCH_NAME_ORIG
184+ JOB_OPT=""
185+fi
186+if test "$LONG" -eq 0
187+then
188+ SIZE_OPT=""
189+else
190+ SIZE_OPT="--stream-size 1000000"
191+fi
192+
162193RTS_OPTIONS=\
163194"+RTS -T \
164195$(bench_exe_rts_opts $(basename $BENCH_EXEC_PATH)) \
@@ -168,17 +199,27 @@ $RTS_OPTIONS \
168199-RTS"
169200
170201QUICK_BENCH_OPTIONS="\
202+$(if test "$QUICK_MODE" -ne 0; then echo $SUPER_QUICK_OPTIONS; else :; fi)
171203$(bench_exe_quick_opts $(basename $BENCH_EXEC_PATH)) \
172204$(bench_quick_opts $BENCH_NAME)"
173205
174−if test -n "$STREAM_SIZE"
175−then
176− STREAM_LEN=$(env LC_ALL=en_US.UTF-8 printf "\--stream-size %'.f\n" $STREAM_SIZE)
177−fi
206+output_file=$(bench_output_file $(basename $BENCH_EXEC_PATH))
207+mkdir -p `dirname $output_file`
178208
179209echo "$BENCH_NAME: \
180−$STREAM_LEN \
181210$QUICK_BENCH_OPTIONS \
182211$RTS_OPTIONS"
183212
184−$BENCH_EXEC_PATH $RTS_OPTIONS "$@" $QUICK_BENCH_OPTIONS
213+rm -f ${output_file}.tmp
214+if test $USE_GAUGE -eq 0
215+then
216+ BENCH_NAME_ESC=$(echo "$BENCH_NAME_ORIG" | sed -e 's/\\/\\\\/g' | sed -e 's/"/\\"/g')
217+ $BENCH_EXEC_PATH $SIZE_OPT $JOB_OPT $RTS_OPTIONS $QUICK_BENCH_OPTIONS --csv=${output_file}.tmp \
218+ -p '$0 == "'"$BENCH_NAME_ESC"'"'
219+ tail -n +2 ${output_file}.tmp | \
220+ awk 'BEGIN {FPAT = "([^,]+)|(\"[^\"]+\")";OFS=","} {$2=$2/1000000000000;print}' >> $output_file
221+else
222+ $BENCH_EXEC_PATH $SIZE_OPT $RTS_OPTIONS $QUICK_BENCH_OPTIONS --csvraw=${output_file}.tmp \
223+ -m exact "$BENCH_NAME"
224+ tail -n +2 ${output_file}.tmp >> $output_file
225+fi
bin/bench.sh+62−50
@@ -142,62 +142,71 @@ bench_output_file() {
142142 echo "charts/$bench_name/results.csv"
143143}
144144
145−# --min-duration 0 means exactly one iteration per sample. We use a million
146−# iterations in the benchmarking code explicitly and do not use the iterations
147−# done by the benchmarking tool.
148−#
149−# Benchmarking tool by default discards the first iteration to remove
150−# aberrations due to initial evaluations etc. We do not discard it because we
151−# are anyway doing iterations in the benchmarking code and many of them so that
152−# any constant factor gets amortized and anyway it is a cost that we pay in
153−# real life.
154−#
155−# We can pass --min-samples value from the command line as second argument
156−# after the benchmark name in case we want to use more than one sample.
157−
158−# $1: bench name
159−# $2: bench executable
160−target_exe_extra_args () {
161− local bench_name=$1
162− local bench_prog=$2
145+run_bench_target () {
146+ local package_name=$1
147+ local component=$2
148+ local target_name=$3
149+ local output_file=$(bench_output_file $target_name)
163150
164− local output_file=$(bench_output_file $bench_name)
165− mkdir -p `dirname $output_file`
151+ local target_prog
152+ target_prog=$(cabal_target_prog $package_name $component $target_name) || \
153+ die "Cannot find executable for target $target_name"
166154
167− local QUICK_OPTS="--quick --min-duration 0"
168− local SPEED_OPTIONS
169− if test "$LONG" -eq 0
155+ echo "Running executable $target_name ..."
156+
157+ # Needed by bench-exec-one.sh
158+ export BENCH_EXEC_PATH=$target_prog
159+ if test "$LONG" -ne 0
170160 then
171− if test "$SLOW" -eq 0
161+ BENCH_ARGS="-p /$target_name\/o-1-space/"
162+ STREAM_SIZE=10000000
163+ export STREAM_SIZE
164+ fi
165+
166+ local MATCH=""
167+ if test "$USE_GAUGE" -eq 0
168+ then
169+ if test "$LONG" -ne 0
172170 then
173− if test "$QUICK_MODE" -eq 0
171+ MATCH="-p /$target_name\/o-1-space/"
172+ else
173+ if test -n "$GAUGE_ARGS"
174174 then
175− # default mode, not super quick, not slow
176− SPEED_OPTIONS="$QUICK_OPTS --min-samples 10 --time-limit 1"
177− else
178− # super quick but less accurate
179− # When the time-limit is too low and the benchmark is tiny,
180− # then if the number of iterations is very small the GC stats
181− # may remain 0. So keep the time-limit at a minimum of 10 ms
182− # to collect significant stats. The problem was observed in
183− # the Prelude.Serial/reverse' benchmark.
184− SPEED_OPTIONS="$QUICK_OPTS --time-limit 0.01 --include-first-iter"
175+ local GAUGE_ARGS1=$(echo "$GAUGE_ARGS" | sed -e 's/\//\\\//g')
176+ MATCH="-p /$GAUGE_ARGS1/"
185177 fi
186− else
187− # Slow but more accurate mode
188− SPEED_OPTIONS="--min-duration 0"
189178 fi
179+ echo "Name,cpuTime,2*Stdev (ps),Allocated,bytesCopied" >> $output_file
180+ $target_prog -l $MATCH \
181+ | grep "^All" \
182+ | while read -r name; do bin/bench-exec-one.sh "$name"; done
190183 else
191− # large stream size, always super quick
192− GAUGE_ARGS="$GAUGE_ARGS $bench_name/o-1-space"
193− SPEED_OPTIONS="--stream-size 10000000 $QUICK_OPTS --include-first-iter"
184+ if test "$LONG" -ne 0
185+ then
186+ MATCH="$target_name/o-1-space"
187+ else
188+ MATCH="$GAUGE_ARGS"
189+ fi
190+ echo "name,iters,time,cycles,cpuTime,utime,stime,maxrss,minflt,majflt,nvcsw,nivcsw,allocated,numGcs,bytesCopied,mutatorWallSeconds,mutatorCpuSeconds,gcWallSeconds,gcCpuSeconds" >> $output_file
191+ # XXX We may have to use "sort | awk" to keep only benchmark names with
192+ # shortest prefix e.g. "a/b/c" and "a/b", we should only keep "a/b"
193+ # otherwise benchmarks will run multiple times.
194+ $target_prog -l \
195+ | grep "^$target_name" \
196+ | grep "^$MATCH" \
197+ | sort | paste -sd "," - | awk 'BEGIN {FS=","} {t="XU987"; for(i=1;i<=NF;i++) if (substr($i,1,length(t)) != t) {print $i; t=$i}}' \
198+ | while read -r name; do bin/bench-exec-one.sh "$name"; done
194199 fi
200+}
195201
196− echo "$SPEED_OPTIONS \
197− --csvraw=$output_file \
198− -v 2 \
199− --measure-with "$SCRIPT_DIR/bench-exec-one.sh" \
200− $GAUGE_ARGS"
202+# $1: package name
203+# $2: component
204+# $3: targets
205+run_bench_targets() {
206+ for i in $3
207+ do
208+ run_bench_target $1 $2 $i
209+ done
201210}
202211
203212run_benches_comparing() {
@@ -217,14 +226,14 @@ run_benches_comparing() {
217226 git checkout "$BASE" || die "Checkout of base commit [$BASE] failed"
218227
219228 $BUILD_BENCH || die "build failed"
220− run_targets streamly-benchmarks b "$bench_list" target_exe_extra_args
229+ run_bench_targets streamly-benchmarks b "$bench_list" target_exe_extra_args
221230
222231 echo "Checking out candidate commit [$CANDIDATE] for benchmarking"
223232 git checkout "$CANDIDATE" || \
224233 die "Checkout of candidate [$CANDIDATE] commit failed"
225234
226235 $BUILD_BENCH || die "build failed"
227− run_targets streamly-benchmarks b "$bench_list" target_exe_extra_args
236+ run_bench_targets streamly-benchmarks b "$bench_list" target_exe_extra_args
228237 # XXX reset back to the original commit
229238}
230239
@@ -248,7 +257,7 @@ run_measurements() {
248257
249258 if test "$COMPARE" = "0"
250259 then
251− run_targets streamly-benchmarks b "$bench_list" target_exe_extra_args
260+ run_bench_targets streamly-benchmarks b "$bench_list" target_exe_extra_args
252261 else
253262 run_benches_comparing "$bench_list"
254263 fi
@@ -274,6 +283,8 @@ run_reports() {
274283
275284cd $SCRIPT_DIR/..
276285
286+USE_GAUGE=0
287+export USE_GAUGE
277288USE_GIT_CABAL=1
278289set_common_vars
279290
@@ -323,7 +334,7 @@ do
323334 --long) LONG=1; shift ;;
324335 --graphs) GRAPH=1; shift ;;
325336 --no-measure) MEASURE=0; shift ;;
326− --dev-build) RUNNING_DEVBUILD=1; shift ;;
337+ --dev-build) RUNNING_DEVBUILD=1; shift ;;
327338 --) shift; break ;;
328339 -*|--*) echo "Unknown flags: $*"; echo; print_help ;;
329340 *) break ;;
@@ -409,6 +420,7 @@ then
409420 run_build "$BUILD_BENCH" streamly-benchmarks bench "$TARGETS"
410421 export QUICK_MODE
411422 export RTS_OPTIONS
423+ export LONG
412424 run_measurements "$TARGETS"
413425fi
414426
stack.yaml+3−1
@@ -1,4 +1,4 @@
1−resolver: lts-16.12
1+resolver: lts-17.9
22packages:
33- '.'
44- './benchmark'
@@ -8,6 +8,8 @@ extra-deps:
88- QuickCheck-2.14.1@sha256:01e46d7b0a8d3148288ec977625f62d5516ebb5031a50c63f0453863301b4a79,7736
99- random-1.2.0@sha256:4969b16ef23e9a57a415abcd39d982c81844031557b66349651b0fe3255b8986,6094
1010- splitmix-0.1.0.1@sha256:22f9662e7e8b173421872241edd39350078a9ed4bb9e9f503948c5b483c79276,5253
11+- tasty-bench-0.2.5@sha256:4f00fdafc3aba6e48366a3684c427618d98c0b1b130cd9cab35c9498796c9a65,1497
12+- tasty-1.4.1@sha256:69e90e965543faf0fc2c8e486d6c1d8cf81fd108e2c4541234c41490f392f94f,2638
1113
1214#allow-newer: true
1315flags: {}