on master help sign in
Add support for benchmarking with tasty-bench
b68baf3Ranjeet Kumar Ranjan
.github/workflows/haskell.yml+8−9
@@ -41,16 +41,15 @@ jobs:
41 strategy:
41 strategy:
42 fail-fast: false
42 fail-fast: false
43 matrix:
43 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]
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.2"]
45+ cabal_version: ["3.4"]
46 include:
46 include:
47− #- name: 9.0.1+no-bench
47+ - name: 9.0.1
48− # ghc_version: 9.0.1
48+ ghc_version: 9.0.1
49− # build: cabal-v2
49+ build: cabal-v2
50− # cabal_build_options: "--allow-newer=hsc2hs"
50+ cabal_build_options: "--allow-newer=hsc2hs"
51− # cabal_build_targets: "streamly streamly-tests"
52− # disable_sdist_build: "y"
51+ disable_sdist_build: "y"
53− # runner: ubuntu-latest
52+ runner: ubuntu-latest
54 - name: 8.10.4+stack+lts-17.8
53 - name: 8.10.4+stack+lts-17.8
55 build: stack
54 build: stack
56 resolver: lts-17.8
55 resolver: lts-17.8
benchmark/Streamly/Benchmark/Data/Array.hs+1−3
@@ -183,9 +183,7 @@ defStreamSize = defaultStreamSize
183#endif
183#endif
184
184
185main :: IO ()
185main :: IO ()
186−main = do
186+main = runWithCLIOpts defStreamSize allBenchmarks
187− (value, cfg, benches) <- parseCLIOpts defStreamSize
188− value `seq` runMode (mode cfg) cfg benches (allBenchmarks value)
189
187
190 where
188 where
191
189
benchmark/Streamly/Benchmark/Data/Array/Stream/Foreign.hs+2−4
@@ -192,14 +192,12 @@ moduleName = "Data.Array.Stream.Foreign"
192
192
193main :: IO ()
193main :: IO ()
194main = do
194main = do
195− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
196 env <- mkHandleBenchEnv
195 env <- mkHandleBenchEnv
197− value `seq` runMode (mode cfg) cfg benches
198− (allBenchmarks env)
196+ runWithCLIOpts defaultStreamSize (allBenchmarks env)
199
197
200 where
198 where
201
199
202− allBenchmarks env =
200+ allBenchmarks env _ =
203 [ bgroup (o_1_space_prefix moduleName)
201 [ bgroup (o_1_space_prefix moduleName)
204 ( o_1_space_read_chunked env
202 ( o_1_space_read_chunked env
205 ++ o_1_space_copy_toChunks_group_ungroup env
203 ++ 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 =
319-------------------------------------------------------------------------------
319-------------------------------------------------------------------------------
320
320
321main :: IO ()
321main :: IO ()
322−main = do
322+main = runWithCLIOpts defaultStreamSize allBenchmarks
323− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
324− value `seq` runMode (mode cfg) cfg benches (allBenchmarks value)
325
323
326 where
324 where
327
325
benchmark/Streamly/Benchmark/Data/Parser.hs+14−4
@@ -395,16 +395,26 @@ o_n_heap_serial value =
395-- Driver
395-- Driver
396-------------------------------------------------------------------------------
396-------------------------------------------------------------------------------
397
397
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+
398main :: IO ()
409main :: IO ()
399main = do
410main = do
400− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
401 env <- mkHandleBenchEnv
411 env <- mkHandleBenchEnv
402− arrays <- IP.toList $ IP.arraysOf 100 $ sourceUnfoldrM value 0
412+ arrays <- getArray (allBenchmarks env [])
403− value `seq` runMode (mode cfg) cfg benches (allBenchmarks env value arrays)
413+ runWithCLIOpts defaultStreamSize (allBenchmarks env arrays)
404
414
405 where
415 where
406
416
407− allBenchmarks env value arrays =
417+ allBenchmarks env arrays value =
408 [ bgroup (o_1_space_prefix moduleName) (o_1_space_serial value)
418 [ bgroup (o_1_space_prefix moduleName) (o_1_space_serial value)
409 , bgroup
419 , bgroup
410 (o_1_space_prefix moduleName ++ "/filesystem")
420 (o_1_space_prefix moduleName ++ "/filesystem")
benchmark/Streamly/Benchmark/Data/Parser/ParserD.hs+15−5
@@ -5,6 +5,7 @@
5-- License : BSD-3-Clause
5-- License : BSD-3-Clause
6-- Maintainer : streamly@composewell.com
6-- Maintainer : streamly@composewell.com
7
7
8+{-# LANGUAGE CPP #-}
8{-# LANGUAGE FlexibleContexts #-}
9{-# LANGUAGE FlexibleContexts #-}
9{-# LANGUAGE ScopedTypeVariables #-}
10{-# LANGUAGE ScopedTypeVariables #-}
10{-# OPTIONS_GHC -fspec-constr-recursive=4 #-}
11{-# OPTIONS_GHC -fspec-constr-recursive=4 #-}
@@ -358,16 +359,25 @@ o_n_space_serial value =
358-- Driver
359-- Driver
359-------------------------------------------------------------------------------
360-------------------------------------------------------------------------------
360
361
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+
361main :: IO ()
373main :: IO ()
362main = do
374main = do
363− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
375+ arrays <- getArray (allBenchmarks [])
364− arraysSmall <- IP.toList $ IP.arraysOf 100 $ sourceUnfoldrM value 0
376+ runWithCLIOpts defaultStreamSize (allBenchmarks arrays)
365− value `seq` runMode (mode cfg) cfg benches
366− (allBenchmarks value arraysSmall)
367
377
368 where
378 where
369
379
370− allBenchmarks value arraysSmall =
380+ allBenchmarks arraysSmall value =
371 [ bgroup (o_1_space_prefix moduleName) (o_1_space_serial value)
381 [ bgroup (o_1_space_prefix moduleName) (o_1_space_serial value)
372 , bgroup (o_1_space_prefix moduleName) (o_1_space_serial_spanning value)
382 , bgroup (o_1_space_prefix moduleName) (o_1_space_serial_spanning value)
373 , bgroup (o_1_space_prefix moduleName) (o_1_space_serial_nested value)
383 , 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 =
153-------------------------------------------------------------------------------
153-------------------------------------------------------------------------------
154
154
155main :: IO ()
155main :: IO ()
156−main = do
156+main = runWithCLIOpts defaultStreamSize allBenchmarks
157− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
158− value `seq` runMode (mode cfg) cfg benches (allBenchmarks value)
159
157
160 where
158 where
161
159
benchmark/Streamly/Benchmark/Data/Unfold.hs+2−3
@@ -799,13 +799,12 @@ o_1_space_copy_read_exceptions env =
799
799
800main :: IO ()
800main :: IO ()
801main = do
801main = do
802− (size, cfg, benches) <- parseCLIOpts defaultStreamSize
803 env <- mkHandleBenchEnv
802 env <- mkHandleBenchEnv
804− size `seq` runMode (mode cfg) cfg benches (allBenchmarks size env)
803+ runWithCLIOpts defaultStreamSize (allBenchmarks env)
805
804
806 where
805 where
807
806
808− allBenchmarks size env =
807+ allBenchmarks env size =
809 [ bgroup (o_1_space_prefix moduleName)
808 [ bgroup (o_1_space_prefix moduleName)
810 $ Prelude.concat
809 $ Prelude.concat
811 [ o_1_space_transformation_input size
810 [ o_1_space_transformation_input size
benchmark/Streamly/Benchmark/FileSystem/Handle.hs+2−3
@@ -43,13 +43,12 @@ moduleName = "FileSystem.Handle"
43
43
44main :: IO ()
44main :: IO ()
45main = do
45main = do
46− (_, cfg, benches) <- parseCLIOpts defaultStreamSize
47 env <- mkHandleBenchEnv
46 env <- mkHandleBenchEnv
48− runMode (mode cfg) cfg benches (allBenchmarks env)
47+ runWithCLIOpts defaultStreamSize (allBenchmarks env)
49
48
50 where
49 where
51
50
52− allBenchmarks env =
51+ allBenchmarks env _ =
53 [ bgroup (o_1_space_prefix moduleName) $ Prelude.concat
52 [ bgroup (o_1_space_prefix moduleName) $ Prelude.concat
54 [ RO.allBenchmarks env
53 [ RO.allBenchmarks env
55 , RW.allBenchmarks env
54 , RW.allBenchmarks env
benchmark/Streamly/Benchmark/Prelude/Ahead.hs+1−3
@@ -117,9 +117,7 @@ o_n_space_outerProduct value =
117-------------------------------------------------------------------------------
117-------------------------------------------------------------------------------
118
118
119main :: IO ()
119main :: IO ()
120−main = do
120+main = runWithCLIOpts defaultStreamSize allBenchmarks
121− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
122− value `seq` runMode (mode cfg) cfg benches (allBenchmarks value)
123
121
124 where
122 where
125
123
benchmark/Streamly/Benchmark/Prelude/Async.hs+2−3
@@ -183,9 +183,8 @@ o_n_space_outerProduct value =
183-------------------------------------------------------------------------------
183-------------------------------------------------------------------------------
184
184
185main :: IO ()
185main :: IO ()
186−main = do
186+main = runWithCLIOpts defaultStreamSize allBenchmarks
187− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
187+
188− value `seq` runMode (mode cfg) cfg benches (allBenchmarks value)
189
188
190 where
189 where
191
190
benchmark/Streamly/Benchmark/Prelude/Concurrent.hs+3−8
@@ -1,4 +1,3 @@
1−{-# LANGUAGE RankNTypes #-}
2-- |
1-- |
3-- Module : Main
2-- Module : Main
4-- Copyright : (c) 2018 Composewell Technologies
3-- Copyright : (c) 2018 Composewell Technologies
@@ -6,6 +5,8 @@
6-- License : BSD3
5-- License : BSD3
7-- Maintainer : streamly@composewell.com
6-- Maintainer : streamly@composewell.com
8
7
8+{-# LANGUAGE RankNTypes #-}
9+
9import Control.Concurrent
10import Control.Concurrent
10import Control.Monad (when, replicateM)
11import Control.Monad (when, replicateM)
11import Streamly.Prelude
12import Streamly.Prelude
@@ -77,13 +78,7 @@ concatGroup buflen threads usec n =
77
78
78main :: IO ()
79main :: IO ()
79main =
80main =
80− defaultMainWith (defaultConfig
81+ defaultMain
81− { timeLimit = Just 0
82− , minSamples = Just 1
83− , minDuration = 0
84− , includeFirstIter = True
85− , quickMode = True
86− })
87
82
88 [ -- bgroup "append/buf-1-threads-10k-0sec" (appendGroup 1 10000 0)
83 [ -- bgroup "append/buf-1-threads-10k-0sec" (appendGroup 1 10000 0)
89 -- , bgroup "append/buf-100-threads-100k-0sec" (appendGroup 100 100000 0)
84 -- , 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 =
219-------------------------------------------------------------------------------
219-------------------------------------------------------------------------------
220
220
221main :: IO ()
221main :: IO ()
222−main = do
222+main = runWithCLIOpts defaultStreamSize allBenchmarks
223− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
224− value `seq` runMode (mode cfg) cfg benches (allBenchmarks value)
225
223
226 where
224 where
227
225
benchmark/Streamly/Benchmark/Prelude/Rate.hs+9−11
@@ -1,4 +1,5 @@
1{-# LANGUAGE FlexibleContexts #-}
1{-# LANGUAGE FlexibleContexts #-}
2+{-# LANGUAGE CPP #-}
2
3
3-- |
4-- |
4-- Module : Main
5-- Module : Main
@@ -87,12 +88,12 @@ o_1_space_async value =
87o_1_space_ahead :: Int -> [Benchmark]
88o_1_space_ahead :: Int -> [Benchmark]
88o_1_space_ahead value =
89o_1_space_ahead value =
89 [ bgroup
90 [ bgroup
90− "aheadly"
91+ "aheadly"
91− [ benchIOSrc fromAhead "avgRate/1M" $ avgRate value 1000000
92+ [ benchIOSrc fromAhead "avgRate/1M" $ avgRate value 1000000
92− , benchIOSrc fromAhead "minRate/1M" $ minRate value 1000000
93+ , benchIOSrc fromAhead "minRate/1M" $ minRate value 1000000
93− , benchIOSrc fromAhead "maxRate/1M" $ maxRate value 1000000
94+ , benchIOSrc fromAhead "maxRate/1M" $ maxRate value 1000000
94− , benchIOSrc fromAsync "constRate/1M" $ constRate value 1000000
95+ , benchIOSrc fromAsync "constRate/1M" $ constRate value 1000000
95− ]
96+ ]
96 ]
97 ]
97
98
98-------------------------------------------------------------------------------
99-------------------------------------------------------------------------------
@@ -100,13 +101,10 @@ o_1_space_ahead value =
100-------------------------------------------------------------------------------
101-------------------------------------------------------------------------------
101
102
102main :: IO ()
103main :: IO ()
103−main = do
104+main = runWithCLIOpts defaultStreamSize allBenchmarks
104− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
105− value `seq` runMode (mode cfg) cfg benches (allBenchmarks value)
106
105
107 where
106 where
108
107
109 allBenchmarks value =
108 allBenchmarks value =
110 [ bgroup (o_1_space_prefix moduleName)
109 [ bgroup (o_1_space_prefix moduleName)
111− $ concat [o_1_space_async value, o_1_space_ahead value]
110+ $ concat [o_1_space_async value, o_1_space_ahead value]]
112− ]
benchmark/Streamly/Benchmark/Prelude/Serial.hs+3−4
@@ -22,7 +22,6 @@ import qualified Serial.Transformation1 as Transformation1
22import qualified Serial.Transformation2 as Transformation2
22import qualified Serial.Transformation2 as Transformation2
23import qualified Serial.Transformation3 as Transformation3
23import qualified Serial.Transformation3 as Transformation3
24
24
25−import Gauge hiding (env)
26import Streamly.Benchmark.Common
25import Streamly.Benchmark.Common
27
26
28moduleName :: String
27moduleName :: String
@@ -37,12 +36,12 @@ moduleName = "Prelude.Serial"
37--
36--
38main :: IO ()
37main :: IO ()
39main = do
38main = do
40− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
41 env <- mkHandleBenchEnv
39 env <- mkHandleBenchEnv
42− value `seq` runMode (mode cfg) cfg benches (allBenchmarks value env)
40+ runWithCLIOpts defaultStreamSize (allBenchmarks env)
41+
43 where
42 where
44
43
45− allBenchmarks size env = Prelude.concat
44+ allBenchmarks env size = Prelude.concat
46 [ Generation.benchmarks moduleName size
45 [ Generation.benchmarks moduleName size
47 , Elimination.benchmarks moduleName size
46 , Elimination.benchmarks moduleName size
48 , Exceptions.benchmarks moduleName env
47 , Exceptions.benchmarks moduleName env
benchmark/Streamly/Benchmark/Prelude/WAsync.hs+4−4
@@ -5,6 +5,8 @@
5-- License : BSD3
5-- License : BSD3
6-- Maintainer : streamly@composewell.com
6-- Maintainer : streamly@composewell.com
7
7
8+{-# LANGUAGE CPP #-}
9+
8import Prelude hiding (mapM)
10import Prelude hiding (mapM)
9
11
10import Streamly.Prelude (fromWAsync, fromSerial, wAsync, maxBuffer, maxThreads)
12import Streamly.Prelude (fromWAsync, fromSerial, wAsync, maxBuffer, maxThreads)
@@ -167,10 +169,8 @@ o_n_space_outerProduct value =
167-------------------------------------------------------------------------------
169-------------------------------------------------------------------------------
168
170
169main :: IO ()
171main :: IO ()
170−main = do
172+main = runWithCLIOpts defaultStreamSize allBenchmarks
171− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
173+
172− value `seq` runMode (mode cfg) cfg benches (allBenchmarks value)
173−
174 where
174 where
175
175
176 allBenchmarks value =
176 allBenchmarks value =
benchmark/Streamly/Benchmark/Prelude/WSerial.hs+2−3
@@ -201,9 +201,8 @@ o_n_space_outerProduct value =
201-- passed using the --stream-size option.
201-- passed using the --stream-size option.
202--
202--
203main :: IO ()
203main :: IO ()
204−main = do
204+main = runWithCLIOpts defaultStreamSize allBenchmarks
205− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
205+
206− value `seq` runMode (mode cfg) cfg benches (allBenchmarks value)
207
206
208 where
207 where
209
208
benchmark/Streamly/Benchmark/Prelude/ZipAsync.hs+2−3
@@ -6,6 +6,7 @@
6-- Maintainer : streamly@composewell.com
6-- Maintainer : streamly@composewell.com
7
7
8{-# LANGUAGE FlexibleContexts #-}
8{-# LANGUAGE FlexibleContexts #-}
9+{-# LANGUAGE CPP #-}
9
10
10import Streamly.Prelude (fromSerial)
11import Streamly.Prelude (fromSerial)
11import qualified Streamly.Prelude as S
12import qualified Streamly.Prelude as S
@@ -69,9 +70,7 @@ o_1_space_outerProduct value =
69-------------------------------------------------------------------------------
70-------------------------------------------------------------------------------
70
71
71main :: IO ()
72main :: IO ()
72−main = do
73+main = runWithCLIOpts defaultStreamSize allBenchmarks
73− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
74− value `seq` runMode (mode cfg) cfg benches (allBenchmarks value)
75
74
76 where
75 where
77
76
benchmark/Streamly/Benchmark/Prelude/ZipSerial.hs+5−7
@@ -60,8 +60,8 @@ zipWith count n =
60 S.drain $
60 S.drain $
61 S.zipWith
61 S.zipWith
62 (,)
62 (,)
63− (S.fromSerial $ sourceUnfoldrM count n)
63+ (S.fromSerial $ Main.sourceUnfoldrM count n)
64− (S.fromSerial $ sourceUnfoldrM count (n + 1))
64+ (S.fromSerial $ Main.sourceUnfoldrM count (n + 1))
65
65
66#ifdef INSPECTION
66#ifdef INSPECTION
67inspect $ hasNoTypeClasses 'zipWith
67inspect $ hasNoTypeClasses 'zipWith
@@ -75,8 +75,8 @@ zipWithM count n =
75 S.drain $
75 S.drain $
76 S.zipWithM
76 S.zipWithM
77 (curry return)
77 (curry return)
78− (sourceUnfoldrM count n)
78+ (Main.sourceUnfoldrM count n)
79− (sourceUnfoldrM count (n + 1))
79+ (Main.sourceUnfoldrM count (n + 1))
80
80
81#ifdef INSPECTION
81#ifdef INSPECTION
82inspect $ hasNoTypeClasses 'zipWithM
82inspect $ hasNoTypeClasses 'zipWithM
@@ -125,9 +125,7 @@ o_1_space_outerProduct value =
125-- passed using the --stream-size option.
125-- passed using the --stream-size option.
126--
126--
127main :: IO ()
127main :: IO ()
128−main = do
128+main = runWithCLIOpts defaultStreamSize allBenchmarks
129− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
130− value `seq` runMode (mode cfg) cfg benches (allBenchmarks value)
131
129
132 where
130 where
133
131
benchmark/Streamly/Benchmark/Unicode/Stream.hs+1−2
@@ -288,9 +288,8 @@ o_1_space_decode_encode_read env =
288
288
289main :: IO ()
289main :: IO ()
290main = do
290main = do
291− (_, cfg, benches) <- parseCLIOpts defaultStreamSize
292 env <- mkHandleBenchEnv
291 env <- mkHandleBenchEnv
293− runMode (mode cfg) cfg benches (allBenchmarks env)
292+ defaultMain (allBenchmarks env)
294
293
295 where
294 where
296
295
benchmark/lib/Streamly/Benchmark/Common.hs+52−5
@@ -16,7 +16,9 @@ module Streamly.Benchmark.Common
16 , o_n_heap_prefix
16 , o_n_heap_prefix
17 , o_n_stack_prefix
17 , o_n_stack_prefix
18
18
19+ -- , parseEnvOpts
19 , parseCLIOpts
20 , parseCLIOpts
21+ , runWithCLIOpts
20
22
21 , benchIOSink1
23 , benchIOSink1
22 , benchPure
24 , benchPure
@@ -31,19 +33,34 @@ module Streamly.Benchmark.Common
31 , mkListString
33 , mkListString
32
34
33 , defaultStreamSize
35 , defaultStreamSize
36+ , BenchOpts(..)
37+#ifndef MIN_VERSION_gauge
38+ , OptionDescription(..)
39+ , includingOptions
40+ , lookupOption
41+ , defaultMainWithIngredients
42+ , parseOptions
43+#endif
34 )
44 )
35where
45where
36
46
37−import Control.DeepSeq (NFData(..))
47+#ifdef MIN_VERSION_gauge
38import Control.Exception (evaluate)
48import Control.Exception (evaluate)
39import Control.Monad (when)
49import Control.Monad (when)
40−import Data.Functor.Identity (Identity, runIdentity)
50+import Text.Read (readMaybe)
41import Data.List (scanl')
51import Data.List (scanl')
42import Data.Maybe (mapMaybe)
52import Data.Maybe (mapMaybe)
43import System.Console.GetOpt
53import System.Console.GetOpt
44 (OptDescr(..), ArgDescr(..), ArgOrder(..), getOpt')
54 (OptDescr(..), ArgDescr(..), ArgOrder(..), getOpt')
45import System.Environment (getArgs, lookupEnv, setEnv)
55import 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)
47import System.Random (randomRIO)
64import System.Random (randomRIO)
48
65
49import qualified Streamly.Prelude as S
66import qualified Streamly.Prelude as S
@@ -136,6 +153,7 @@ defaultStreamSize = 100000
136
153
137newtype BenchOpts = StreamSize Int deriving Show
154newtype BenchOpts = StreamSize Int deriving Show
138
155
156+#ifdef MIN_VERSION_gauge
139getStreamSize :: String -> Int
157getStreamSize :: String -> Int
140getStreamSize size =
158getStreamSize size =
141 case (readMaybe size :: Maybe Int) of
159 case (readMaybe size :: Maybe Int) of
@@ -145,7 +163,7 @@ getStreamSize size =
145options :: [OptDescr BenchOpts]
163options :: [OptDescr BenchOpts]
146options =
164options =
147 [
165 [
148− Option [] ["stream-size"] (ReqArg getSize "COUNT") "Stream element count"
166+ System.Console.GetOpt.Option [] ["stream-size"] (ReqArg getSize "COUNT") "Stream element count"
149 ]
167 ]
150
168
151 where
169 where
@@ -169,7 +187,6 @@ deleteOptArgs (Just prev, _) opt =
169parseCLIOpts :: Int -> IO (Int, Config, [String])
187parseCLIOpts :: Int -> IO (Int, Config, [String])
170parseCLIOpts defStreamSize = do
188parseCLIOpts defStreamSize = do
171 args <- getArgs
189 args <- getArgs
172−
173 -- Parse custom options
190 -- Parse custom options
174 let (opts, _, _, errs) = getOpt' Permute options args
191 let (opts, _, _, errs) = getOpt' Permute options args
175 when (not $ null errs) $ error $ concat errs
192 when (not $ null errs) $ error $ concat errs
@@ -207,3 +224,33 @@ parseCLIOpts defStreamSize = do
207 }
224 }
208 let (cfg, benches) = parseWith config args'
225 let (cfg, benches) = parseWith config args'
209 streamSize `seq` return (streamSize, cfg, benches)
226 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 =
86 then str
86 then str
87 else str ++ " (1/" ++ show factor ++ ")"
87 else str ++ " (1/" ++ show factor ++ ")"
88
88
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+
89mkBenchCommon ::
112mkBenchCommon ::
90 NFData b
113 NFData b
91 => (RefHandles -> Handles)
114 => (RefHandles -> Handles)
@@ -94,30 +117,11 @@ mkBenchCommon ::
94 -> (Handle -> Handle -> IO b)
117 -> (Handle -> Handle -> IO b)
95 -> Benchmark
118 -> Benchmark
96mkBenchCommon mkHandles name env action =
119mkBenchCommon mkHandles name env action =
97− bench name $ perRunEnv (do
120+ bench name $ nfIO $ do
98− r <- readIORef $ href env
121+ -- XXX adds significant cpu time to the benchmarks
99−
122+ -- tasty-bench should provide a better way to do this
100− -- close old handles
123+ (Handles h1 h2) <- getHandles env mkHandles
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)
124+ action h1 h2
121
125
122mkBench ::
126mkBench ::
123 NFData b => String -> BenchEnv -> (Handle -> Handle -> IO b) -> Benchmark
127 NFData b => String -> BenchEnv -> (Handle -> Handle -> IO b) -> Benchmark
benchmark/streamly-benchmarks.cabal+15−1
@@ -54,6 +54,11 @@ flag opt
54 manual: True
54 manual: True
55 default: True
55 default: True
56
56
57+flag use-gauge
58+ description: Use gauge instead of tasty-bench for benchmarking
59+ manual: True
60+ default: False
61+
57-------------------------------------------------------------------------------
62-------------------------------------------------------------------------------
58-- Common stanzas
63-- Common stanzas
59-------------------------------------------------------------------------------
64-------------------------------------------------------------------------------
@@ -113,13 +118,22 @@ common bench-depends
113 -- other libraries
118 -- other libraries
114 , streamly >= 0.7.0
119 , streamly >= 0.7.0
115 , random >= 1.0 && < 2.0
120 , random >= 1.0 && < 2.0
116− , gauge >= 0.2.4 && < 0.3
117 , transformers >= 0.4 && < 0.6
121 , transformers >= 0.4 && < 0.6
118 , containers >= 0.5 && < 0.7
122 , containers >= 0.5 && < 0.7
119 , typed-process >= 0.2.3 && < 0.3
123 , typed-process >= 0.2.3 && < 0.3
120 , directory >= 1.2.2 && < 1.4
124 , directory >= 1.2.2 && < 1.4
121 , ghc-prim >= 0.4 && < 0.8
125 , ghc-prim >= 0.4 && < 0.8
122
126
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+
123 if flag(fusion-plugin) && !impl(ghcjs) && !impl(ghc < 8.6)
137 if flag(fusion-plugin) && !impl(ghcjs) && !impl(ghc < 8.6)
124 build-depends:
138 build-depends:
125 fusion-plugin >= 0.2 && < 0.3
139 fusion-plugin >= 0.2 && < 0.3
bin/bench-exec-one.sh+53−12
@@ -100,9 +100,15 @@ bench_rts_opts_specific () {
100# Speed options
100# Speed options
101#------------------------------------------------------------------------------
101#------------------------------------------------------------------------------
102
102
103+if test "$USE_GAUGE" -eq 0
104+then
105+ SUPER_QUICK_OPTIONS="--stdev 1000000"
106+ QUICKER_OPTIONS="--stdev 1000"
107+else
103−# Do not keep time limit as 0 otherwise GC stats may remain 0 in some cases.
108+ # 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"
109+ SUPER_QUICK_OPTIONS="--quick --min-duration 0 --time-limit 0.01 --include-first-iter"
105−QUICKER_OPTIONS="--min-samples 3 --time-limit 1"
110+ QUICKER_OPTIONS="--min-samples 3 --time-limit 1"
111+fi
106
112
107# For certain long benchmarks if the user has not requested super quick
113# For certain long benchmarks if the user has not requested super quick
108# mode we anyway use a slightly quicker mode.
114# mode we anyway use a slightly quicker mode.
@@ -152,13 +158,38 @@ bench_quick_opts () {
152 esac
158 esac
153}
159}
154
160
155−last=""
161+bench_output_file() {
162+ local bench_name=$1
163+ echo "charts/$bench_name/results.csv"
164+}
165+
166+BENCH_NAME_ORIG=""
156for i in "$@"
167for i in "$@"
157do
168do
158− BENCH_NAME="$last"
169+ BENCH_NAME_ORIG="$i"
159− last="$i"
160done
170done
161
171
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+
162RTS_OPTIONS=\
193RTS_OPTIONS=\
163"+RTS -T \
194"+RTS -T \
164$(bench_exe_rts_opts $(basename $BENCH_EXEC_PATH)) \
195$(bench_exe_rts_opts $(basename $BENCH_EXEC_PATH)) \
@@ -168,17 +199,27 @@ $RTS_OPTIONS \
168-RTS"
199-RTS"
169
200
170QUICK_BENCH_OPTIONS="\
201QUICK_BENCH_OPTIONS="\
202+$(if test "$QUICK_MODE" -ne 0; then echo $SUPER_QUICK_OPTIONS; else :; fi)
171$(bench_exe_quick_opts $(basename $BENCH_EXEC_PATH)) \
203$(bench_exe_quick_opts $(basename $BENCH_EXEC_PATH)) \
172$(bench_quick_opts $BENCH_NAME)"
204$(bench_quick_opts $BENCH_NAME)"
173
205
174−if test -n "$STREAM_SIZE"
206+output_file=$(bench_output_file $(basename $BENCH_EXEC_PATH))
175−then
207+mkdir -p `dirname $output_file`
176− STREAM_LEN=$(env LC_ALL=en_US.UTF-8 printf "\--stream-size %'.f\n" $STREAM_SIZE)
177−fi
178
208
179echo "$BENCH_NAME: \
209echo "$BENCH_NAME: \
180−$STREAM_LEN \
181$QUICK_BENCH_OPTIONS \
210$QUICK_BENCH_OPTIONS \
182$RTS_OPTIONS"
211$RTS_OPTIONS"
183
212
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')
184−$BENCH_EXEC_PATH $RTS_OPTIONS "$@" $QUICK_BENCH_OPTIONS
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() {
142 echo "charts/$bench_name/results.csv"
142 echo "charts/$bench_name/results.csv"
143}
143}
144
144
145−# --min-duration 0 means exactly one iteration per sample. We use a million
145+run_bench_target () {
146−# iterations in the benchmarking code explicitly and do not use the iterations
146+ local package_name=$1
147−# done by the benchmarking tool.
147+ local component=$2
148−#
148+ local target_name=$3
149−# Benchmarking tool by default discards the first iteration to remove
149+ local output_file=$(bench_output_file $target_name)
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
163
150
164− local output_file=$(bench_output_file $bench_name)
151+ local target_prog
165− mkdir -p `dirname $output_file`
152+ target_prog=$(cabal_target_prog $package_name $component $target_name) || \
153+ die "Cannot find executable for target $target_name"
166
154
167− local QUICK_OPTS="--quick --min-duration 0"
155+ echo "Running executable $target_name ..."
168− local SPEED_OPTIONS
156+
157+ # Needed by bench-exec-one.sh
158+ export BENCH_EXEC_PATH=$target_prog
169− if test "$LONG" -eq 0
159+ if test "$LONG" -ne 0
170 then
160 then
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
171− if test "$SLOW" -eq 0
169+ if test "$LONG" -ne 0
172 then
170 then
173− if test "$QUICK_MODE" -eq 0
171+ MATCH="-p /$target_name\/o-1-space/"
172+ else
173+ if test -n "$GAUGE_ARGS"
174 then
174 then
175− # default mode, not super quick, not slow
175+ local GAUGE_ARGS1=$(echo "$GAUGE_ARGS" | sed -e 's/\//\\\//g')
176− SPEED_OPTIONS="$QUICK_OPTS --min-samples 10 --time-limit 1"
176+ MATCH="-p /$GAUGE_ARGS1/"
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"
185 fi
177 fi
186− else
187− # Slow but more accurate mode
188− SPEED_OPTIONS="--min-duration 0"
189 fi
178 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
190 else
183 else
191− # large stream size, always super quick
184+ if test "$LONG" -ne 0
192− GAUGE_ARGS="$GAUGE_ARGS $bench_name/o-1-space"
185+ then
193− SPEED_OPTIONS="--stream-size 10000000 $QUICK_OPTS --include-first-iter"
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
194 fi
199 fi
200+}
195
201
196− echo "$SPEED_OPTIONS \
202+# $1: package name
197− --csvraw=$output_file \
203+# $2: component
198− -v 2 \
204+# $3: targets
199− --measure-with "$SCRIPT_DIR/bench-exec-one.sh" \
205+run_bench_targets() {
200− $GAUGE_ARGS"
206+ for i in $3
207+ do
208+ run_bench_target $1 $2 $i
209+ done
201}
210}
202
211
203run_benches_comparing() {
212run_benches_comparing() {
@@ -217,14 +226,14 @@ run_benches_comparing() {
217 git checkout "$BASE" || die "Checkout of base commit [$BASE] failed"
226 git checkout "$BASE" || die "Checkout of base commit [$BASE] failed"
218
227
219 $BUILD_BENCH || die "build failed"
228 $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
221
230
222 echo "Checking out candidate commit [$CANDIDATE] for benchmarking"
231 echo "Checking out candidate commit [$CANDIDATE] for benchmarking"
223 git checkout "$CANDIDATE" || \
232 git checkout "$CANDIDATE" || \
224 die "Checkout of candidate [$CANDIDATE] commit failed"
233 die "Checkout of candidate [$CANDIDATE] commit failed"
225
234
226 $BUILD_BENCH || die "build failed"
235 $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
228 # XXX reset back to the original commit
237 # XXX reset back to the original commit
229}
238}
230
239
@@ -248,7 +257,7 @@ run_measurements() {
248
257
249 if test "$COMPARE" = "0"
258 if test "$COMPARE" = "0"
250 then
259 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
252 else
261 else
253 run_benches_comparing "$bench_list"
262 run_benches_comparing "$bench_list"
254 fi
263 fi
@@ -274,6 +283,8 @@ run_reports() {
274
283
275cd $SCRIPT_DIR/..
284cd $SCRIPT_DIR/..
276
285
286+USE_GAUGE=0
287+export USE_GAUGE
277USE_GIT_CABAL=1
288USE_GIT_CABAL=1
278set_common_vars
289set_common_vars
279
290
@@ -323,7 +334,7 @@ do
323 --long) LONG=1; shift ;;
334 --long) LONG=1; shift ;;
324 --graphs) GRAPH=1; shift ;;
335 --graphs) GRAPH=1; shift ;;
325 --no-measure) MEASURE=0; shift ;;
336 --no-measure) MEASURE=0; shift ;;
326− --dev-build) RUNNING_DEVBUILD=1; shift ;;
337+ --dev-build) RUNNING_DEVBUILD=1; shift ;;
327 --) shift; break ;;
338 --) shift; break ;;
328 -*|--*) echo "Unknown flags: $*"; echo; print_help ;;
339 -*|--*) echo "Unknown flags: $*"; echo; print_help ;;
329 *) break ;;
340 *) break ;;
@@ -409,6 +420,7 @@ then
409 run_build "$BUILD_BENCH" streamly-benchmarks bench "$TARGETS"
420 run_build "$BUILD_BENCH" streamly-benchmarks bench "$TARGETS"
410 export QUICK_MODE
421 export QUICK_MODE
411 export RTS_OPTIONS
422 export RTS_OPTIONS
423+ export LONG
412 run_measurements "$TARGETS"
424 run_measurements "$TARGETS"
413fi
425fi
414
426
stack.yaml+3−1
@@ -1,4 +1,4 @@
1−resolver: lts-16.12
1+resolver: lts-17.9
2packages:
2packages:
3- '.'
3- '.'
4- './benchmark'
4- './benchmark'
@@ -8,6 +8,8 @@ extra-deps:
8- QuickCheck-2.14.1@sha256:01e46d7b0a8d3148288ec977625f62d5516ebb5031a50c63f0453863301b4a79,7736
8- QuickCheck-2.14.1@sha256:01e46d7b0a8d3148288ec977625f62d5516ebb5031a50c63f0453863301b4a79,7736
9- random-1.2.0@sha256:4969b16ef23e9a57a415abcd39d982c81844031557b66349651b0fe3255b8986,6094
9- random-1.2.0@sha256:4969b16ef23e9a57a415abcd39d982c81844031557b66349651b0fe3255b8986,6094
10- splitmix-0.1.0.1@sha256:22f9662e7e8b173421872241edd39350078a9ed4bb9e9f503948c5b483c79276,5253
10- 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
11
13
12#allow-newer: true
14#allow-newer: true
13flags: {}
15flags: {}