on master help sign in
split "linear" benchmark based on space usage

Benchmarks are divided into 4 categories: 1) using O(1) space 2) using O(n) Heap 3) using O(n) stack 4) using O(n) heap and stack This allows us to run the benchmark executables with restricted heap and stack memory limites so that we can catch space leak issues or high space usage issues quickly.

commitf65dda69081aa3a29ec0843ca90d8de08e3fdb5f authorHarendra Kumar <harendra.kumar@gmail.com> dateTue 18 Feb 2020, 20:10 (7 years ago) parent16819a6
8 files changed+1216−945
benchmark/Common.hs+97−4 benchmark/Linear.hs−600 benchmark/exe/Prelude/Serial/O_1_Space.hs+434 benchmark/exe/Prelude/Serial/O_n_Heap.hs+109 benchmark/exe/Prelude/Serial/O_n_Space.hs+104 benchmark/exe/Prelude/Serial/O_n_Stack.hs+72 benchmark/streamly-benchmarks.cabal+392 streamly.cabal+8−341
benchmark/Common.hs+97−4
@@ -1,3 +1,8 @@
1+{-# LANGUAGE CPP #-}
2+#if __GLASGOW_HASKELL__ >= 800
3+{-# OPTIONS_GHC -Wno-orphans #-}
4+#endif
5+
16-- |
27-- Module : Main
38-- Copyright : (c) 2019 Composewell Technologies
@@ -5,19 +10,107 @@
510-- License : BSD3
611-- Maintainer : streamly@composewell.com
712
8−module Common (parseCLIOpts) where
13+module Common
14+ ( parseCLIOpts
15+
16+ , benchIOSink1
17+ , benchPure
18+ , benchPureSink1
19+
20+ , benchIOSrc1
21+ , benchPureSrc
22+
23+ , mkString
24+ , mkList
25+ , mkListString
926
27+ , defaultStreamSize
28+ , limitStreamSize
29+ )
30+where
31+
32+import Control.DeepSeq (NFData(..))
1033import Control.Exception (evaluate)
1134import Control.Monad (when)
35+import Data.Functor.Identity (Identity, runIdentity)
1236import Data.List (scanl')
1337import Data.Maybe (catMaybes)
1438import System.Console.GetOpt
1539 (OptDescr(..), ArgDescr(..), ArgOrder(..), getOpt')
1640import System.Environment (getArgs, lookupEnv, setEnv)
1741import Text.Read (readMaybe)
42+import System.Random (randomRIO)
43+
44+import qualified Streamly.Prelude as S
1845
46+import Streamly
1947import Gauge
2048
49+-------------------------------------------------------------------------------
50+-- Benchmarking utilities
51+-------------------------------------------------------------------------------
52+
53+#if !MIN_VERSION_deepseq(1,4,3)
54+instance NFData Ordering where rnf = (`seq` ())
55+#endif
56+
57+-- XXX once we convert all the functions to use this we can rename this to
58+-- benchIOSink
59+{-# INLINE benchIOSink1 #-}
60+benchIOSink1 :: NFData b => String -> (Int -> IO b) -> Benchmark
61+benchIOSink1 name f = bench name $ nfIO $ randomRIO (1,1) >>= f
62+
63+{-# INLINE benchIOSrc1 #-}
64+benchIOSrc1 :: String -> (Int -> IO ()) -> Benchmark
65+benchIOSrc1 name f = bench name $ nfIO $ randomRIO (1,1) >>= f
66+
67+{-# INLINE benchPure #-}
68+benchPure :: NFData b => String -> (Int -> a) -> (a -> b) -> Benchmark
69+benchPure name src f = bench name $ nfIO $ randomRIO (1,1) >>= return . f . src
70+
71+-- XXX once we convert all the functions to use this we can rename this to
72+-- benchPureSink
73+{-# INLINE benchPureSink1 #-}
74+benchPureSink1 :: NFData b => String -> (Int -> Identity b) -> Benchmark
75+benchPureSink1 name f =
76+ bench name $ nfIO $ randomRIO (1,1) >>= return . runIdentity . f
77+
78+{-# INLINE benchPureSrc #-}
79+benchPureSrc :: String -> (Int -> SerialT Identity a) -> Benchmark
80+benchPureSrc name src = benchPure name src (runIdentity . S.drain)
81+
82+-------------------------------------------------------------------------------
83+-- String/List generation for read instances
84+-------------------------------------------------------------------------------
85+
86+{-# INLINABLE mkString #-}
87+mkString :: Int -> String
88+mkString value = "fromList [1" ++ concat (replicate value ",1") ++ "]"
89+
90+{-# INLINABLE mkListString #-}
91+mkListString :: Int -> String
92+mkListString value = "[1" ++ concat (replicate value ",1") ++ "]"
93+
94+{-# INLINABLE mkList #-}
95+mkList :: Int -> [Int]
96+mkList value = [1..value]
97+
98+-------------------------------------------------------------------------------
99+-- Stream size
100+-------------------------------------------------------------------------------
101+
102+defaultStreamSize :: Int
103+defaultStreamSize = 100000
104+
105+limitStreamSize :: Int -> IO Int
106+limitStreamSize value = do
107+ let val = min value defaultStreamSize
108+ when (val /= value) $
109+ putStrLn $ "Limiting stream size to "
110+ ++ show defaultStreamSize
111+ ++ " for non O(1) space operations"
112+ return val
113+
21114-------------------------------------------------------------------------------
22115-- Parse custom CLI options
23116-------------------------------------------------------------------------------
@@ -55,7 +148,7 @@ deleteOptArgs (Just prev, _) opt =
55148 else (Just opt, Just opt)
56149
57150parseCLIOpts :: Int -> IO (Int, Config, [String])
58−parseCLIOpts defaultStreamSize = do
151+parseCLIOpts defStreamSize = do
59152 args <- getArgs
60153
61154 -- Parse custom options
@@ -83,13 +176,13 @@ parseCLIOpts defaultStreamSize = do
83176 Just x -> do
84177 s <- evaluate $ getStreamSize x
85178 return (s, args)
86− Nothing -> return (defaultStreamSize, args)
179+ Nothing -> return (defStreamSize, args)
87180
88181 -- Parse gauge options
89182 let config = defaultConfig
90183 { timeLimit = Just 1
91184 , minDuration = 0
92− , includeFirstIter = streamSize > defaultStreamSize
185+ , includeFirstIter = streamSize > defStreamSize
93186 }
94187 let (cfg, benches) = parseWith config args'
95188 streamSize `seq` return (streamSize, cfg, benches)
benchmark/Linear.hsdeleted−600
@@ -1,600 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−#if __GLASGOW_HASKELL__ >= 800
3−{-# OPTIONS_GHC -Wno-orphans #-}
4−#endif
5−
6−-- |
7−-- Module : Main
8−-- Copyright : (c) 2018 Harendra Kumar
9−--
10−-- License : BSD3
11−-- Maintainer : streamly@composewell.com
12−
13−import Control.DeepSeq (NFData(..))
14−import Control.Monad (when)
15−import Data.Functor.Identity (Identity, runIdentity)
16−import Data.Monoid (Last(..))
17−import System.Random (randomRIO)
18−
19−import Common (parseCLIOpts)
20−
21−import qualified GHC.Exts as GHC
22−import qualified Streamly.Benchmark.Prelude as Ops
23−
24−import Streamly
25−import qualified Streamly.Data.Fold as FL
26−import qualified Streamly.Memory.Array as A
27−import qualified Streamly.Prelude as S
28−import qualified Streamly.Internal.Data.Sink as Sink
29−
30−import Streamly.Internal.Data.Time.Units
31−import qualified Streamly.Internal.Memory.Array as IA
32−import qualified Streamly.Internal.Data.Fold as IFL
33−import qualified Streamly.Internal.Prelude as IP
34−import qualified Streamly.Internal.Data.Pipe as Pipe
35−
36−import Gauge
37−
38−-------------------------------------------------------------------------------
39−--
40−-------------------------------------------------------------------------------
41−
42−#if !MIN_VERSION_deepseq(1,4,3)
43−instance NFData Ordering where rnf = (`seq` ())
44−#endif
45−
46−-- We need a monadic bind here to make sure that the function f does not get
47−-- completely optimized out by the compiler in some cases.
48−
49−-- | Takes a fold method, and uses it with a default source.
50−{-# INLINE benchIOSink #-}
51−benchIOSink
52− :: (IsStream t, NFData b)
53− => Int -> String -> (t IO Int -> IO b) -> Benchmark
54−benchIOSink value name f = bench name $ nfIO $ randomRIO (1,1) >>= f . Ops.source value
55−
56−{-# INLINE benchHoistSink #-}
57−benchHoistSink
58− :: (IsStream t, NFData b)
59− => Int -> String -> (t Identity Int -> IO b) -> Benchmark
60−benchHoistSink value name f =
61− bench name $ nfIO $ randomRIO (1,1) >>= f . Ops.sourceUnfoldr value
62−
63−-- XXX once we convert all the functions to use this we can rename this to
64−-- benchIOSink
65−{-# INLINE benchIOSink1 #-}
66−benchIOSink1 :: NFData b => String -> (Int -> IO b) -> Benchmark
67−benchIOSink1 name f = bench name $ nfIO $ randomRIO (1,1) >>= f
68−
69−-- XXX We should be using sourceUnfoldrM for fair comparison with IO monad, but
70−-- we can't use it as it requires MonadAsync constraint.
71−{-# INLINE benchIdentitySink #-}
72−benchIdentitySink
73− :: (IsStream t, NFData b)
74− => Int -> String -> (t Identity Int -> Identity b) -> Benchmark
75−benchIdentitySink value name f = bench name $ nf (f . Ops.sourceUnfoldr value) 1
76−
77−-- | Takes a source, and uses it with a default drain/fold method.
78−{-# INLINE benchIOSrc #-}
79−benchIOSrc
80− :: (t IO a -> SerialT IO a)
81− -> String
82− -> (Int -> t IO a)
83− -> Benchmark
84−benchIOSrc t name f =
85− bench name $ nfIO $ randomRIO (1,1) >>= Ops.toNull t . f
86−
87−{-# INLINE benchIOSrc1 #-}
88−benchIOSrc1 :: String -> (Int -> IO ()) -> Benchmark
89−benchIOSrc1 name f = bench name $ nfIO $ randomRIO (1,1) >>= f
90−
91−{-# INLINE benchPure #-}
92−benchPure :: NFData b => String -> (Int -> a) -> (a -> b) -> Benchmark
93−benchPure name src f = bench name $ nfIO $ randomRIO (1,1) >>= return . f . src
94−
95−{-# INLINE benchPureSink #-}
96−benchPureSink :: NFData b => Int -> String -> (SerialT Identity Int -> b) -> Benchmark
97−benchPureSink value name f = benchPure name (Ops.sourceUnfoldr value) f
98−
99−-- XXX once we convert all the functions to use this we can rename this to
100−-- benchPureSink
101−{-# INLINE benchPureSink1 #-}
102−benchPureSink1 :: NFData b => String -> (Int -> Identity b) -> Benchmark
103−benchPureSink1 name f =
104− bench name $ nfIO $ randomRIO (1,1) >>= return . runIdentity . f
105−
106−{-# INLINE benchPureSinkIO #-}
107−benchPureSinkIO
108− :: NFData b
109− => Int -> String -> (SerialT Identity Int -> IO b) -> Benchmark
110−benchPureSinkIO value name f =
111− bench name $ nfIO $ randomRIO (1, 1) >>= f . Ops.sourceUnfoldr value
112−
113−{-# INLINE benchPureSrc #-}
114−benchPureSrc :: String -> (Int -> SerialT Identity a) -> Benchmark
115−benchPureSrc name src = benchPure name src (runIdentity . S.drain)
116−
117−mkString :: Int -> String
118−mkString value = "fromList [1" ++ concat (replicate value ",1") ++ "]"
119−
120−mkListString :: Int -> String
121−mkListString value = "[1" ++ concat (replicate value ",1") ++ "]"
122−
123−mkList :: Int -> [Int]
124−mkList value = [1..value]
125−
126−defaultStreamSize :: Int
127−defaultStreamSize = 100000
128−
129−main :: IO ()
130−main = do
131− -- XXX Fix indentation
132− (value, cfg, benches) <- parseCLIOpts defaultStreamSize
133− let bufValue = min value defaultStreamSize
134− when (bufValue /= value) $
135− putStrLn $ "Limiting stream size to "
136− ++ show defaultStreamSize
137− ++ " for non O(1) space operations"
138−
139− bufValue `seq` value `seq` runMode (mode cfg) cfg benches
140− -- Operations that can work on infinite streams using constant/O(1) space.
141− [ bgroup "serially"
142− [ bgroup "pure"
143− [ benchPureSink value "id" id
144− , benchPureSink1 "eqBy" (Ops.eqByPure value)
145− , benchPureSink value "==" Ops.eqInstance
146− , benchPureSink value "/=" Ops.eqInstanceNotEq
147− , benchPureSink1 "cmpBy" (Ops.cmpByPure value)
148− , benchPureSink value "<" Ops.ordInstance
149− , benchPureSink value "min" Ops.ordInstanceMin
150− , benchPureSrc "IsList.fromList" (Ops.sourceIsList value)
151− -- length is used to check for foldr/build fusion
152− , benchPureSink value "length . IsList.toList" (length . GHC.toList)
153− , benchPureSrc "IsString.fromString" (Ops.sourceIsString value)
154− , benchPureSink value "showsPrec pure streams" Ops.showInstance
155− , benchPureSink value "foldl'" Ops.pureFoldl'
156− ]
157− , bgroup "foldable"
158− [ -- Foldable instance
159− -- type class operations
160− bench "foldl'" $ nf (Ops.foldableFoldl' value) 1
161− , bench "foldrElem" $ nf (Ops.foldableFoldrElem value) 1
162− -- , bench "null" $ nf (Ops.foldableNull value) 1
163− , bench "elem" $ nf (Ops.foldableElem value) 1
164− , bench "length" $ nf (Ops.foldableLength value) 1
165− , bench "sum" $ nf (Ops.foldableSum value) 1
166− , bench "product" $ nf (Ops.foldableProduct value) 1
167− , bench "minimum" $ nf (Ops.foldableMin value) 1
168− , bench "maximum" $ nf (Ops.foldableMax value) 1
169− , bench "length . toList" $
170− nf (length . Ops.foldableToList value) 1
171−
172− -- folds
173− , bench "notElem" $ nf (Ops.foldableNotElem value) 1
174− , bench "find" $ nf (Ops.foldableFind value) 1
175− , bench "all" $ nf (Ops.foldableAll value) 1
176− , bench "any" $ nf (Ops.foldableAny value) 1
177− , bench "and" $ nf (Ops.foldableAnd value) 1
178− , bench "or" $ nf (Ops.foldableOr value) 1
179−
180− -- Note: minimumBy/maximumBy do not work in constant memory they are in
181− -- the O(n) group of benchmarks down below in this file.
182−
183− -- Applicative and Traversable operations
184− -- TBD: traverse_
185− , benchIOSink1 "mapM_" (Ops.foldableMapM_ value)
186− -- TBD: for_
187− -- TBD: forM_
188− , benchIOSink1 "sequence_" (Ops.foldableSequence_ value)
189− -- TBD: sequenceA_
190− -- TBD: asum
191− -- , benchIOSink1 "msum" (Ops.foldableMsum value)
192− ]
193− , bgroup "generation"
194− [ -- Most basic, barely stream continuations running
195− benchIOSrc serially "unfoldr" (Ops.sourceUnfoldr value)
196− , benchIOSrc serially "unfoldrM" (Ops.sourceUnfoldrM value)
197− , benchIOSrc serially "intFromTo" (Ops.sourceIntFromTo value)
198− , benchIOSrc serially "intFromThenTo" (Ops.sourceIntFromThenTo value)
199− , benchIOSrc serially "integerFromStep" (Ops.sourceIntegerFromStep value)
200− , benchIOSrc serially "fracFromThenTo" (Ops.sourceFracFromThenTo value)
201− , benchIOSrc serially "fracFromTo" (Ops.sourceFracFromTo value)
202− , benchIOSrc serially "fromList" (Ops.sourceFromList value)
203− , benchIOSrc serially "fromListM" (Ops.sourceFromListM value)
204− -- These are essentially cons and consM
205− , benchIOSrc serially "fromFoldable" (Ops.sourceFromFoldable value)
206− , benchIOSrc serially "fromFoldableM" (Ops.sourceFromFoldableM value)
207− , benchIOSrc serially "currentTime/0.00001s"
208− $ Ops.currentTime value 0.00001
209− ]
210− , bgroup "elimination"
211− [ bgroup "reduce"
212− [ bgroup "IO"
213− [
214− benchIOSink value "foldl'" Ops.foldl'Reduce
215− , benchIOSink value "foldl1'" Ops.foldl1'Reduce
216− , benchIOSink value "foldlM'" Ops.foldlM'Reduce
217− ]
218− , bgroup "Identity"
219− [
220− benchIdentitySink value "foldl'" Ops.foldl'Reduce
221− , benchIdentitySink value "foldl1'" Ops.foldl1'Reduce
222− , benchIdentitySink value "foldlM'" Ops.foldlM'Reduce
223− ]
224− ]
225−
226− , bgroup "build"
227− [ bgroup "IO"
228− [ benchIOSink value "foldrMElem" (Ops.foldrMElem value)
229− ]
230− , bgroup "Identity"
231− [ benchIdentitySink value "foldrMElem" (Ops.foldrMElem value)
232− , benchIdentitySink value "foldrMToStreamLength"
233− (S.length . runIdentity . Ops.foldrMToStream)
234− , benchPureSink value "foldrMToListLength"
235− (length . runIdentity . Ops.foldrMBuild)
236− ]
237− ]
238− , benchIOSink value "uncons" Ops.uncons
239− , benchIOSink value "toNull" $ Ops.toNull serially
240− , benchIOSink value "mapM_" Ops.mapM_
241−
242− , benchIOSink value "init" Ops.init
243−
244− -- this is too low and causes all benchmarks reported in ns
245− -- , benchIOSink value "head" Ops.head
246− , benchIOSink value "last" Ops.last
247− -- , benchIOSink value "lookup" Ops.lookup
248− , benchIOSink value "find" (Ops.find value)
249− , benchIOSink value "findIndex" (Ops.findIndex value)
250− , benchIOSink value "elemIndex" (Ops.elemIndex value)
251−
252− -- this is too low and causes all benchmarks reported in ns
253− -- , benchIOSink value "null" Ops.null
254− , benchIOSink value "elem" (Ops.elem value)
255− , benchIOSink value "notElem" (Ops.notElem value)
256− , benchIOSink value "all" (Ops.all value)
257− , benchIOSink value "any" (Ops.any value)
258− , benchIOSink value "and" (Ops.and value)
259− , benchIOSink value "or" (Ops.or value)
260−
261− , benchIOSink value "length" Ops.length
262− , benchHoistSink value "length . generally" (Ops.length . IP.generally)
263− , benchIOSink value "sum" Ops.sum
264− , benchIOSink value "product" Ops.product
265−
266− , benchIOSink value "maximumBy" Ops.maximumBy
267− , benchIOSink value "maximum" Ops.maximum
268− , benchIOSink value "minimumBy" Ops.minimumBy
269− , benchIOSink value "minimum" Ops.minimum
270−
271− ]
272− , bgroup "folds"
273− [ benchIOSink value "drain" (S.fold FL.drain)
274− , benchIOSink value "drainN" (S.fold (IFL.drainN value))
275− , benchIOSink value "drainWhileTrue" (S.fold (IFL.drainWhile $ (<=) (value + 1)))
276− , benchIOSink value "drainWhileFalse" (S.fold (IFL.drainWhile $ (>=) (value + 1)))
277− , benchIOSink value "sink" (S.fold $ Sink.toFold Sink.drain)
278− , benchIOSink value "last" (S.fold FL.last)
279− , benchIOSink value "lastN.1" (S.fold (IA.lastN 1))
280− , benchIOSink value "lastN.10" (S.fold (IA.lastN 10))
281− , benchIOSink value "length" (S.fold FL.length)
282− , benchIOSink value "sum" (S.fold FL.sum)
283− , benchIOSink value "product" (S.fold FL.product)
284− , benchIOSink value "maximumBy" (S.fold (FL.maximumBy compare))
285− , benchIOSink value "maximum" (S.fold FL.maximum)
286− , benchIOSink value "minimumBy" (S.fold (FL.minimumBy compare))
287− , benchIOSink value "minimum" (S.fold FL.minimum)
288− , benchIOSink value "mean" (\s -> S.fold FL.mean (S.map (fromIntegral :: Int -> Double) s))
289− , benchIOSink value "variance" (\s -> S.fold FL.variance (S.map (fromIntegral :: Int -> Double) s))
290− , benchIOSink value "stdDev" (\s -> S.fold FL.stdDev (S.map (fromIntegral :: Int -> Double) s))
291−
292− , benchIOSink value "mconcat" (S.fold FL.mconcat . (S.map (Last . Just)))
293− , benchIOSink value "foldMap" (S.fold (FL.foldMap (Last . Just)))
294−
295− , benchIOSink value "index" (S.fold (FL.index (value + 1)))
296− , benchIOSink value "head" (S.fold FL.head)
297− , benchIOSink value "find" (S.fold (FL.find (== (value + 1))))
298− , benchIOSink value "findIndex" (S.fold (FL.findIndex (== (value + 1))))
299− , benchIOSink value "elemIndex" (S.fold (FL.elemIndex (value + 1)))
300−
301− , benchIOSink value "null" (S.fold FL.null)
302− , benchIOSink value "elem" (S.fold (FL.elem (value + 1)))
303− , benchIOSink value "notElem" (S.fold (FL.notElem (value + 1)))
304− , benchIOSink value "all" (S.fold (FL.all (<= (value + 1))))
305− , benchIOSink value "any" (S.fold (FL.any (> (value + 1))))
306− , benchIOSink value "and" (\s -> S.fold FL.and (S.map (<= (value + 1)) s))
307− , benchIOSink value "or" (\s -> S.fold FL.or (S.map (> (value + 1)) s))
308− ]
309− , bgroup "fold-multi-stream"
310− [ benchIOSink1 "eqBy" (Ops.eqBy value)
311− , benchIOSink1 "cmpBy" (Ops.cmpBy value)
312− , benchIOSink value "isPrefixOf" Ops.isPrefixOf
313− , benchIOSink value "isSubsequenceOf" Ops.isSubsequenceOf
314− , benchIOSink value "stripPrefix" Ops.stripPrefix
315− ]
316− , bgroup "folds-transforms"
317− [ benchIOSink value "drain" (S.fold FL.drain)
318− , benchIOSink value "lmap" (S.fold (IFL.lmap (+1) FL.drain))
319− , benchIOSink value "pipe-mapM"
320− (S.fold (IFL.transform (Pipe.mapM (\x -> return $ x + 1)) FL.drain))
321− ]
322− , bgroup "folds-compositions" -- Applicative
323− [
324− benchIOSink value "all,any" (S.fold ((,) <$> FL.all (<= (value + 1))
325− <*> FL.any (> (value + 1))))
326− , benchIOSink value "sum,length" (S.fold ((,) <$> FL.sum <*> FL.length))
327− ]
328− , bgroup "pipes"
329− [ benchIOSink value "mapM" (Ops.transformMapM serially 1)
330− , benchIOSink value "compose" (Ops.transformComposeMapM serially 1)
331− , benchIOSink value "tee" (Ops.transformTeeMapM serially 1)
332− , benchIOSink value "zip" (Ops.transformZipMapM serially 1)
333− ]
334− , bgroup "pipesX4"
335− [ benchIOSink value "mapM" (Ops.transformMapM serially 4)
336− , benchIOSink value "compose" (Ops.transformComposeMapM serially 4)
337− , benchIOSink value "tee" (Ops.transformTeeMapM serially 4)
338− , benchIOSink value "zip" (Ops.transformZipMapM serially 4)
339− ]
340− , bgroup "transformer"
341− [ benchIOSrc serially "evalState" (Ops.evalStateT value)
342− , benchIOSrc serially "withState" (Ops.withState value)
343− ]
344− , bgroup "transformation"
345− [ benchIOSink value "scanl" (Ops.scan 1)
346− , benchIOSink value "scanl1'" (Ops.scanl1' 1)
347− , benchIOSink value "map" (Ops.map 1)
348− , benchIOSink value "fmap" (Ops.fmap 1)
349− , benchIOSink value "mapM" (Ops.mapM serially 1)
350− , benchIOSink value "mapMaybe" (Ops.mapMaybe 1)
351− , benchIOSink value "mapMaybeM" (Ops.mapMaybeM 1)
352− , bench "sequence" $ nfIO $ randomRIO (1,1000) >>= \n ->
353− Ops.sequence serially (Ops.sourceUnfoldrMAction value n)
354− , benchIOSink value "findIndices" (Ops.findIndices value 1)
355− , benchIOSink value "elemIndices" (Ops.elemIndices value 1)
356− , benchIOSink value "foldrS" (Ops.foldrS 1)
357− , benchIOSink value "foldrSMap" (Ops.foldrSMap 1)
358− , benchIOSink value "foldrT" (Ops.foldrT 1)
359− , benchIOSink value "foldrTMap" (Ops.foldrTMap 1)
360− , benchIOSink value "tap" (Ops.tap 1)
361− , benchIOSink value "tapRate 1 second" (Ops.tapRate 1)
362− , benchIOSink value "pollCounts 1 second" (Ops.pollCounts 1)
363− , benchIOSink value "tapAsync" (Ops.tapAsync 1)
364− , benchIOSink value "tapAsyncS" (Ops.tapAsyncS 1)
365− ]
366− , bgroup "transformationX4"
367− [ benchIOSink value "scan" (Ops.scan 4)
368− , benchIOSink value "scanl1'" (Ops.scanl1' 4)
369− , benchIOSink value "map" (Ops.map 4)
370− , benchIOSink value "fmap" (Ops.fmap 4)
371− , benchIOSink value "mapM" (Ops.mapM serially 4)
372− , benchIOSink value "mapMaybe" (Ops.mapMaybe 4)
373− , benchIOSink value "mapMaybeM" (Ops.mapMaybeM 4)
374− -- , bench "sequence" $ nfIO $ randomRIO (1,1000) >>= \n ->
375− -- Ops.sequence serially (Ops.sourceUnfoldrMAction n)
376− , benchIOSink value "findIndices" (Ops.findIndices value 4)
377− , benchIOSink value "elemIndices" (Ops.elemIndices value 4)
378− ]
379− , bgroup "filtering"
380− [ benchIOSink value "filter-even" (Ops.filterEven 1)
381− , benchIOSink value "filter-all-out" (Ops.filterAllOut value 1)
382− , benchIOSink value "filter-all-in" (Ops.filterAllIn value 1)
383−
384− , benchIOSink value "take-all" (Ops.takeAll value 1)
385− , benchIOSink value "takeByTime-all"
386− (Ops.takeByTime (NanoSecond64 maxBound) 1)
387− , benchIOSink value "takeWhile-true" (Ops.takeWhileTrue value 1)
388− --, benchIOSink value "takeWhileM-true" (Ops.takeWhileMTrue 1)
389−
390− -- "drop-one" is dual to "last"
391− , benchIOSink value "drop-one" (Ops.dropOne 1)
392− , benchIOSink value "drop-all" (Ops.dropAll value 1)
393− , benchIOSink value "dropByTime-all"
394− (Ops.dropByTime (NanoSecond64 maxBound) 1)
395− , benchIOSink value "dropWhile-true" (Ops.dropWhileTrue value 1)
396− --, benchIOSink value "dropWhileM-true" (Ops.dropWhileMTrue 1)
397− , benchIOSink value "dropWhile-false" (Ops.dropWhileFalse value 1)
398−
399− , benchIOSink value "deleteBy" (Ops.deleteBy value 1)
400− , benchIOSink value "intersperse" (Ops.intersperse value 1)
401− , benchIOSink value "insertBy" (Ops.insertBy value 1)
402− ]
403− , bgroup "filteringX4"
404− [ benchIOSink value "filter-even" (Ops.filterEven 4)
405− , benchIOSink value "filter-all-out" (Ops.filterAllOut value 4)
406− , benchIOSink value "filter-all-in" (Ops.filterAllIn value 4)
407− , benchIOSink value "take-all" (Ops.takeAll value 4)
408− , benchIOSink value "takeWhile-true" (Ops.takeWhileTrue value 4)
409− --, benchIOSink value "takeWhileM-true" (Ops.takeWhileMTrue 4)
410− , benchIOSink value "drop-one" (Ops.dropOne 4)
411− , benchIOSink value "drop-all" (Ops.dropAll value 4)
412− , benchIOSink value "dropWhile-true" (Ops.dropWhileTrue value 4)
413− --, benchIOSink value "dropWhileM-true" (Ops.dropWhileMTrue 4)
414− , benchIOSink value "dropWhile-false" (Ops.dropWhileFalse value 4)
415− , benchIOSink value "deleteBy" (Ops.deleteBy value 4)
416− , benchIOSink value "intersperse" (Ops.intersperse value 4)
417− , benchIOSink value "insertBy" (Ops.insertBy value 4)
418− ]
419− , bgroup "joining"
420− [ benchIOSrc1 "zip (2,x/2)" (Ops.zip (value `div` 2))
421− , benchIOSrc1 "zipM (2,x/2)" (Ops.zipM (value `div` 2))
422− , benchIOSrc1 "mergeBy (2,x/2)" (Ops.mergeBy (value `div` 2))
423− , benchIOSrc1 "serial (2,x/2)" (Ops.serial2 (value `div` 2))
424− , benchIOSrc1 "append (2,x/2)" (Ops.append2 (value `div` 2))
425− , benchIOSrc1 "serial (2,2,x/4)" (Ops.serial4 (value `div` 4))
426− , benchIOSrc1 "append (2,2,x/4)" (Ops.append4 (value `div` 4))
427− , benchIOSrc1 "wSerial (2,x/2)" (Ops.wSerial2 value)
428− , benchIOSrc1 "interleave (2,x/2)" (Ops.interleave2 value)
429− , benchIOSrc1 "roundRobin (2,x/2)" (Ops.roundRobin2 value)
430− ]
431− , bgroup "concat-foldable"
432− [ benchIOSrc serially "foldMapWith" (Ops.sourceFoldMapWith value)
433− , benchIOSrc serially "foldMapWithM" (Ops.sourceFoldMapWithM value)
434− , benchIOSrc serially "foldMapM" (Ops.sourceFoldMapM value)
435− , benchIOSrc serially "foldWithConcatMapId" (Ops.sourceConcatMapId value)
436− ]
437− , bgroup "concat-serial"
438− [ benchIOSrc1 "concatMapPure (2,x/2)" (Ops.concatMapPure 2 (value `div` 2))
439− , benchIOSrc1 "concatMap (2,x/2)" (Ops.concatMap 2 (value `div` 2))
440− , benchIOSrc1 "concatMap (x/2,2)" (Ops.concatMap (value `div` 2) 2)
441− , benchIOSrc1 "concatMapRepl (x/4,4)" (Ops.concatMapRepl4xN value)
442− , benchIOSrc1 "concatUnfoldRepl (x/4,4)" (Ops.concatUnfoldRepl4xN value)
443−
444− , benchIOSrc1 "concatMapWithSerial (2,x/2)"
445− (Ops.concatMapWithSerial 2 (value `div` 2))
446− , benchIOSrc1 "concatMapWithSerial (x/2,2)"
447− (Ops.concatMapWithSerial (value `div` 2) 2)
448−
449− , benchIOSrc1 "concatMapWithAppend (2,x/2)"
450− (Ops.concatMapWithAppend 2 (value `div` 2))
451− ]
452− , bgroup "concat-interleave"
453− [ benchIOSrc1 "concatMapWithWSerial (2,x/2)"
454− (Ops.concatMapWithWSerial 2 (value `div` 2))
455− , benchIOSrc1 "concatMapWithWSerial (x/2,2)"
456− (Ops.concatMapWithWSerial (value `div` 2) 2)
457− ]
458− -- scanl-map and foldl-map are equivalent to the scan and fold in the foldl
459− -- library. If scan/fold followed by a map is efficient enough we may not
460− -- need monolithic implementations of these.
461− , bgroup "mixed"
462− [ benchIOSink value "scanl-map" (Ops.scanMap 1)
463− , benchIOSink value "foldl-map" Ops.foldl'ReduceMap
464− , benchIOSink value "sum-product-fold" Ops.sumProductFold
465− , benchIOSink value "sum-product-scan" Ops.sumProductScan
466− ]
467− , bgroup "mixedX4"
468− [ benchIOSink value "scan-map" (Ops.scanMap 4)
469− , benchIOSink value "drop-map" (Ops.dropMap 4)
470− , benchIOSink value "drop-scan" (Ops.dropScan 4)
471− , benchIOSink value "take-drop" (Ops.takeDrop value 4)
472− , benchIOSink value "take-scan" (Ops.takeScan value 4)
473− , benchIOSink value "take-map" (Ops.takeMap value 4)
474− , benchIOSink value "filter-drop" (Ops.filterDrop value 4)
475− , benchIOSink value "filter-take" (Ops.filterTake value 4)
476− , benchIOSink value "filter-scan" (Ops.filterScan 4)
477− , benchIOSink value "filter-scanl1" (Ops.filterScanl1 4)
478− , benchIOSink value "filter-map" (Ops.filterMap value 4)
479− ]
480− ]
481− , bgroup "wSerially"
482− [ bgroup "transformation"
483− [ benchIOSink value "fmap" $ Ops.fmap' wSerially 1
484− ]
485− ]
486− , bgroup "zipSerially"
487− [ bgroup "transformation"
488− [ benchIOSink value "fmap" $ Ops.fmap' zipSerially 1
489− ]
490− ]
491− -- Operations that cannot work on infinite streams.
492− --
493− -- We keep these in a spearate group so that we
494− -- can run these conveniently with smaller stream size.
495− --
496− -- These are also the operations that programmers should be aware of and
497− -- should avoid using in a streaming application.
498−
499− -- Operations using O(n) stack space but O(1) heap space.
500− -- Head recursive operations.
501− , bgroup "iterated"
502− [ benchIOSrc serially "mapMx10K" Ops.iterateMapM
503− , benchIOSrc serially "scanx100" Ops.iterateScan
504− , benchIOSrc serially "scanl1x100" Ops.iterateScanl1
505− , benchIOSrc serially "filterEvenx10K" Ops.iterateFilterEven
506− , benchIOSrc serially "takeAllx10K" (Ops.iterateTakeAll value)
507− , benchIOSrc serially "dropOnex10K" Ops.iterateDropOne
508− , benchIOSrc serially "dropWhileFalsex10K"
509− (Ops.iterateDropWhileFalse value)
510− , benchIOSrc serially "dropWhileTruex10K"
511− (Ops.iterateDropWhileTrue value)
512−
513− , benchIOSink bufValue "tail" Ops.tail
514− , benchIOSink bufValue "nullHeadTail" Ops.nullHeadTail
515− ]
516− -- Head recursive strict right folds.
517− , bgroup "foldr"
518− [
519− -- Right folds for reducing are inherently non-streaming as the
520− -- expression needs to be fully built before it can be reduced.
521− benchIOSink bufValue "foldrM/reduce/IO" Ops.foldrMReduce
522− , benchIdentitySink bufValue "foldrM/reduce/Identity" Ops.foldrMReduce
523−
524− -- accumulation due to strictness of IO monad
525− , benchIOSink bufValue "foldrM/build/IO" Ops.foldrMBuild
526− ]
527− -- Operations using O(1) stack space and O(n) heap space.
528− -- Tail recursive left folds
529− , bgroup "foldl"
530− [
531− -- Left folds for building a structure are inherently non-streaming as
532− -- the structure cannot be lazily consumed until fully built.
533− benchIOSink bufValue "foldl'/build/IO" Ops.foldl'Build
534− , benchIdentitySink bufValue "foldl'/build/Identity" Ops.foldl'Build
535− , benchIOSink bufValue "foldlM'/build/IO" Ops.foldlM'Build
536− , benchIdentitySink bufValue "foldlM'/build/Identity" Ops.foldlM'Build
537−
538− , benchIOSink bufValue "toStream" (S.fold IP.toStream)
539− , benchIOSink bufValue "toStreamRev" (S.fold IP.toStreamRev)
540−
541− , benchIOSink bufValue "toList" (S.fold FL.toList)
542− , benchIOSink bufValue "toListRevF" (S.fold IFL.toListRevF)
543−
544− -- Converting the stream to an array
545− , benchIOSink bufValue "lastN.Max" (S.fold (IA.lastN (bufValue + 1)))
546− , benchIOSink bufValue "writeN" (S.fold (A.writeN bufValue))
547−
548− -- Reversing/sorting a stream
549− , benchIOSink bufValue "reverse" (Ops.reverse 1)
550− , benchIOSink bufValue "reverse'" (Ops.reverse' 1)
551−
552− -- XXX the definitions of minimumBy and maximumBy in Data.Foldable use
553− -- foldl1 which does not work in constant memory for our implementation.
554− -- It works in constant memory for lists but even for lists it takes 15x
555− -- more time compared to our foldl' based implementation.
556− , bench "minimumBy" $ nf (flip Ops.foldableMinBy 1) value
557− , bench "maximumBy" $ nf (flip Ops.foldableMaxBy 1) value
558− , bench "minimumByList" $ nf (flip Ops.foldableListMinBy 1) value
559− ]
560−
561− -- Buffering operations using heap proportional to number of elements.
562− , bgroup "traversable"
563− [ -- Traversable instance
564− benchPureSinkIO bufValue "traverse" Ops.traversableTraverse
565− , benchPureSinkIO bufValue "sequenceA" Ops.traversableSequenceA
566− , benchPureSinkIO bufValue "mapM" Ops.traversableMapM
567− , benchPureSinkIO bufValue "sequence" Ops.traversableSequence
568− ]
569− , bgroup "buffering"
570− [
571− -- Buffers the output of show/read.
572− -- XXX can the outputs be streaming? Can we have special read/show
573− -- style type classes, readM/showM supporting streaming effects?
574− bench "readsPrec pure streams" $
575− nf Ops.readInstance (mkString bufValue)
576− , bench "readsPrec Haskell lists" $
577− nf Ops.readInstanceList (mkListString bufValue)
578− , bench "showPrec Haskell lists" $
579− nf Ops.showInstanceList (mkList bufValue)
580−
581− -- interleave x/4 streams of 4 elements each. Needs to buffer
582− -- proportional to x/4. This is different from WSerial because WSerial
583− -- expands slowly because of binary interleave behavior and this expands
584− -- immediately because of Nary interleave behavior.
585− , benchIOSrc1 "concatUnfoldInterleaveRepl (x/4,4)"
586− (Ops.concatUnfoldInterleaveRepl4xN bufValue)
587− , benchIOSrc1 "concatUnfoldRoundrobinRepl (x/4,4)"
588− (Ops.concatUnfoldRoundrobinRepl4xN bufValue)
589− ]
590− -- Both heap and stack space are O(n)
591− , bgroup "toList"
592− [
593− -- Converting the stream to a list or pure stream in a strict monad
594− benchIOSink bufValue "foldrMToList" Ops.foldrMBuild
595− , benchIOSink bufValue "toList" Ops.toList
596− , benchIOSink bufValue "toListRev" Ops.toListRev
597− -- , benchIOSink bufValue "toPure" Ops.toPure
598− -- , benchIOSink bufValue "toPureRev" Ops.toPureRev
599− ]
600− ]
benchmark/exe/Prelude/Serial/O_1_Space.hsnew file+434
@@ -0,0 +1,434 @@
1+-- |
2+-- Module : Main
3+-- Copyright : (c) 2018 Harendra Kumar
4+--
5+-- License : BSD3
6+-- Maintainer : streamly@composewell.com
7+
8+import Control.DeepSeq (NFData(..))
9+import Data.Functor.Identity (Identity, runIdentity)
10+import Data.Monoid (Last(..))
11+import System.Random (randomRIO)
12+
13+import qualified GHC.Exts as GHC
14+import qualified Streamly.Benchmark.Prelude as Ops
15+
16+import Streamly
17+import qualified Streamly.Data.Fold as FL
18+import qualified Streamly.Prelude as S
19+import qualified Streamly.Internal.Data.Sink as Sink
20+
21+import Streamly.Internal.Data.Time.Units
22+import qualified Streamly.Internal.Memory.Array as IA
23+import qualified Streamly.Internal.Data.Fold as IFL
24+import qualified Streamly.Internal.Prelude as IP
25+import qualified Streamly.Internal.Data.Pipe as Pipe
26+
27+import Gauge
28+import Common
29+
30+-------------------------------------------------------------------------------
31+--
32+-------------------------------------------------------------------------------
33+
34+-- We need a monadic bind here to make sure that the function f does not get
35+-- completely optimized out by the compiler in some cases.
36+
37+-- | Takes a fold method, and uses it with a default source.
38+{-# INLINE benchIOSink #-}
39+benchIOSink
40+ :: (IsStream t, NFData b)
41+ => Int -> String -> (t IO Int -> IO b) -> Benchmark
42+benchIOSink value name f = bench name $ nfIO $ randomRIO (1,1) >>= f . Ops.source value
43+
44+{-# INLINE benchHoistSink #-}
45+benchHoistSink
46+ :: (IsStream t, NFData b)
47+ => Int -> String -> (t Identity Int -> IO b) -> Benchmark
48+benchHoistSink value name f =
49+ bench name $ nfIO $ randomRIO (1,1) >>= f . Ops.sourceUnfoldr value
50+
51+-- XXX We should be using sourceUnfoldrM for fair comparison with IO monad, but
52+-- we can't use it as it requires MonadAsync constraint.
53+{-# INLINE benchIdentitySink #-}
54+benchIdentitySink
55+ :: (IsStream t, NFData b)
56+ => Int -> String -> (t Identity Int -> Identity b) -> Benchmark
57+benchIdentitySink value name f = bench name $ nf (f . Ops.sourceUnfoldr value) 1
58+
59+-- | Takes a source, and uses it with a default drain/fold method.
60+{-# INLINE benchIOSrc #-}
61+benchIOSrc
62+ :: (t IO a -> SerialT IO a)
63+ -> String
64+ -> (Int -> t IO a)
65+ -> Benchmark
66+benchIOSrc t name f =
67+ bench name $ nfIO $ randomRIO (1,1) >>= Ops.toNull t . f
68+
69+{-# INLINE benchPureSink #-}
70+benchPureSink :: NFData b => Int -> String -> (SerialT Identity Int -> b) -> Benchmark
71+benchPureSink value name f = benchPure name (Ops.sourceUnfoldr value) f
72+
73+-------------------------------------------------------------------------------
74+--
75+-------------------------------------------------------------------------------
76+
77+-- In addition to gauge options, the number of elements in the stream can be
78+-- passed using the --stream-size option.
79+--
80+main :: IO ()
81+main = do
82+ (value, cfg, benches) <- parseCLIOpts defaultStreamSize
83+ value `seq` runMode (mode cfg) cfg benches
84+ [ bgroup "serially"
85+ [ bgroup "pure"
86+ [ benchPureSink value "id" id
87+ , benchPureSink1 "eqBy" (Ops.eqByPure value)
88+ , benchPureSink value "==" Ops.eqInstance
89+ , benchPureSink value "/=" Ops.eqInstanceNotEq
90+ , benchPureSink1 "cmpBy" (Ops.cmpByPure value)
91+ , benchPureSink value "<" Ops.ordInstance
92+ , benchPureSink value "min" Ops.ordInstanceMin
93+ , benchPureSrc "IsList.fromList" (Ops.sourceIsList value)
94+ -- length is used to check for foldr/build fusion
95+ , benchPureSink value "length . IsList.toList" (length . GHC.toList)
96+ , benchPureSrc "IsString.fromString" (Ops.sourceIsString value)
97+ , benchPureSink value "showsPrec pure streams" Ops.showInstance
98+ , benchPureSink value "foldl'" Ops.pureFoldl'
99+ ]
100+ , bgroup "foldable"
101+ [ -- Foldable instance
102+ -- type class operations
103+ bench "foldl'" $ nf (Ops.foldableFoldl' value) 1
104+ , bench "foldrElem" $ nf (Ops.foldableFoldrElem value) 1
105+ -- , bench "null" $ nf (Ops.foldableNull value) 1
106+ , bench "elem" $ nf (Ops.foldableElem value) 1
107+ , bench "length" $ nf (Ops.foldableLength value) 1
108+ , bench "sum" $ nf (Ops.foldableSum value) 1
109+ , bench "product" $ nf (Ops.foldableProduct value) 1
110+ , bench "minimum" $ nf (Ops.foldableMin value) 1
111+ , bench "maximum" $ nf (Ops.foldableMax value) 1
112+ , bench "length . toList" $
113+ nf (length . Ops.foldableToList value) 1
114+
115+ -- folds
116+ , bench "notElem" $ nf (Ops.foldableNotElem value) 1
117+ , bench "find" $ nf (Ops.foldableFind value) 1
118+ , bench "all" $ nf (Ops.foldableAll value) 1
119+ , bench "any" $ nf (Ops.foldableAny value) 1
120+ , bench "and" $ nf (Ops.foldableAnd value) 1
121+ , bench "or" $ nf (Ops.foldableOr value) 1
122+
123+ -- Note: minimumBy/maximumBy do not work in constant memory they are in
124+ -- the O(n) group of benchmarks down below in this file.
125+
126+ -- Applicative and Traversable operations
127+ -- TBD: traverse_
128+ , benchIOSink1 "mapM_" (Ops.foldableMapM_ value)
129+ -- TBD: for_
130+ -- TBD: forM_
131+ , benchIOSink1 "sequence_" (Ops.foldableSequence_ value)
132+ -- TBD: sequenceA_
133+ -- TBD: asum
134+ -- , benchIOSink1 "msum" (Ops.foldableMsum value)
135+ ]
136+ , bgroup "generation"
137+ [ -- Most basic, barely stream continuations running
138+ benchIOSrc serially "unfoldr" (Ops.sourceUnfoldr value)
139+ , benchIOSrc serially "unfoldrM" (Ops.sourceUnfoldrM value)
140+ , benchIOSrc serially "intFromTo" (Ops.sourceIntFromTo value)
141+ , benchIOSrc serially "intFromThenTo" (Ops.sourceIntFromThenTo value)
142+ , benchIOSrc serially "integerFromStep" (Ops.sourceIntegerFromStep value)
143+ , benchIOSrc serially "fracFromThenTo" (Ops.sourceFracFromThenTo value)
144+ , benchIOSrc serially "fracFromTo" (Ops.sourceFracFromTo value)
145+ , benchIOSrc serially "fromList" (Ops.sourceFromList value)
146+ , benchIOSrc serially "fromListM" (Ops.sourceFromListM value)
147+ -- These are essentially cons and consM
148+ , benchIOSrc serially "fromFoldable" (Ops.sourceFromFoldable value)
149+ , benchIOSrc serially "fromFoldableM" (Ops.sourceFromFoldableM value)
150+ , benchIOSrc serially "currentTime/0.00001s"
151+ $ Ops.currentTime value 0.00001
152+ ]
153+ , bgroup "elimination"
154+ [ bgroup "reduce"
155+ [ bgroup "IO"
156+ [
157+ benchIOSink value "foldl'" Ops.foldl'Reduce
158+ , benchIOSink value "foldl1'" Ops.foldl1'Reduce
159+ , benchIOSink value "foldlM'" Ops.foldlM'Reduce
160+ ]
161+ , bgroup "Identity"
162+ [
163+ benchIdentitySink value "foldl'" Ops.foldl'Reduce
164+ , benchIdentitySink value "foldl1'" Ops.foldl1'Reduce
165+ , benchIdentitySink value "foldlM'" Ops.foldlM'Reduce
166+ ]
167+ ]
168+
169+ , bgroup "build"
170+ [ bgroup "IO"
171+ [ benchIOSink value "foldrMElem" (Ops.foldrMElem value)
172+ ]
173+ , bgroup "Identity"
174+ [ benchIdentitySink value "foldrMElem" (Ops.foldrMElem value)
175+ , benchIdentitySink value "foldrMToStreamLength"
176+ (S.length . runIdentity . Ops.foldrMToStream)
177+ , benchPureSink value "foldrMToListLength"
178+ (length . runIdentity . Ops.foldrMBuild)
179+ ]
180+ ]
181+ , benchIOSink value "uncons" Ops.uncons
182+ , benchIOSink value "toNull" $ Ops.toNull serially
183+ , benchIOSink value "mapM_" Ops.mapM_
184+
185+ , benchIOSink value "init" Ops.init
186+
187+ -- this is too low and causes all benchmarks reported in ns
188+ -- , benchIOSink value "head" Ops.head
189+ , benchIOSink value "last" Ops.last
190+ -- , benchIOSink value "lookup" Ops.lookup
191+ , benchIOSink value "find" (Ops.find value)
192+ , benchIOSink value "findIndex" (Ops.findIndex value)
193+ , benchIOSink value "elemIndex" (Ops.elemIndex value)
194+
195+ -- this is too low and causes all benchmarks reported in ns
196+ -- , benchIOSink value "null" Ops.null
197+ , benchIOSink value "elem" (Ops.elem value)
198+ , benchIOSink value "notElem" (Ops.notElem value)
199+ , benchIOSink value "all" (Ops.all value)
200+ , benchIOSink value "any" (Ops.any value)
201+ , benchIOSink value "and" (Ops.and value)
202+ , benchIOSink value "or" (Ops.or value)
203+
204+ , benchIOSink value "length" Ops.length
205+ , benchHoistSink value "length . generally" (Ops.length . IP.generally)
206+ , benchIOSink value "sum" Ops.sum
207+ , benchIOSink value "product" Ops.product
208+
209+ , benchIOSink value "maximumBy" Ops.maximumBy
210+ , benchIOSink value "maximum" Ops.maximum
211+ , benchIOSink value "minimumBy" Ops.minimumBy
212+ , benchIOSink value "minimum" Ops.minimum
213+
214+ ]
215+ , bgroup "folds"
216+ [ benchIOSink value "drain" (S.fold FL.drain)
217+ , benchIOSink value "drainN" (S.fold (IFL.drainN value))
218+ , benchIOSink value "drainWhileTrue" (S.fold (IFL.drainWhile $ (<=) (value + 1)))
219+ , benchIOSink value "drainWhileFalse" (S.fold (IFL.drainWhile $ (>=) (value + 1)))
220+ , benchIOSink value "sink" (S.fold $ Sink.toFold Sink.drain)
221+ , benchIOSink value "last" (S.fold FL.last)
222+ , benchIOSink value "lastN.1" (S.fold (IA.lastN 1))
223+ , benchIOSink value "lastN.10" (S.fold (IA.lastN 10))
224+ , benchIOSink value "length" (S.fold FL.length)
225+ , benchIOSink value "sum" (S.fold FL.sum)
226+ , benchIOSink value "product" (S.fold FL.product)
227+ , benchIOSink value "maximumBy" (S.fold (FL.maximumBy compare))
228+ , benchIOSink value "maximum" (S.fold FL.maximum)
229+ , benchIOSink value "minimumBy" (S.fold (FL.minimumBy compare))
230+ , benchIOSink value "minimum" (S.fold FL.minimum)
231+ , benchIOSink value "mean" (\s -> S.fold FL.mean (S.map (fromIntegral :: Int -> Double) s))
232+ , benchIOSink value "variance" (\s -> S.fold FL.variance (S.map (fromIntegral :: Int -> Double) s))
233+ , benchIOSink value "stdDev" (\s -> S.fold FL.stdDev (S.map (fromIntegral :: Int -> Double) s))
234+
235+ , benchIOSink value "mconcat" (S.fold FL.mconcat . (S.map (Last . Just)))
236+ , benchIOSink value "foldMap" (S.fold (FL.foldMap (Last . Just)))
237+
238+ , benchIOSink value "index" (S.fold (FL.index (value + 1)))
239+ , benchIOSink value "head" (S.fold FL.head)
240+ , benchIOSink value "find" (S.fold (FL.find (== (value + 1))))
241+ , benchIOSink value "findIndex" (S.fold (FL.findIndex (== (value + 1))))
242+ , benchIOSink value "elemIndex" (S.fold (FL.elemIndex (value + 1)))
243+
244+ , benchIOSink value "null" (S.fold FL.null)
245+ , benchIOSink value "elem" (S.fold (FL.elem (value + 1)))
246+ , benchIOSink value "notElem" (S.fold (FL.notElem (value + 1)))
247+ , benchIOSink value "all" (S.fold (FL.all (<= (value + 1))))
248+ , benchIOSink value "any" (S.fold (FL.any (> (value + 1))))
249+ , benchIOSink value "and" (\s -> S.fold FL.and (S.map (<= (value + 1)) s))
250+ , benchIOSink value "or" (\s -> S.fold FL.or (S.map (> (value + 1)) s))
251+ ]
252+ , bgroup "fold-multi-stream"
253+ [ benchIOSink1 "eqBy" (Ops.eqBy value)
254+ , benchIOSink1 "cmpBy" (Ops.cmpBy value)
255+ , benchIOSink value "isPrefixOf" Ops.isPrefixOf
256+ , benchIOSink value "isSubsequenceOf" Ops.isSubsequenceOf
257+ , benchIOSink value "stripPrefix" Ops.stripPrefix
258+ ]
259+ , bgroup "folds-transforms"
260+ [ benchIOSink value "drain" (S.fold FL.drain)
261+ , benchIOSink value "lmap" (S.fold (IFL.lmap (+1) FL.drain))
262+ , benchIOSink value "pipe-mapM"
263+ (S.fold (IFL.transform (Pipe.mapM (\x -> return $ x + 1)) FL.drain))
264+ ]
265+ , bgroup "folds-compositions" -- Applicative
266+ [
267+ benchIOSink value "all,any" (S.fold ((,) <$> FL.all (<= (value + 1))
268+ <*> FL.any (> (value + 1))))
269+ , benchIOSink value "sum,length" (S.fold ((,) <$> FL.sum <*> FL.length))
270+ ]
271+ , bgroup "pipes"
272+ [ benchIOSink value "mapM" (Ops.transformMapM serially 1)
273+ , benchIOSink value "compose" (Ops.transformComposeMapM serially 1)
274+ , benchIOSink value "tee" (Ops.transformTeeMapM serially 1)
275+ , benchIOSink value "zip" (Ops.transformZipMapM serially 1)
276+ ]
277+ , bgroup "pipesX4"
278+ [ benchIOSink value "mapM" (Ops.transformMapM serially 4)
279+ , benchIOSink value "compose" (Ops.transformComposeMapM serially 4)
280+ , benchIOSink value "tee" (Ops.transformTeeMapM serially 4)
281+ , benchIOSink value "zip" (Ops.transformZipMapM serially 4)
282+ ]
283+ , bgroup "transformer"
284+ [ benchIOSrc serially "evalState" (Ops.evalStateT value)
285+ , benchIOSrc serially "withState" (Ops.withState value)
286+ ]
287+ , bgroup "transformation"
288+ [ benchIOSink value "scanl" (Ops.scan 1)
289+ , benchIOSink value "scanl1'" (Ops.scanl1' 1)
290+ , benchIOSink value "map" (Ops.map 1)
291+ , benchIOSink value "fmap" (Ops.fmap 1)
292+ , benchIOSink value "mapM" (Ops.mapM serially 1)
293+ , benchIOSink value "mapMaybe" (Ops.mapMaybe 1)
294+ , benchIOSink value "mapMaybeM" (Ops.mapMaybeM 1)
295+ , bench "sequence" $ nfIO $ randomRIO (1,1000) >>= \n ->
296+ Ops.sequence serially (Ops.sourceUnfoldrMAction value n)
297+ , benchIOSink value "findIndices" (Ops.findIndices value 1)
298+ , benchIOSink value "elemIndices" (Ops.elemIndices value 1)
299+ , benchIOSink value "foldrS" (Ops.foldrS 1)
300+ , benchIOSink value "foldrSMap" (Ops.foldrSMap 1)
301+ , benchIOSink value "foldrT" (Ops.foldrT 1)
302+ , benchIOSink value "foldrTMap" (Ops.foldrTMap 1)
303+ , benchIOSink value "tap" (Ops.tap 1)
304+ , benchIOSink value "tapRate 1 second" (Ops.tapRate 1)
305+ , benchIOSink value "pollCounts 1 second" (Ops.pollCounts 1)
306+ , benchIOSink value "tapAsync" (Ops.tapAsync 1)
307+ , benchIOSink value "tapAsyncS" (Ops.tapAsyncS 1)
308+ ]
309+ , bgroup "transformationX4"
310+ [ benchIOSink value "scan" (Ops.scan 4)
311+ , benchIOSink value "scanl1'" (Ops.scanl1' 4)
312+ , benchIOSink value "map" (Ops.map 4)
313+ , benchIOSink value "fmap" (Ops.fmap 4)
314+ , benchIOSink value "mapM" (Ops.mapM serially 4)
315+ , benchIOSink value "mapMaybe" (Ops.mapMaybe 4)
316+ , benchIOSink value "mapMaybeM" (Ops.mapMaybeM 4)
317+ -- , bench "sequence" $ nfIO $ randomRIO (1,1000) >>= \n ->
318+ -- Ops.sequence serially (Ops.sourceUnfoldrMAction n)
319+ , benchIOSink value "findIndices" (Ops.findIndices value 4)
320+ , benchIOSink value "elemIndices" (Ops.elemIndices value 4)
321+ ]
322+ , bgroup "filtering"
323+ [ benchIOSink value "filter-even" (Ops.filterEven 1)
324+ , benchIOSink value "filter-all-out" (Ops.filterAllOut value 1)
325+ , benchIOSink value "filter-all-in" (Ops.filterAllIn value 1)
326+
327+ , benchIOSink value "take-all" (Ops.takeAll value 1)
328+ , benchIOSink value "takeByTime-all"
329+ (Ops.takeByTime (NanoSecond64 maxBound) 1)
330+ , benchIOSink value "takeWhile-true" (Ops.takeWhileTrue value 1)
331+ --, benchIOSink value "takeWhileM-true" (Ops.takeWhileMTrue 1)
332+
333+ -- "drop-one" is dual to "last"
334+ , benchIOSink value "drop-one" (Ops.dropOne 1)
335+ , benchIOSink value "drop-all" (Ops.dropAll value 1)
336+ , benchIOSink value "dropByTime-all"
337+ (Ops.dropByTime (NanoSecond64 maxBound) 1)
338+ , benchIOSink value "dropWhile-true" (Ops.dropWhileTrue value 1)
339+ --, benchIOSink value "dropWhileM-true" (Ops.dropWhileMTrue 1)
340+ , benchIOSink value "dropWhile-false" (Ops.dropWhileFalse value 1)
341+
342+ , benchIOSink value "deleteBy" (Ops.deleteBy value 1)
343+ , benchIOSink value "intersperse" (Ops.intersperse value 1)
344+ , benchIOSink value "insertBy" (Ops.insertBy value 1)
345+ ]
346+ , bgroup "filteringX4"
347+ [ benchIOSink value "filter-even" (Ops.filterEven 4)
348+ , benchIOSink value "filter-all-out" (Ops.filterAllOut value 4)
349+ , benchIOSink value "filter-all-in" (Ops.filterAllIn value 4)
350+ , benchIOSink value "take-all" (Ops.takeAll value 4)
351+ , benchIOSink value "takeWhile-true" (Ops.takeWhileTrue value 4)
352+ --, benchIOSink value "takeWhileM-true" (Ops.takeWhileMTrue 4)
353+ , benchIOSink value "drop-one" (Ops.dropOne 4)
354+ , benchIOSink value "drop-all" (Ops.dropAll value 4)
355+ , benchIOSink value "dropWhile-true" (Ops.dropWhileTrue value 4)
356+ --, benchIOSink value "dropWhileM-true" (Ops.dropWhileMTrue 4)
357+ , benchIOSink value "dropWhile-false" (Ops.dropWhileFalse value 4)
358+ , benchIOSink value "deleteBy" (Ops.deleteBy value 4)
359+ , benchIOSink value "intersperse" (Ops.intersperse value 4)
360+ , benchIOSink value "insertBy" (Ops.insertBy value 4)
361+ ]
362+ , bgroup "joining"
363+ [ benchIOSrc1 "zip (2,x/2)" (Ops.zip (value `div` 2))
364+ , benchIOSrc1 "zipM (2,x/2)" (Ops.zipM (value `div` 2))
365+ , benchIOSrc1 "mergeBy (2,x/2)" (Ops.mergeBy (value `div` 2))
366+ , benchIOSrc1 "serial (2,x/2)" (Ops.serial2 (value `div` 2))
367+ , benchIOSrc1 "append (2,x/2)" (Ops.append2 (value `div` 2))
368+ , benchIOSrc1 "serial (2,2,x/4)" (Ops.serial4 (value `div` 4))
369+ , benchIOSrc1 "append (2,2,x/4)" (Ops.append4 (value `div` 4))
370+ , benchIOSrc1 "wSerial (2,x/2)" (Ops.wSerial2 value)
371+ , benchIOSrc1 "interleave (2,x/2)" (Ops.interleave2 value)
372+ , benchIOSrc1 "roundRobin (2,x/2)" (Ops.roundRobin2 value)
373+ ]
374+ , bgroup "concat-foldable"
375+ [ benchIOSrc serially "foldMapWith" (Ops.sourceFoldMapWith value)
376+ , benchIOSrc serially "foldMapWithM" (Ops.sourceFoldMapWithM value)
377+ , benchIOSrc serially "foldMapM" (Ops.sourceFoldMapM value)
378+ , benchIOSrc serially "foldWithConcatMapId" (Ops.sourceConcatMapId value)
379+ ]
380+ , bgroup "concat-serial"
381+ [ benchIOSrc1 "concatMapPure (2,x/2)" (Ops.concatMapPure 2 (value `div` 2))
382+ , benchIOSrc1 "concatMap (2,x/2)" (Ops.concatMap 2 (value `div` 2))
383+ , benchIOSrc1 "concatMap (x/2,2)" (Ops.concatMap (value `div` 2) 2)
384+ , benchIOSrc1 "concatMapRepl (x/4,4)" (Ops.concatMapRepl4xN value)
385+ , benchIOSrc1 "concatUnfoldRepl (x/4,4)" (Ops.concatUnfoldRepl4xN value)
386+
387+ , benchIOSrc1 "concatMapWithSerial (2,x/2)"
388+ (Ops.concatMapWithSerial 2 (value `div` 2))
389+ , benchIOSrc1 "concatMapWithSerial (x/2,2)"
390+ (Ops.concatMapWithSerial (value `div` 2) 2)
391+
392+ , benchIOSrc1 "concatMapWithAppend (2,x/2)"
393+ (Ops.concatMapWithAppend 2 (value `div` 2))
394+ ]
395+ , bgroup "concat-interleave"
396+ [ benchIOSrc1 "concatMapWithWSerial (2,x/2)"
397+ (Ops.concatMapWithWSerial 2 (value `div` 2))
398+ , benchIOSrc1 "concatMapWithWSerial (x/2,2)"
399+ (Ops.concatMapWithWSerial (value `div` 2) 2)
400+ ]
401+ -- scanl-map and foldl-map are equivalent to the scan and fold in the foldl
402+ -- library. If scan/fold followed by a map is efficient enough we may not
403+ -- need monolithic implementations of these.
404+ , bgroup "mixed"
405+ [ benchIOSink value "scanl-map" (Ops.scanMap 1)
406+ , benchIOSink value "foldl-map" Ops.foldl'ReduceMap
407+ , benchIOSink value "sum-product-fold" Ops.sumProductFold
408+ , benchIOSink value "sum-product-scan" Ops.sumProductScan
409+ ]
410+ , bgroup "mixedX4"
411+ [ benchIOSink value "scan-map" (Ops.scanMap 4)
412+ , benchIOSink value "drop-map" (Ops.dropMap 4)
413+ , benchIOSink value "drop-scan" (Ops.dropScan 4)
414+ , benchIOSink value "take-drop" (Ops.takeDrop value 4)
415+ , benchIOSink value "take-scan" (Ops.takeScan value 4)
416+ , benchIOSink value "take-map" (Ops.takeMap value 4)
417+ , benchIOSink value "filter-drop" (Ops.filterDrop value 4)
418+ , benchIOSink value "filter-take" (Ops.filterTake value 4)
419+ , benchIOSink value "filter-scan" (Ops.filterScan 4)
420+ , benchIOSink value "filter-scanl1" (Ops.filterScanl1 4)
421+ , benchIOSink value "filter-map" (Ops.filterMap value 4)
422+ ]
423+ ]
424+ , bgroup "wSerially"
425+ [ bgroup "transformation"
426+ [ benchIOSink value "fmap" $ Ops.fmap' wSerially 1
427+ ]
428+ ]
429+ , bgroup "zipSerially"
430+ [ bgroup "transformation"
431+ [ benchIOSink value "fmap" $ Ops.fmap' zipSerially 1
432+ ]
433+ ]
434+ ]
benchmark/exe/Prelude/Serial/O_n_Heap.hsnew file+109
@@ -0,0 +1,109 @@
1+-- |
2+-- Module : Main
3+-- Copyright : (c) 2018 Harendra Kumar
4+--
5+-- License : BSD3
6+-- Maintainer : streamly@composewell.com
7+
8+import Control.DeepSeq (NFData(..))
9+import Data.Functor.Identity (Identity)
10+import System.Random (randomRIO)
11+
12+import qualified Streamly.Benchmark.Prelude as Ops
13+
14+import qualified Streamly.Data.Fold as FL
15+import qualified Streamly.Memory.Array as A
16+import qualified Streamly.Prelude as S
17+
18+import qualified Streamly.Internal.Data.Fold as IFL
19+import qualified Streamly.Internal.Memory.Array as IA
20+import qualified Streamly.Internal.Prelude as IP
21+
22+import Gauge
23+import Streamly
24+import Common
25+
26+-------------------------------------------------------------------------------
27+--
28+-------------------------------------------------------------------------------
29+
30+-- We need a monadic bind here to make sure that the function f does not get
31+-- completely optimized out by the compiler in some cases.
32+
33+-- | Takes a fold method, and uses it with a default source.
34+{-# INLINE benchIOSink #-}
35+benchIOSink
36+ :: (IsStream t, NFData b)
37+ => Int -> String -> (t IO Int -> IO b) -> Benchmark
38+benchIOSink value name f =
39+ bench name $ nfIO $ randomRIO (1,1) >>= f . Ops.source value
40+
41+-- XXX We should be using sourceUnfoldrM for fair comparison with IO monad, but
42+-- we can't use it as it requires MonadAsync constraint.
43+{-# INLINE benchIdentitySink #-}
44+benchIdentitySink
45+ :: (IsStream t, NFData b)
46+ => Int -> String -> (t Identity Int -> Identity b) -> Benchmark
47+benchIdentitySink value name f = bench name $ nf (f . Ops.sourceUnfoldr value) 1
48+
49+-------------------------------------------------------------------------------
50+--
51+-------------------------------------------------------------------------------
52+
53+-- In addition to gauge options, the number of elements in the stream can be
54+-- passed using the --stream-size option.
55+--
56+main :: IO ()
57+main = do
58+ -- XXX Fix indentation
59+ (value, cfg, benches) <- parseCLIOpts defaultStreamSize
60+ size <- limitStreamSize value
61+
62+ size `seq` runMode (mode cfg) cfg benches
63+ -- Operations using O(1) stack space and O(n) heap space.
64+ -- Tail recursive left folds
65+ [ bgroup "foldl"
66+ [
67+ -- Left folds for building a structure are inherently non-streaming
68+ -- as the structure cannot be lazily consumed until fully built.
69+ benchIOSink size "foldl'/build/IO" Ops.foldl'Build
70+ , benchIdentitySink size "foldl'/build/Identity" Ops.foldl'Build
71+ , benchIOSink size "foldlM'/build/IO" Ops.foldlM'Build
72+ , benchIdentitySink size "foldlM'/build/Identity" Ops.foldlM'Build
73+
74+ , benchIOSink size "toStream" (S.fold IP.toStream)
75+ , benchIOSink size "toStreamRev" (S.fold IP.toStreamRev)
76+
77+ , benchIOSink size "toList" (S.fold FL.toList)
78+ , benchIOSink size "toListRevF" (S.fold IFL.toListRevF)
79+
80+ -- Converting the stream to an array
81+ , benchIOSink size "lastN.Max" (S.fold (IA.lastN (size + 1)))
82+ , benchIOSink size "writeN" (S.fold (A.writeN size))
83+
84+ -- Reversing/sorting a stream
85+ , benchIOSink size "reverse" (Ops.reverse 1)
86+ , benchIOSink size "reverse'" (Ops.reverse' 1)
87+ ]
88+ , bgroup "buffering"
89+ [
90+ -- Buffers the output of show/read.
91+ -- XXX can the outputs be streaming? Can we have special read/show
92+ -- style type classes, readM/showM supporting streaming effects?
93+ bench "readsPrec pure streams" $
94+ nf Ops.readInstance (mkString size)
95+ , bench "readsPrec Haskell lists" $
96+ nf Ops.readInstanceList (mkListString size)
97+ , bench "showPrec Haskell lists" $
98+ nf Ops.showInstanceList (mkList size)
99+
100+ -- interleave x/4 streams of 4 elements each. Needs to buffer
101+ -- proportional to x/4. This is different from WSerial because
102+ -- WSerial expands slowly because of binary interleave behavior and
103+ -- this expands immediately because of Nary interleave behavior.
104+ , benchIOSrc1 "concatUnfoldInterleaveRepl (x/4,4)"
105+ (Ops.concatUnfoldInterleaveRepl4xN size)
106+ , benchIOSrc1 "concatUnfoldRoundrobinRepl (x/4,4)"
107+ (Ops.concatUnfoldRoundrobinRepl4xN size)
108+ ]
109+ ]
benchmark/exe/Prelude/Serial/O_n_Space.hsnew file+104
@@ -0,0 +1,104 @@
1+-- |
2+-- Module : Main
3+-- Copyright : (c) 2018 Harendra Kumar
4+--
5+-- License : BSD3
6+-- Maintainer : streamly@composewell.com
7+
8+import Control.DeepSeq (NFData(..))
9+import Data.Functor.Identity (Identity)
10+import System.Random (randomRIO)
11+
12+import qualified Streamly.Benchmark.Prelude as Ops
13+
14+import Streamly
15+import Gauge
16+import Common
17+
18+-------------------------------------------------------------------------------
19+-- Benchmarking utilities
20+-------------------------------------------------------------------------------
21+
22+-- XXX we should replace these with the ones in the Common module. We should
23+-- not pass the generation or drain function to he benchmarking function. If we
24+-- generate and terminate the whole pipeline in the benchmarked op itself we
25+-- can use inspection tests on it.
26+--
27+-- We need a monadic bind here to make sure that the function f does not get
28+-- completely optimized out by the compiler in some cases.
29+
30+-- | Takes a fold method, and uses it with a default source.
31+{-# INLINE benchIOSink #-}
32+benchIOSink
33+ :: (IsStream t, NFData b)
34+ => Int -> String -> (t IO Int -> IO b) -> Benchmark
35+benchIOSink value name f = bench name $ nfIO $ randomRIO (1,1) >>= f . Ops.source value
36+
37+-- XXX We should be using sourceUnfoldrM for fair comparison with IO monad, but
38+-- we can't use it as it requires MonadAsync constraint.
39+{-# INLINE benchIdentitySink #-}
40+benchIdentitySink
41+ :: (IsStream t, NFData b)
42+ => Int -> String -> (t Identity Int -> Identity b) -> Benchmark
43+benchIdentitySink value name f = bench name $ nf (f . Ops.sourceUnfoldr value) 1
44+
45+{-# INLINE benchPureSinkIO #-}
46+benchPureSinkIO
47+ :: NFData b
48+ => Int -> String -> (SerialT Identity Int -> IO b) -> Benchmark
49+benchPureSinkIO value name f =
50+ bench name $ nfIO $ randomRIO (1, 1) >>= f . Ops.sourceUnfoldr value
51+
52+-------------------------------------------------------------------------------
53+-- Benchmarks
54+-------------------------------------------------------------------------------
55+
56+-- In addition to gauge options, the number of elements in the stream can be
57+-- passed using the --stream-size option.
58+--
59+main :: IO ()
60+main = do
61+ (value, cfg, benches) <- parseCLIOpts defaultStreamSize
62+ size <- limitStreamSize value
63+
64+ size `seq` runMode (mode cfg) cfg benches
65+ -- Both heap and stack space are O(n)
66+ [ bgroup "toList" -- < 2MB
67+ [
68+ -- Converting the stream to a list or pure stream in a strict monad
69+ benchIOSink size "foldrMToList" Ops.foldrMBuild
70+ , benchIOSink size "toList" Ops.toList
71+ , benchIOSink size "toListRev" Ops.toListRev
72+ -- , benchIOSink size "toPure" Ops.toPure
73+ -- , benchIOSink size "toPureRev" Ops.toPureRev
74+ ]
75+ -- Buffering operations using heap proportional to number of elements.
76+ , bgroup "traversable" -- < 2MB
77+ [ -- Traversable instance
78+ benchPureSinkIO size "traverse" Ops.traversableTraverse
79+ , benchPureSinkIO size "sequenceA" Ops.traversableSequenceA
80+ , benchPureSinkIO size "mapM" Ops.traversableMapM
81+ , benchPureSinkIO size "sequence" Ops.traversableSequence
82+ ]
83+ -- Head recursive strict right folds.
84+ , bgroup "foldr"
85+ [ -- < 2MB
86+ -- accumulation due to strictness of IO monad
87+ benchIOSink size "foldrM/build/IO" Ops.foldrMBuild
88+ -- Right folds for reducing are inherently non-streaming as the
89+ -- expression needs to be fully built before it can be reduced.
90+ , benchIdentitySink size "foldrM/reduce/Identity" Ops.foldrMReduce
91+
92+ -- takes < 4MB
93+ , benchIOSink size "foldrM/reduce/IO" Ops.foldrMReduce
94+
95+ -- XXX the definitions of minimumBy and maximumBy in Data.Foldable use
96+ -- foldl1 which does not work in constant memory for our implementation.
97+ -- It works in constant memory for lists but even for lists it takes 15x
98+ -- more time compared to our foldl' based implementation.
99+ -- XXX these take < 16M stack space
100+ , bench "minimumBy" $ nf (flip Ops.foldableMinBy 1) size
101+ , bench "maximumBy" $ nf (flip Ops.foldableMaxBy 1) size
102+ , bench "minimumByList" $ nf (flip Ops.foldableListMinBy 1) size
103+ ]
104+ ]
benchmark/exe/Prelude/Serial/O_n_Stack.hsnew file+72
@@ -0,0 +1,72 @@
1+-- |
2+-- Module : Main
3+-- Copyright : (c) 2018 Harendra Kumar
4+--
5+-- License : BSD3
6+-- Maintainer : streamly@composewell.com
7+
8+import Control.DeepSeq (NFData(..))
9+import System.Random (randomRIO)
10+
11+import Common -- (parseCLIOpts)
12+
13+import qualified Streamly.Benchmark.Prelude as Ops
14+
15+import Streamly
16+import Gauge
17+
18+-------------------------------------------------------------------------------
19+--
20+-------------------------------------------------------------------------------
21+
22+-- We need a monadic bind here to make sure that the function f does not get
23+-- completely optimized out by the compiler in some cases.
24+
25+-- | Takes a fold method, and uses it with a default source.
26+{-# INLINE benchIOSink #-}
27+benchIOSink
28+ :: (IsStream t, NFData b)
29+ => Int -> String -> (t IO Int -> IO b) -> Benchmark
30+benchIOSink value name f = bench name $ nfIO $ randomRIO (1,1) >>= f . Ops.source value
31+
32+-- | Takes a source, and uses it with a default drain/fold method.
33+{-# INLINE benchIOSrc #-}
34+benchIOSrc
35+ :: (t IO a -> SerialT IO a)
36+ -> String
37+ -> (Int -> t IO a)
38+ -> Benchmark
39+benchIOSrc t name f =
40+ bench name $ nfIO $ randomRIO (1,1) >>= Ops.toNull t . f
41+
42+-------------------------------------------------------------------------------
43+--
44+-------------------------------------------------------------------------------
45+
46+-- In addition to gauge options, the number of elements in the stream can be
47+-- passed using the --stream-size option.
48+--
49+main :: IO ()
50+main = do
51+ (value, cfg, benches) <- parseCLIOpts defaultStreamSize
52+ size <- limitStreamSize value
53+
54+ -- Operations using O(n) stack space but O(1) heap space.
55+ size `seq` runMode (mode cfg) cfg benches
56+ -- Head recursive operations.
57+ [ bgroup "iterated"
58+ [ benchIOSrc serially "mapMx10K" Ops.iterateMapM
59+ , benchIOSrc serially "scanx100" Ops.iterateScan
60+ , benchIOSrc serially "scanl1x100" Ops.iterateScanl1
61+ , benchIOSrc serially "filterEvenx10K" Ops.iterateFilterEven
62+ , benchIOSrc serially "takeAllx10K" (Ops.iterateTakeAll size)
63+ , benchIOSrc serially "dropOnex10K" Ops.iterateDropOne
64+ , benchIOSrc serially "dropWhileFalsex10K"
65+ (Ops.iterateDropWhileFalse size)
66+ , benchIOSrc serially "dropWhileTruex10K"
67+ (Ops.iterateDropWhileTrue size)
68+
69+ , benchIOSink size "tail" Ops.tail
70+ , benchIOSink size "nullHeadTail" Ops.nullHeadTail
71+ ]
72+ ]
benchmark/streamly-benchmarks.cabalnew file+392
@@ -0,0 +1,392 @@
1+cabal-version: 2.2
2+name: streamly-benchmarks
3+version: 0.0.0
4+synopsis: Benchmarks for streamly
5+description: Benchmarks are separated from the main package because we
6+ want to have a library for benchmarks to reuse the code across different
7+ benchmark executables. For example, we have common benchmarking code for
8+ different types of streams. We need different benchmarking executables
9+ for serial, async, ahead style streams, therefore, we need to use
10+ the common code in several benchmarks, just changing the type of
11+ the stream. It takes a long time to compile this file and it gets
12+ compiled for each benchmarks once if we do not have a library. Cabal
13+ does no support internal libraries without per-component builds and
14+ per-component builds are not supported with Configure, so we are not
15+ left with any other choice.
16+
17+flag fusion-plugin
18+ description: Use fusion plugin for benchmarks and executables
19+ manual: True
20+ default: False
21+
22+flag inspection
23+ description: Enable inspection testing
24+ manual: True
25+ default: False
26+
27+flag debug
28+ description: Debug build with asserts enabled
29+ manual: True
30+ default: False
31+
32+flag dev
33+ description: Development build
34+ manual: True
35+ default: False
36+
37+flag has-llvm
38+ description: Use llvm backend for better performance
39+ manual: True
40+ default: False
41+
42+flag no-charts
43+ description: Disable benchmark charts in development build
44+ manual: True
45+ default: False
46+
47+-------------------------------------------------------------------------------
48+-- Common stanzas
49+-------------------------------------------------------------------------------
50+
51+common compile-options
52+ default-language: Haskell2010
53+
54+ if flag(dev)
55+ cpp-options: -DDEVBUILD
56+
57+ if flag(inspection)
58+ cpp-options: -DINSPECTION
59+
60+ ghc-options: -Wall
61+
62+ if flag(has-llvm)
63+ ghc-options: -fllvm
64+
65+ if flag(dev)
66+ ghc-options: -Wmissed-specialisations
67+ -Wall-missed-specialisations
68+
69+ if flag(dev) || flag(debug)
70+ ghc-options: -fno-ignore-asserts
71+
72+ if impl(ghc >= 8.0)
73+ ghc-options: -Wcompat
74+ -Wunrecognised-warning-flags
75+ -Widentities
76+ -Wincomplete-record-updates
77+ -Wincomplete-uni-patterns
78+ -Wredundant-constraints
79+ -Wnoncanonical-monad-instances
80+
81+common optimization-options
82+ ghc-options: -O2
83+ -fdicts-strict
84+ -fspec-constr-recursive=16
85+ -fmax-worker-args=16
86+ if flag(fusion-plugin) && !impl(ghcjs) && !impl(ghc < 8.6)
87+ ghc-options: -fplugin Fusion.Plugin
88+
89+-- We need optimization options here to optimize internal (non-inlined)
90+-- versions of functions. Also, we have some benchmarking inspection tests
91+-- part of the library when built with --benchmarks flag. Thos tests fail
92+-- if we do not use optimization options here. It was observed that due to
93+-- -O2 here some concurrent/nested benchmarks improved and others regressed.
94+-- We can investigate a bit more here why the regression occurred.
95+common lib-options
96+ import: compile-options, optimization-options
97+
98+-------------------------------------------------------------------------------
99+-- Library
100+-------------------------------------------------------------------------------
101+
102+library
103+ import: lib-options
104+ hs-source-dirs: .
105+ exposed-modules:
106+ Common
107+
108+ build-depends:
109+ -- Core libraries shipped with ghc, the min and max
110+ -- constraints of these libraries should match with
111+ -- the GHC versions we support
112+ base >= 4.8 && < 5
113+ , deepseq >= 1.4.1 && < 1.5
114+
115+ -- other libraries
116+ , streamly >= 0.7.0
117+ , gauge >= 0.2.4 && < 0.3
118+ , random >= 1.0 && < 2.0
119+
120+library lib-prelude
121+ import: lib-options
122+ hs-source-dirs: .
123+ exposed-modules:
124+ Streamly.Benchmark.Prelude
125+ build-depends:
126+ -- Core libraries shipped with ghc, the min and max
127+ -- constraints of these libraries should match with
128+ -- the GHC versions we support
129+ base >= 4.8 && < 5
130+ , deepseq >= 1.4.1 && < 1.5
131+ , mtl >= 2.2 && < 3
132+
133+ -- other libraries
134+ , streamly >= 0.7.0
135+ if flag(inspection)
136+ build-depends: template-haskell >= 2.14 && < 2.16
137+ , inspection-testing >= 0.4 && < 0.5
138+ -- Array uses a Storable constraint in dev build making several inspection
139+ -- tests fail
140+ if flag(dev) && flag(inspection)
141+ build-depends: inspection-and-dev-flags-cannot-be-used-together
142+
143+-------------------------------------------------------------------------------
144+-- Benchmarks
145+-------------------------------------------------------------------------------
146+
147+common bench-depends
148+ build-depends:
149+ -- Core libraries shipped with ghc, the min and max
150+ -- constraints of these libraries should match with
151+ -- the GHC versions we support
152+ base >= 4.8 && < 5
153+ , deepseq >= 1.4.1 && < 1.5
154+ , mtl >= 2.2 && < 3
155+
156+ -- other libraries
157+ , streamly-benchmarks
158+ , streamly >= 0.7.0
159+ , random >= 1.0 && < 2.0
160+ , gauge >= 0.2.4 && < 0.3
161+ if flag(fusion-plugin) && !impl(ghcjs) && !impl(ghc < 8.6)
162+ build-depends:
163+ fusion-plugin >= 0.2 && < 0.3
164+ if impl(ghc < 8.0)
165+ build-depends:
166+ transformers >= 0.4 && < 0.6
167+ if flag(inspection)
168+ build-depends: template-haskell >= 2.14 && < 2.16
169+ , inspection-testing >= 0.4 && < 0.5
170+
171+-- Whatever stack size below 32K we use GHC seems to report the stack size as
172+-- 32K at crash. Even K0K works. Therefore it probably does not make sense to
173+-- set it to lower than 32K.
174+
175+common bench-options
176+ import: compile-options, optimization-options, bench-depends
177+ ghc-options: -with-rtsopts "-T -K32K -M16M"
178+
179+-- Some benchmarks are threaded some are not
180+common bench-options-threaded
181+ import: compile-options, optimization-options, bench-depends
182+ -- -threaded and -N2 is important because some GC and space leak issues
183+ -- trigger only with these options.
184+ ghc-options: -threaded -with-rtsopts "-T -N2 -K32K -M16M"
185+
186+-------------------------------------------------------------------------------
187+-- Serial Streams
188+-------------------------------------------------------------------------------
189+
190+benchmark linear
191+-- benchmark serial-O-1-space
192+ import: bench-options
193+ type: exitcode-stdio-1.0
194+ ghc-options: -with-rtsopts "-T -K36K -M16M"
195+ hs-source-dirs: exe
196+ main-is: Prelude/Serial/O_1_Space.hs
197+ build-depends:
198+ lib-prelude
199+
200+benchmark serial-O-n-heap
201+ import: bench-options
202+ type: exitcode-stdio-1.0
203+ ghc-options: -with-rtsopts "-T -K36K -M128M"
204+ hs-source-dirs: exe
205+ main-is: Prelude/Serial/O_n_Heap.hs
206+ build-depends:
207+ lib-prelude
208+
209+benchmark serial-O-n-stack
210+ import: bench-options
211+ type: exitcode-stdio-1.0
212+ ghc-options: -with-rtsopts "-T -K1M -M16M"
213+ hs-source-dirs: exe
214+ main-is: Prelude/Serial/O_n_Stack.hs
215+ build-depends:
216+ lib-prelude
217+
218+benchmark serial-O-n-space
219+ import: bench-options
220+ type: exitcode-stdio-1.0
221+ ghc-options: -with-rtsopts "-T -K16M -M64M"
222+ hs-source-dirs: exe
223+ main-is: Prelude/Serial/O_n_Space.hs
224+ build-depends:
225+ lib-prelude
226+
227+benchmark nested
228+ import: bench-options
229+ type: exitcode-stdio-1.0
230+ ghc-options: -with-rtsopts "-T -K256K -M16M"
231+ hs-source-dirs: .
232+ main-is: Nested.hs
233+ other-modules: NestedOps
234+
235+benchmark nested-unfold
236+ import: bench-options
237+ type: exitcode-stdio-1.0
238+ hs-source-dirs: .
239+ ghc-options: -with-rtsopts "-T -K64K -M16M"
240+ main-is: NestedUnfold.hs
241+ other-modules: NestedUnfoldOps
242+
243+benchmark base
244+ import: bench-options
245+ type: exitcode-stdio-1.0
246+ hs-source-dirs: .
247+ if flag(dev)
248+ cpp-options: -DDEVBUILD
249+ ghc-options: -with-rtsopts "-T -K2M -M16M"
250+ else
251+ ghc-options: -with-rtsopts "-T -K128K -M16M"
252+ main-is: BaseStreams.hs
253+ other-modules: StreamDOps
254+ , StreamKOps
255+ , StreamDKOps
256+
257+executable nano-bench
258+ import: bench-options
259+ hs-source-dirs: .
260+ main-is: NanoBenchmarks.hs
261+ if flag(dev)
262+ buildable: True
263+ else
264+ buildable: False
265+
266+-------------------------------------------------------------------------------
267+-- Concurrent Streams
268+-------------------------------------------------------------------------------
269+
270+benchmark linear-async
271+ import: bench-options-threaded
272+ type: exitcode-stdio-1.0
273+ hs-source-dirs: .
274+ ghc-options: -with-rtsopts "-T -N2 -K64K -M16M"
275+ main-is: LinearAsync.hs
276+ build-depends:
277+ lib-prelude
278+
279+benchmark nested-concurrent
280+ import: bench-options-threaded
281+ type: exitcode-stdio-1.0
282+ hs-source-dirs: .
283+ -- XXX this can be lowered once we split out the finite benchmarks
284+ ghc-options: -with-rtsopts "-T -N2 -K256K -M128M"
285+ main-is: NestedConcurrent.hs
286+ other-modules: NestedOps
287+
288+benchmark parallel
289+ import: bench-options-threaded
290+ type: exitcode-stdio-1.0
291+ hs-source-dirs: .
292+ ghc-options: -with-rtsopts "-T -N2 -K128K -M256M"
293+ main-is: Parallel.hs
294+ other-modules: NestedOps
295+ build-depends:
296+ lib-prelude
297+
298+benchmark concurrent
299+ import: bench-options-threaded
300+ type: exitcode-stdio-1.0
301+ hs-source-dirs: .
302+ main-is: Concurrent.hs
303+ ghc-options: -with-rtsopts "-T -N2 -K256K -M384M"
304+
305+benchmark adaptive
306+ import: bench-options-threaded
307+ type: exitcode-stdio-1.0
308+ hs-source-dirs: .
309+ main-is: Adaptive.hs
310+ default-language: Haskell2010
311+ build-depends:
312+ streamly
313+ , base >= 4.8 && < 5
314+ , gauge >= 0.2.4 && < 0.3
315+ , random >= 1.0 && < 2.0
316+
317+benchmark linear-rate
318+ import: bench-options-threaded
319+ type: exitcode-stdio-1.0
320+ hs-source-dirs: .
321+ main-is: LinearRate.hs
322+ build-depends:
323+ lib-prelude
324+
325+-------------------------------------------------------------------------------
326+-- Array Benchmarks
327+-------------------------------------------------------------------------------
328+
329+benchmark unpinned-array
330+ import: bench-options
331+ type: exitcode-stdio-1.0
332+ hs-source-dirs: .
333+ ghc-options: -with-rtsopts "-T -K1K -M128M"
334+ main-is: Streamly/Benchmark/Data/Array.hs
335+ other-modules: Streamly.Benchmark.Data.ArrayOps
336+
337+benchmark prim-array
338+ import: bench-options
339+ type: exitcode-stdio-1.0
340+ hs-source-dirs: .
341+ ghc-options: -with-rtsopts "-T -K64K -M32M"
342+ main-is: Streamly/Benchmark/Data/Prim/Array.hs
343+ other-modules: Streamly.Benchmark.Data.Prim.ArrayOps
344+
345+benchmark small-array
346+ import: bench-options
347+ type: exitcode-stdio-1.0
348+ hs-source-dirs: .
349+ ghc-options: -with-rtsopts "-T -K128K -M16M"
350+ main-is: Streamly/Benchmark/Data/SmallArray.hs
351+ other-modules: Streamly.Benchmark.Data.SmallArrayOps
352+
353+benchmark array
354+ import: bench-options
355+ type: exitcode-stdio-1.0
356+ hs-source-dirs: .
357+ ghc-options: -with-rtsopts "-T -K64K -M128M"
358+ main-is: Array.hs
359+ other-modules: ArrayOps
360+
361+-------------------------------------------------------------------------------
362+-- FileIO Benchmarks
363+-------------------------------------------------------------------------------
364+
365+benchmark fileio
366+ import: bench-options
367+ type: exitcode-stdio-1.0
368+ hs-source-dirs: .
369+ main-is: FileIO.hs
370+ other-modules: Streamly.Benchmark.FileIO.Array
371+ , Streamly.Benchmark.FileIO.Stream
372+ build-depends:
373+ typed-process >= 0.2.3 && < 0.3
374+
375+-------------------------------------------------------------------------------
376+-- benchmark comparison and presentation
377+-------------------------------------------------------------------------------
378+
379+executable chart
380+ default-language: Haskell2010
381+ ghc-options: -Wall
382+ hs-source-dirs: .
383+ main-is: Chart.hs
384+ if flag(dev) && !flag(no-charts) && !impl(ghcjs)
385+ buildable: True
386+ build-Depends:
387+ base >= 4.8 && < 5
388+ , bench-show >= 0.3 && < 0.4
389+ , split
390+ , transformers >= 0.4 && < 0.6
391+ else
392+ buildable: False
streamly.cabal+8−341
@@ -119,6 +119,14 @@ extra-source-files:
119119 configure.ac
120120 configure
121121 src/Streamly/Internal/Data/Time/config.h.in
122+ benchmark/streamly-benchmarks.cabal
123+ benchmark/README.md
124+ benchmark/*.hs
125+ benchmark/Streamly/Benchmark/Data/*.hs
126+ benchmark/Streamly/Benchmark/Data/Prim/*.hs
127+ benchmark/Streamly/Benchmark/FileIO/*.hs
128+ benchmark/Streamly/Benchmark/*.hs
129+ benchmark/exe/Prelude/Serial/*.hs
122130
123131extra-tmp-files:
124132 config.log
@@ -155,11 +163,6 @@ flag has-llvm
155163 manual: True
156164 default: False
157165
158−flag no-charts
159− description: Disable benchmark charts in development build
160− manual: True
161− default: False
162−
163166flag no-fusion
164167 description: Disable rewrite rules for stream fusion
165168 manual: True
@@ -259,28 +262,6 @@ common exe-options
259262 build-depends:
260263 fusion-plugin >= 0.2 && < 0.3
261264
262−-- Some benchmarks are threaded some are not
263−-- XXX dependencies should be separated under bench-depends
264−common bench-options
265− import: compile-options, optimization-options
266− ghc-options: -with-rtsopts "-T -K32K -M16M"
267− if flag(fusion-plugin) && !impl(ghcjs) && !impl(ghc < 8.6)
268− ghc-options: -fplugin Fusion.Plugin
269− build-depends:
270− fusion-plugin >= 0.2 && < 0.3
271− build-depends: mtl >= 2.2 && < 3
272−
273−common bench-options-threaded
274− import: compile-options, optimization-options
275− -- -threaded and -N2 is important because some GC and space leak issues
276− -- trigger only with these options.
277− ghc-options: -threaded -with-rtsopts "-T -N2 -K32K -M16M"
278− if flag(fusion-plugin) && !impl(ghcjs) && !impl(ghc < 8.6)
279− ghc-options: -fplugin Fusion.Plugin
280− build-depends:
281− fusion-plugin >= 0.2 && < 0.3
282− build-depends: mtl >= 2.2 && < 3
283−
284265-------------------------------------------------------------------------------
285266-- Library
286267-------------------------------------------------------------------------------
@@ -656,320 +637,6 @@ test-suite version-bounds
656637 , ghc
657638 , base >= 4.8 && < 5
658639
659−-------------------------------------------------------------------------------
660−-- Benchmarks
661−-------------------------------------------------------------------------------
662−
663−-- For linear, linear-async, linear-rate, nested and nested-concurrent
664−-- you can pass the number of elements in the stream using the
665−-- --stream-size option:
666−-- $ cabal run linear -- --stream-size 1000000
667−
668−benchmark linear
669− import: bench-options
670− type: exitcode-stdio-1.0
671− hs-source-dirs: benchmark
672− -- XXX heap/stack limits can be reduced once we split out the buffered
673− -- benchmarks into a separate suite
674− ghc-options: -with-rtsopts "-T -K4M -M128M"
675− main-is: Linear.hs
676− other-modules: Streamly.Benchmark.Prelude, Common
677− build-depends:
678− streamly
679− , base >= 4.8 && < 5
680− , deepseq >= 1.4.1 && < 1.5
681− , random >= 1.0 && < 2.0
682− , gauge >= 0.2.4 && < 0.3
683− if impl(ghc < 8.0)
684− build-depends:
685− transformers >= 0.4 && < 0.6
686− if flag(inspection)
687− build-depends: template-haskell >= 2.14 && < 2.16
688− , inspection-testing >= 0.4 && < 0.5
689−
690−benchmark nested
691− import: bench-options
692− type: exitcode-stdio-1.0
693− hs-source-dirs: benchmark
694− ghc-options: -with-rtsopts "-T -K256K -M16M"
695− main-is: Nested.hs
696− other-modules: NestedOps, Common
697− build-depends:
698− streamly
699− , base >= 4.8 && < 5
700− , deepseq >= 1.4.1 && < 1.5
701− , random >= 1.0 && < 2.0
702− , gauge >= 0.2.4 && < 0.3
703− if impl(ghc < 8.0)
704− build-depends:
705− transformers >= 0.4 && < 0.6
706−
707−benchmark nested-unfold
708− import: bench-options
709− type: exitcode-stdio-1.0
710− hs-source-dirs: benchmark
711− ghc-options: -with-rtsopts "-T -K64K -M16M"
712− main-is: NestedUnfold.hs
713− other-modules: NestedUnfoldOps, Common
714− build-depends:
715− streamly
716− , base >= 4.8 && < 5
717− , deepseq >= 1.4.1 && < 1.5
718− , random >= 1.0 && < 2.0
719− , gauge >= 0.2.4 && < 0.3
720− if impl(ghc < 8.0)
721− build-depends:
722− transformers >= 0.4 && < 0.6
723−
724−benchmark unpinned-array
725− import: bench-options
726− type: exitcode-stdio-1.0
727− hs-source-dirs: benchmark
728− ghc-options: -with-rtsopts "-T -K1K -M128M"
729− main-is: Streamly/Benchmark/Data/Array.hs
730− other-modules: Streamly.Benchmark.Data.ArrayOps
731− build-depends:
732− streamly
733− , base >= 4.8 && < 5
734− , deepseq >= 1.4.1 && < 1.5
735− , random >= 1.0 && < 2.0
736− , gauge >= 0.2.4 && < 0.3
737− if impl(ghc < 8.0)
738− build-depends:
739− transformers >= 0.4 && < 0.6
740−
741−benchmark prim-array
742− import: bench-options
743− type: exitcode-stdio-1.0
744− hs-source-dirs: benchmark
745− ghc-options: -with-rtsopts "-T -K64K -M32M"
746− main-is: Streamly/Benchmark/Data/Prim/Array.hs
747− other-modules: Streamly.Benchmark.Data.Prim.ArrayOps
748− build-depends:
749− streamly
750− , base >= 4.8 && < 5
751− , deepseq >= 1.4.1 && < 1.5
752− , random >= 1.0 && < 2.0
753− , gauge >= 0.2.4 && < 0.3
754− if impl(ghc < 8.0)
755− build-depends:
756− transformers >= 0.4 && < 0.6
757−
758−benchmark small-array
759− import: bench-options
760− type: exitcode-stdio-1.0
761− hs-source-dirs: benchmark
762− ghc-options: -with-rtsopts "-T -K128K -M16M"
763− main-is: Streamly/Benchmark/Data/SmallArray.hs
764− other-modules: Streamly.Benchmark.Data.SmallArrayOps
765− build-depends:
766− streamly
767− , base >= 4.8 && < 5
768− , deepseq >= 1.4.1 && < 1.5
769− , random >= 1.0 && < 2.0
770− , gauge >= 0.2.4 && < 0.3
771− if impl(ghc < 8.0)
772− build-depends:
773− transformers >= 0.4 && < 0.6
774−
775−benchmark array
776− import: bench-options
777− type: exitcode-stdio-1.0
778− hs-source-dirs: benchmark
779− ghc-options: -with-rtsopts "-T -K64K -M128M"
780− main-is: Array.hs
781− other-modules: ArrayOps
782− build-depends:
783− streamly
784− , base >= 4.8 && < 5
785− , deepseq >= 1.4.1 && < 1.5
786− , random >= 1.0 && < 2.0
787− , gauge >= 0.2.4 && < 0.3
788− if impl(ghc < 8.0)
789− build-depends:
790− transformers >= 0.4 && < 0.6
791−
792−benchmark fileio
793− import: bench-options
794− type: exitcode-stdio-1.0
795− -- A value of 400 works better for some benchmarks, however, it takes
796− -- extraordinary amount of time to compile with that.
797− -- ghc-options: -funfolding-use-threshold=150
798− hs-source-dirs: benchmark
799− main-is: FileIO.hs
800− other-modules: Streamly.Benchmark.FileIO.Array
801− , Streamly.Benchmark.FileIO.Stream
802− build-depends:
803− streamly
804− , base >= 4.8 && < 5
805− , gauge >= 0.2.4 && < 0.3
806− , typed-process >= 0.2.3 && < 0.3
807− , deepseq >= 1.4.1 && < 1.5
808− if flag(inspection)
809− build-depends: template-haskell >= 2.14 && < 2.16
810− , inspection-testing >= 0.4 && < 0.5
811−
812−-------------------------------------------------------------------------------
813−-- Threaded Benchmarks
814−-------------------------------------------------------------------------------
815−
816−benchmark linear-async
817− import: bench-options-threaded
818− type: exitcode-stdio-1.0
819− hs-source-dirs: benchmark
820− ghc-options: -with-rtsopts "-T -N2 -K64K -M16M"
821− main-is: LinearAsync.hs
822− other-modules: Streamly.Benchmark.Prelude, Common
823− build-depends:
824− streamly
825− , base >= 4.8 && < 5
826− , deepseq >= 1.4.1 && < 1.5
827− , random >= 1.0 && < 2.0
828− , gauge >= 0.2.4 && < 0.3
829− if impl(ghc < 8.0)
830− build-depends:
831− transformers >= 0.4 && < 0.6
832− if flag(inspection)
833− build-depends: template-haskell >= 2.14 && < 2.16
834− , inspection-testing >= 0.4 && < 0.5
835−
836−benchmark nested-concurrent
837− import: bench-options-threaded
838− type: exitcode-stdio-1.0
839− hs-source-dirs: benchmark
840− -- XXX this can be lowered once we split out the finite benchmarks
841− ghc-options: -with-rtsopts "-T -N2 -K256K -M128M"
842− main-is: NestedConcurrent.hs
843− other-modules: NestedOps, Common
844− build-depends:
845− streamly
846− , base >= 4.8 && < 5
847− , deepseq >= 1.4.1 && < 1.5
848− , random >= 1.0 && < 2.0
849− , gauge >= 0.2.4 && < 0.3
850− if impl(ghc < 8.0)
851− build-depends:
852− transformers >= 0.4 && < 0.6
853−
854−benchmark parallel
855− import: bench-options-threaded
856− type: exitcode-stdio-1.0
857− hs-source-dirs: benchmark
858− ghc-options: -with-rtsopts "-T -N2 -K128K -M256M"
859− main-is: Parallel.hs
860− other-modules: Streamly.Benchmark.Prelude, NestedOps, Common
861− build-depends:
862− streamly
863− , base >= 4.8 && < 5
864− , deepseq >= 1.4.1 && < 1.5
865− , random >= 1.0 && < 2.0
866− , gauge >= 0.2.4 && < 0.3
867− if impl(ghc < 8.0)
868− build-depends:
869− transformers >= 0.4 && < 0.6
870− if flag(inspection)
871− build-depends: template-haskell >= 2.14 && < 2.16
872− , inspection-testing >= 0.4 && < 0.5
873−
874−benchmark linear-rate
875− import: bench-options-threaded
876− type: exitcode-stdio-1.0
877− hs-source-dirs: benchmark
878− main-is: LinearRate.hs
879− other-modules: Streamly.Benchmark.Prelude, Common
880− build-depends:
881− streamly
882− , base >= 4.8 && < 5
883− , deepseq >= 1.4.1 && < 1.5
884− , random >= 1.0 && < 2.0
885− , gauge >= 0.2.4 && < 0.3
886− if impl(ghc < 8.0)
887− build-depends:
888− transformers >= 0.4 && < 0.6
889− if flag(inspection)
890− build-depends: template-haskell >= 2.14 && < 2.16
891− , inspection-testing >= 0.4 && < 0.5
892−
893−benchmark concurrent
894− import: bench-options-threaded
895− type: exitcode-stdio-1.0
896− hs-source-dirs: benchmark
897− main-is: Concurrent.hs
898− ghc-options: -with-rtsopts "-T -N2 -K256K -M384M"
899− build-depends:
900− streamly
901− , base >= 4.8 && < 5
902− , gauge >= 0.2.4 && < 0.3
903−
904−-------------------------------------------------------------------------------
905−-- Internal benchmarks
906−-------------------------------------------------------------------------------
907−
908−benchmark base
909− import: bench-options
910− type: exitcode-stdio-1.0
911− hs-source-dirs: benchmark
912− if flag(dev)
913− cpp-options: -DDEVBUILD
914− ghc-options: -with-rtsopts "-T -K2M -M16M"
915− else
916− ghc-options: -with-rtsopts "-T -K128K -M16M"
917− main-is: BaseStreams.hs
918− other-modules: StreamDOps
919− , StreamKOps
920− , StreamDKOps
921−
922− build-depends:
923− streamly
924− , base >= 4.8 && < 5
925− , deepseq >= 1.4.1 && < 1.5
926− , random >= 1.0 && < 2.0
927− , gauge >= 0.2.4 && < 0.3
928−
929−executable nano-bench
930− import: bench-options
931− hs-source-dirs: benchmark
932− main-is: NanoBenchmarks.hs
933− if flag(dev)
934− buildable: True
935− build-depends:
936− streamly
937− , base >= 4.8 && < 5
938− , gauge >= 0.2.4 && < 0.3
939− , random >= 1.0 && < 2.0
940− else
941− buildable: False
942−
943−benchmark adaptive
944− import: bench-options-threaded
945− type: exitcode-stdio-1.0
946− hs-source-dirs: benchmark
947− main-is: Adaptive.hs
948− default-language: Haskell2010
949− build-depends:
950− streamly
951− , base >= 4.8 && < 5
952− , gauge >= 0.2.4 && < 0.3
953− , random >= 1.0 && < 2.0
954− if impl(ghc < 8.0)
955− build-depends:
956− transformers >= 0.4 && < 0.6
957−
958−executable chart
959− default-language: Haskell2010
960− ghc-options: -Wall
961− hs-source-dirs: benchmark
962− main-is: Chart.hs
963− if flag(dev) && !flag(no-charts) && !impl(ghcjs)
964− buildable: True
965− build-Depends:
966− base >= 4.8 && < 5
967− , bench-show >= 0.3 && < 0.4
968− , split
969− , transformers >= 0.4 && < 0.6
970− else
971− buildable: False
972−
973640-------------------------------------------------------------------------------
974641-- Examples
975642-------------------------------------------------------------------------------