on master help sign in
Add benchmarks for streaming operations
a445ef9Harendra Kumar
benchmark/BenchmarkOps.hsnew file+127
@@ -0,0 +1,127 @@
1+-- |
2+-- Module : BenchmarkOps
3+-- Copyright : (c) 2018 Harendra Kumar
4+--
5+-- License : MIT
6+-- Maintainer : harendra.kumar@gmail.com
7+
8+module BenchmarkOps where
9+
10+import Prelude
11+ (Monad, Int, (+), id, ($), (.), return, fmap, even, (>), (<=),
12+ subtract, undefined, Maybe)
13+
14+import qualified Streamly as S
15+import qualified Streamly.Prelude as S
16+
17+value, maxValue :: Int
18+value = 1000000
19+maxValue = value + 1000
20+
21+-------------------------------------------------------------------------------
22+-- Benchmark ops
23+-------------------------------------------------------------------------------
24+
25+{-# INLINE toNull #-}
26+{-# INLINE toList #-}
27+{-# INLINE foldl #-}
28+{-# INLINE last #-}
29+{-# INLINE scan #-}
30+{-# INLINE map #-}
31+{-# INLINE filterEven #-}
32+{-# INLINE mapM #-}
33+{-# INLINE filterAllOut #-}
34+{-# INLINE filterAllIn #-}
35+{-# INLINE takeOne #-}
36+{-# INLINE takeAll #-}
37+{-# INLINE takeWhileTrue #-}
38+{-# INLINE dropAll #-}
39+{-# INLINE dropWhileTrue #-}
40+{-# INLINE zip #-}
41+{-# INLINE concat #-}
42+{-# INLINE composeMapM #-}
43+{-# INLINE composeAllInFilters #-}
44+{-# INLINE composeAllOutFilters #-}
45+{-# INLINE composeMapAllInFilter #-}
46+toNull, scan, map, filterEven, mapM, filterAllOut,
47+ filterAllIn, takeOne, takeAll, takeWhileTrue, dropAll, dropWhileTrue, zip,
48+ concat, composeMapM, composeAllInFilters, composeAllOutFilters,
49+ composeMapAllInFilter
50+ :: Monad m
51+ => Stream m Int -> m ()
52+
53+toList :: Monad m => Stream m Int -> m [Int]
54+foldl :: Monad m => Stream m Int -> m Int
55+last :: Monad m => Stream m Int -> m (Maybe Int)
56+
57+-------------------------------------------------------------------------------
58+-- Stream generation and elimination
59+-------------------------------------------------------------------------------
60+
61+type Stream m a = S.StreamT m a
62+
63+source :: Int -> Stream m Int
64+source n = S.each [n..n+value]
65+
66+{-# INLINE runStream #-}
67+runStream :: Monad m => Stream m a -> m ()
68+runStream = S.runStreamT
69+
70+-------------------------------------------------------------------------------
71+-- Elimination
72+-------------------------------------------------------------------------------
73+
74+toNull = runStream
75+toList = S.toList
76+foldl = S.foldl (+) 0 id
77+last = S.last
78+
79+-------------------------------------------------------------------------------
80+-- Transformation
81+-------------------------------------------------------------------------------
82+
83+{-# INLINE transform #-}
84+transform :: Monad m => Stream m a -> m ()
85+transform = runStream
86+
87+scan = transform . S.scan (+) 0 id
88+map = transform . fmap (+1)
89+mapM = transform . S.mapM return
90+filterEven = transform . S.filter even
91+filterAllOut = transform . S.filter (> maxValue)
92+filterAllIn = transform . S.filter (<= maxValue)
93+takeOne = transform . S.take 1
94+takeAll = transform . S.take maxValue
95+takeWhileTrue = transform . S.takeWhile (<= maxValue)
96+dropAll = transform . S.drop maxValue
97+dropWhileTrue = transform . S.dropWhile (<= maxValue)
98+
99+-------------------------------------------------------------------------------
100+-- Zipping and concat
101+-------------------------------------------------------------------------------
102+
103+zip src = transform $ (S.zipWith (,) src src)
104+concat _n = return ()
105+
106+-------------------------------------------------------------------------------
107+-- Composition
108+-------------------------------------------------------------------------------
109+
110+{-# INLINE compose #-}
111+compose :: Monad m => (Stream m Int -> Stream m Int) -> Stream m Int -> m ()
112+compose f = transform . f . f . f . f
113+
114+composeMapM = compose (S.mapM return)
115+composeAllInFilters = compose (S.filter (<= maxValue))
116+composeAllOutFilters = compose (S.filter (> maxValue))
117+composeMapAllInFilter = compose (S.filter (<= maxValue) . fmap (subtract 1))
118+
119+composeScaling :: Monad m => Int -> Stream m Int -> m ()
120+composeScaling m =
121+ case m of
122+ 1 -> transform . f
123+ 2 -> transform . f . f
124+ 3 -> transform . f . f . f
125+ 4 -> transform . f . f . f . f
126+ _ -> undefined
127+ where f = S.filter (<= maxValue)
benchmark/Benchmarks.hsnew file+61
@@ -0,0 +1,61 @@
1+-- |
2+-- Module : Main
3+-- Copyright : (c) 2018 Harendra Kumar
4+--
5+-- License : BSD3
6+-- Maintainer : harendra.kumar@gmail.com
7+
8+import Control.DeepSeq (NFData)
9+import Data.Functor.Identity (Identity, runIdentity)
10+import System.Random (randomRIO)
11+import qualified BenchmarkOps as Ops
12+
13+import Gauge
14+
15+-- We need a monadic bind here to make sure that the function f does not get
16+-- completely optimized out by the compiler in some cases.
17+benchIO :: (NFData b) => String -> (Ops.Stream m Int -> IO b) -> Benchmark
18+benchIO name f = bench name $ nfIO $ randomRIO (1,1000) >>= f . Ops.source
19+
20+_benchId :: (NFData b) => String -> (Ops.Stream m Int -> Identity b) -> Benchmark
21+_benchId name f = bench name $ nf (runIdentity . f) (Ops.source 10)
22+
23+main :: IO ()
24+main = do
25+ defaultMain
26+ [ bgroup "elimination"
27+ [ benchIO "toNull" Ops.toNull
28+ , benchIO "toList" Ops.toList
29+ , benchIO "fold" Ops.foldl
30+ , benchIO "last" Ops.last
31+ ]
32+ , bgroup "transformation"
33+ [ benchIO "scan" Ops.scan
34+ , benchIO "map" Ops.map
35+ , benchIO "mapM" Ops.mapM
36+ , benchIO "concat" Ops.concat
37+ ]
38+ , bgroup "filtering"
39+ [ benchIO "filter-even" Ops.filterEven
40+ , benchIO "filter-all-out" Ops.filterAllOut
41+ , benchIO "filter-all-in" Ops.filterAllIn
42+ , benchIO "take-all" Ops.takeAll
43+ , benchIO "takeWhile-true" Ops.takeWhileTrue
44+ , benchIO "drop-all" Ops.dropAll
45+ , benchIO "dropWhile-true" Ops.dropWhileTrue
46+ ]
47+ , benchIO "zip" Ops.zip
48+ , bgroup "compose"
49+ [ benchIO "mapM" Ops.composeMapM
50+ , benchIO "map-with-all-in-filter" Ops.composeMapAllInFilter
51+ , benchIO "all-in-filters" Ops.composeAllInFilters
52+ , benchIO "all-out-filters" Ops.composeAllOutFilters
53+ ]
54+ , bgroup "compose-scaling"
55+ -- Scaling with same operation in sequence
56+ [ benchIO "1" $ Ops.composeScaling 1
57+ , benchIO "2" $ Ops.composeScaling 2
58+ , benchIO "3" $ Ops.composeScaling 3
59+ , benchIO "4" $ Ops.composeScaling 4
60+ ]
61+ ]
streamly.cabal+27
@@ -193,6 +193,33 @@ benchmark bench
193 , transient >= 0.4 && < 0.6
193 , transient >= 0.4 && < 0.6
194 default-language: Haskell2010
194 default-language: Haskell2010
195
195
196+benchmark benchOps
197+ type: exitcode-stdio-1.0
198+ hs-source-dirs: benchmark
199+ main-is: Benchmarks.hs
200+ other-modules: BenchmarkOps
201+ default-language: Haskell2010
202+ ghc-options: -O2 -Wall
203+ if flag(dev)
204+ ghc-options: -Wmissed-specialisations
205+ -Wall-missed-specialisations
206+ -fno-ignore-asserts
207+ if impl(ghc >= 8.0)
208+ ghc-options: -Wcompat
209+ -Wunrecognised-warning-flags
210+ -Widentities
211+ -Wincomplete-record-updates
212+ -Wincomplete-uni-patterns
213+ -Wredundant-constraints
214+ -Wnoncanonical-monad-instances
215+ -Wnoncanonical-monadfail-instances
216+ build-depends:
217+ streamly
218+ , base >= 4.8 && < 5
219+ , deepseq >= 1.4.0 && < 1.5
220+ , random >= 1.0 && < 2.0
221+ , gauge >= 0.2.1 && < 0.3
222+
196-------------------------------------------------------------------------------
223-------------------------------------------------------------------------------
197-- Examples
224-- Examples
198-------------------------------------------------------------------------------
225-------------------------------------------------------------------------------