on cd67584 help sign in
294 lines 9.8 kB history raw
1-- |
2-- Module : Streamly.Benchmark.Data.Array.Unboxed.Mut
3-- Copyright : (c) 2021 Composewell Technologies
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
7{-# LANGUAGE CPP #-}
8{-# LANGUAGE FlexibleContexts #-}
9{-# LANGUAGE ScopedTypeVariables #-}
11#ifdef __HADDOCK_VERSION__
12#undef INSPECTION
13#endif
15#ifdef INSPECTION
16{-# LANGUAGE TemplateHaskell #-}
17{-# OPTIONS_GHC -fplugin Test.Inspection.Plugin #-}
18#endif
20import Control.DeepSeq (NFData(..))
21import Control.Monad.IO.Class (MonadIO)
22import Data.Functor ((<&>))
23import System.Random (randomRIO)
24import Prelude
25 ( IO
26 , Int
27 , Integral(..)
28 , Maybe(..)
29 , Monad(..)
30 , Num(..)
31 , Ord(..)
32 , String
33 , ($)
34 , (.)
35 , (||)
36 , concat
37 , const
38 , fmap
39 , id
40 , undefined
41 )
43import qualified Streamly.Internal.Data.Array.Unboxed as Array
44import qualified Streamly.Internal.Data.Array.Unboxed.Mut as MArray
45import qualified Streamly.Internal.Data.Fold as Fold
46import qualified Streamly.Internal.Data.Stream as Stream
47import qualified Streamly.Prelude as IsStream (scanl1')
49import Gauge
50import Streamly.Benchmark.Common hiding (benchPureSrc)
52type Stream = MArray.Array
54-------------------------------------------------------------------------------
55-- Benchmark helpers
56-------------------------------------------------------------------------------
58{-# INLINE benchIO #-}
59benchIO :: NFData b => String -> (Int -> IO a) -> (a -> b) -> Benchmark
60benchIO name src f = bench name $ nfIO $
61 (randomRIO (1,1) >>= src) <&> f
63{-# INLINE benchPureSink #-}
64benchPureSink :: NFData b => Int -> String -> (Stream Int -> b) -> Benchmark
65benchPureSink value name = benchIO name (sourceIntFromTo value)
67{-# INLINE benchIO' #-}
68benchIO' :: NFData b => String -> (Int -> IO a) -> (a -> IO b) -> Benchmark
69benchIO' name src f = bench name $ nfIO $
70 randomRIO (1,1) >>= src >>= f
72{-# INLINE benchIOSink #-}
73benchIOSink :: NFData b => Int -> String -> (Stream Int -> IO b) -> Benchmark
74benchIOSink value name = benchIO' name (sourceIntFromTo value)
76-- Drain a source that generates an array in the IO monad
77{-# INLINE benchIOSrc #-}
78benchIOSrc :: String -> (Int -> IO (Stream a)) -> Benchmark
79benchIOSrc name src = benchIO name src id
81drain :: Monad m => Stream.Stream m a -> m ()
82drain = Stream.fold Fold.drain
84-------------------------------------------------------------------------------
85-- Bench Ops
86-------------------------------------------------------------------------------
88{-# INLINE sourceUnfoldr #-}
89sourceUnfoldr :: MonadIO m => Int -> Int -> m (Stream Int)
90sourceUnfoldr value n =
91 Stream.fold (MArray.writeN value) $ Stream.unfoldr step n
93 where
95 step cnt =
96 if cnt > n + value
97 then Nothing
98 else Just (cnt, cnt + 1)
100{-# INLINE sourceIntFromTo #-}
101sourceIntFromTo :: MonadIO m => Int -> Int -> m (Stream Int)
102sourceIntFromTo value n =
103 Stream.fold (MArray.writeN value) $ Stream.enumerateFromTo n (n + value)
105{-# INLINE sourceFromList #-}
106sourceFromList :: MonadIO m => Int -> Int -> m (Stream Int)
107sourceFromList value n =
108 Stream.fold (MArray.writeN value) $ Stream.fromList [n .. n + value]
110{-# INLINE sourceIntFromToFromList #-}
111sourceIntFromToFromList :: MonadIO m => Int -> Int -> m (Stream Int)
112sourceIntFromToFromList value n = MArray.fromListN value [n..n + value]
114{-# INLINE sourceIntFromToFromStream #-}
115sourceIntFromToFromStream :: MonadIO m => Int -> Int -> m (Stream Int)
116sourceIntFromToFromStream value n =
117 Stream.fold MArray.write $ Stream.enumerateFromTo n (n + value)
119{-# INLINE sourceUnfoldrM #-}
120sourceUnfoldrM :: Monad m => Int -> Int -> Stream.Stream m Int
121sourceUnfoldrM value n = Stream.unfoldrM step n
122 where
123 step cnt =
124 if cnt > n + value
125 then return Nothing
126 else return (Just (cnt, cnt + 1))
128-------------------------------------------------------------------------------
129-- Transformation
130-------------------------------------------------------------------------------
132{-# INLINE composeN #-}
133composeN :: Monad m
134 => Int -> (Stream Int -> m (Stream Int)) -> Stream Int -> m (Stream Int)
135composeN n f x =
136 case n of
137 1 -> f x
138 2 -> f x >>= f
139 3 -> f x >>= f >>= f
140 4 -> f x >>= f >>= f >>= f
141 _ -> undefined
143{-# INLINE scanl' #-}
144scanl' :: MonadIO m => Int -> Int -> Stream Int -> m (Stream Int)
145scanl' value n = composeN n $ onArray value $ Stream.scan (Fold.foldl' (+) 0)
147{-# INLINE scanl1' #-}
148scanl1' :: MonadIO m => Int -> Int -> Stream Int -> m (Stream Int)
149scanl1' value n = composeN n $ onArray value $ IsStream.scanl1' (+)
151{-# INLINE map #-}
152map :: MonadIO m => Int -> Int -> Stream Int -> m (Stream Int)
153map value n = composeN n $ onArray value $ fmap (+ 1)
155{-# INLINE onArray #-}
156onArray
157 :: MonadIO m => Int -> (Stream.Stream m Int -> Stream.Stream m Int)
158 -> Stream Int
159 -> m (Stream Int)
160onArray value f arr =
161 Stream.fold (MArray.writeN value) $ f $ Stream.unfold MArray.read arr
163-------------------------------------------------------------------------------
164-- Elimination
165-------------------------------------------------------------------------------
167{-# INLINE unfoldReadDrain #-}
168unfoldReadDrain :: MonadIO m => Stream Int -> m ()
169unfoldReadDrain = drain . Stream.unfold MArray.read
171{-# INLINE unfoldReadRevDrain #-}
172unfoldReadRevDrain :: MonadIO m => Stream Int -> m ()
173unfoldReadRevDrain = drain . Stream.unfold MArray.readRev
175{-# INLINE toStreamDRevDrain #-}
176toStreamDRevDrain :: MonadIO m => Stream Int -> m ()
177toStreamDRevDrain =
178 drain . Stream.fromStreamD . MArray.toStreamDRev
180{-# INLINE toStreamDDrain #-}
181toStreamDDrain :: MonadIO m => Stream Int -> m ()
182toStreamDDrain = drain . Stream.fromStreamD . MArray.toStreamD
184{-# INLINE unfoldFold #-}
185unfoldFold :: MonadIO m => Stream Int -> m Int
186unfoldFold = Stream.fold (Fold.foldl' (+) 0) . Stream.unfold MArray.read
188-------------------------------------------------------------------------------
189-- Bench groups
190-------------------------------------------------------------------------------
192o_1_space_generation :: Int -> [Benchmark]
193o_1_space_generation value =
194 [ bgroup
195 "generation"
196 [ benchIOSrc "writeN . intFromTo" (sourceIntFromTo value)
197 , benchIOSrc
198 "fromList . intFromTo"
199 (sourceIntFromToFromList value)
200 , benchIOSrc "writeN . unfoldr" (sourceUnfoldr value)
201 , benchIOSrc "writeN . fromList" (sourceFromList value)
202 , benchIOSrc "write . intFromTo" (sourceIntFromToFromStream value)
203 ]
204 ]
206o_1_space_elimination :: Int -> [Benchmark]
207o_1_space_elimination value =
208 [ bgroup "elimination"
209 [ benchPureSink value "id" id
210 , benchIOSink value "foldl'" unfoldFold
211 , benchIOSink value "read" unfoldReadDrain
212 , benchIOSink value "readRev" unfoldReadRevDrain
213 , benchIOSink value "toStream" toStreamDDrain
214 , benchIOSink value "toStreamRev" toStreamDRevDrain
215 ]
216 ]
218o_n_heap_serial :: Int -> [Benchmark]
219o_n_heap_serial value =
220 [ bgroup "elimination"
221 [
222 -- Converting the stream to an array
223 benchFold "writeN" (Stream.fold (MArray.writeN value))
224 (sourceUnfoldrM value)
225 ]
226 ]
228o_1_space_transformation :: Int -> [Benchmark]
229o_1_space_transformation value =
230 [ bgroup "transformation"
231 [ benchIOSink value "scanl'" (scanl' value 1)
232 , benchIOSink value "scanl1'" (scanl1' value 1)
233 , benchIOSink value "map" (map value 1)
234 ]
235 ]
237o_1_space_transformationX4 :: Int -> [Benchmark]
238o_1_space_transformationX4 value =
239 [ bgroup "transformationX4"
240 [ benchIOSink value "scanl'" (scanl' value 4)
241 , benchIOSink value "scanl1'" (scanl1' value 4)
242 , benchIOSink value "map" (map value 4)
243 ]
244 ]
246o_1_space_serial_marray ::
247 Int -> (MArray.Array Int, Array.Array Int) -> [Benchmark]
248o_1_space_serial_marray value ~(array, indices) =
249 [ benchIO' "partitionBy (< 0)" (const (return array))
250 $ MArray.partitionBy (< 0)
251 , benchIO' "partitionBy (> 0)" (const (return array))
252 $ MArray.partitionBy (> 0)
253 , benchIO' "partitionBy (< value/2)" (const (return array))
254 $ MArray.partitionBy (< (value `div` 2))
255 , benchIO' "partitionBy (> value/2)" (const (return array))
256 $ MArray.partitionBy (> (value `div` 2))
257 , benchIO' "strip (< value/2 || > value/2)" (const (return array))
258 $ MArray.strip (\x -> x < value `div` 2 || x > value `div` 2)
259 , benchIO' "strip (> 0)" (const (return array))
260 $ MArray.strip (> 0)
261 , benchIO' "modifyIndices (+ 1)" (const (return indices))
262 $ Stream.fold (MArray.modifyIndices (\_idx val -> val + 1) array)
263 . Stream.unfold Array.read
264 ]
266-------------------------------------------------------------------------------
267-- Driver
268-------------------------------------------------------------------------------
270moduleName :: String
271moduleName = "Data.Array.Unboxed.Mut"
273main :: IO ()
274main = do
275 runWithCLIOptsEnv defaultStreamSize alloc allBenchmarks
277 where
279 alloc value = do
280 marr <- MArray.fromStream (sourceUnfoldrM value 0 :: Stream.Stream IO Int)
281 indices <- Array.fromStream (sourceUnfoldrM value 0 :: Stream.Stream IO Int)
282 return (marr, indices)
284 allBenchmarks array value =
285 [ bgroup (o_1_space_prefix moduleName)
286 $ concat
287 [ o_1_space_serial_marray value array
288 , o_1_space_generation value
289 , o_1_space_elimination value
290 , o_1_space_transformation value
291 , o_1_space_transformationX4 value
292 ]
293 , bgroup (o_n_heap_prefix moduleName) (o_n_heap_serial value)
294 ]