2-- Module : Streamly.Benchmark.Data.Fold
3-- Copyright : (c) 2018 Composewell
6-- Maintainer : streamly@composewell.com
8{-# LANGUAGE FlexibleContexts #-}
10module Main (main) where
12import Control.DeepSeq (NFData(..))
13import Data.Map.Strict (Map)
14import Data.Monoid (Last(..), Sum(..))
15import System.Random (randomRIO)
17import Streamly.Prelude (SerialT)
18import Streamly.Internal.Data.Fold (Fold(..))
20import qualified Data.Map.Strict as Map
21import qualified Streamly.Internal.Data.Fold as FL
22import qualified Streamly.Internal.Data.Pipe as Pipe
23import qualified Streamly.Internal.Data.Sink as Sink
24import qualified Streamly.Internal.Data.Stream.IsStream as IP
25import qualified Streamly.Prelude as S
28import Streamly.Benchmark.Common
29import Prelude hiding (all, any, take, unzip, sequence_)
31-- We need a monadic bind here to make sure that the function f does not get
32-- completely optimized out by the compiler in some cases.
34{-# INLINE sourceUnfoldrM #-}
35sourceUnfoldrM :: (S.IsStream t, S.MonadAsync m) => Int -> Int -> t m Int
36sourceUnfoldrM value n = S.unfoldrM step n
41 else return (Just (cnt, cnt + 1))
44source :: (S.MonadAsync m, S.IsStream t) => Int -> Int -> t m Int
45source = sourceUnfoldrM
47-- | Takes a fold method, and uses it with a default source.
48{-# INLINE benchIOSink #-}
50 :: (S.IsStream t, NFData b)
51 => Int -> String -> (t IO Int -> IO b) -> Benchmark
52benchIOSink value name f = bench name $ nfIO $ randomRIO (1,1) >>= f . source value
54-------------------------------------------------------------------------------
56-------------------------------------------------------------------------------
59any :: (Monad m, Ord a) => a -> SerialT m a -> m Bool
60any value = IP.fold (FL.any (> value))
63all :: (Monad m, Ord a) => a -> SerialT m a -> m Bool
64all value = IP.fold (FL.all (<= value))
67take :: Monad m => Int -> SerialT m a -> m ()
68take value = IP.fold (FL.take value FL.drain)
70{-# INLINE sequence_ #-}
71sequence_ :: Monad m => Int -> Fold m a ()
73 foldr f (FL.fromPure ()) (Prelude.replicate value (FL.take 1 FL.drain))
78 f m k = FL.concatMap (const k) m
80-------------------------------------------------------------------------------
81-- Splitting by serial application
82-------------------------------------------------------------------------------
84{-# INLINE takeEndBy_ #-}
85takeEndBy_ :: Monad m => Int -> SerialT m Int -> m ()
86takeEndBy_ value = IP.fold (FL.takeEndBy_ (>= value) FL.drain)
89many :: Monad m => SerialT m Int -> m ()
90many = IP.fold (FL.many (FL.take 1 FL.drain) FL.drain)
92{-# INLINE splitAllAny #-}
93splitAllAny :: Monad m => Int -> SerialT m Int -> m (Bool, Bool)
97 (FL.all (<= (value `div` 2)))
104 => Int -> SerialT m Int -> m ()
108 (FL.all (<= (value `div` 2)))
113-------------------------------------------------------------------------------
114-- Distributing by parallel application
115-------------------------------------------------------------------------------
117{-# INLINE teeSumLength #-}
118teeSumLength :: Monad m => SerialT m Int -> m (Int, Int)
119teeSumLength = IP.fold (FL.teeWith (,) FL.sum FL.length)
121{-# INLINE teeAllAny #-}
122teeAllAny :: (Monad m, Ord a) => a -> SerialT m a -> m (Bool, Bool)
123teeAllAny value = IP.fold (FL.teeWith (,) all_ any_)
127 all_ = FL.all (<= value)
128 any_ = FL.any (> value)
130{-# INLINE distribute #-}
131distribute :: Monad m => SerialT m Int -> m [Int]
132distribute = IP.fold (FL.distribute [FL.sum, FL.length])
134-------------------------------------------------------------------------------
136-------------------------------------------------------------------------------
138{-# INLINE partition #-}
139partition :: Monad m => SerialT m Int -> m (Int, Int)
145 in IP.fold $ FL.map f (FL.partition FL.sum FL.length)
147{-# INLINE demuxWith #-}
151 -> Map k (Fold m a' b)
154demuxWith f mp = S.fold (FL.demuxWith f mp)
156{-# INLINE demuxDefaultWith #-}
158 (Monad m, Ord k, Num b)
160 -> Map k (Fold m b b)
163demuxDefaultWith f mp = S.fold (FL.demuxDefaultWith f mp (FL.map snd FL.sum))
165{-# INLINE classifyWith #-}
167 (Monad m, Ord k, Num a) => (a -> k) -> SerialT m a -> m (Map k a)
168classifyWith f = S.fold (FL.classifyWith f FL.sum)
170-------------------------------------------------------------------------------
172-------------------------------------------------------------------------------
175unzip :: Monad m => SerialT m Int -> m (Int, Int)
176unzip = IP.fold $ FL.map (\a -> (a, a)) (FL.unzip FL.sum FL.length)
178-------------------------------------------------------------------------------
180-------------------------------------------------------------------------------
183moduleName = "Data.Fold"
185o_1_space_serial_elimination :: Int -> [Benchmark]
186o_1_space_serial_elimination value =
187 [ bgroup "elimination"
188 [ benchIOSink value "drain" (S.fold FL.drain)
189 , benchIOSink value "drainBy" (S.fold (FL.drainBy return))
190 , benchIOSink value "drainN" (S.fold (FL.drainN value))
191 , benchIOSink value "sink" (S.fold $ Sink.toFold Sink.drain)
192 , benchIOSink value "last" (S.fold FL.last)
193 , benchIOSink value "length" (S.fold FL.length)
194 , benchIOSink value "sum" (S.fold FL.sum)
195 , benchIOSink value "sum (foldMap)" (S.fold (FL.foldMap Sum))
196 , benchIOSink value "product" (S.fold FL.product)
197 , benchIOSink value "maximumBy" (S.fold (FL.maximumBy compare))
198 , benchIOSink value "maximum" (S.fold FL.maximum)
199 , benchIOSink value "minimumBy" (S.fold (FL.minimumBy compare))
200 , benchIOSink value "minimum" (S.fold FL.minimum)
204 (S.fold FL.mean . S.map (fromIntegral :: Int -> Double))
208 (S.fold FL.variance . S.map (fromIntegral :: Int -> Double))
212 (S.fold FL.stdDev . S.map (fromIntegral :: Int -> Double))
216 (S.fold FL.mconcat . S.map (Last . Just))
220 (S.fold (FL.foldMap (Last . Just)))
224 (S.fold (FL.foldMapM (return . Last . Just)))
225 , benchIOSink value "index" (S.fold (FL.index (value + 1)))
226 , benchIOSink value "head" (S.fold FL.head)
227 , benchIOSink value "find" (S.fold (FL.find (== (value + 1))))
231 (S.fold (FL.map (\a -> (a, a)) (FL.lookup (value + 1))))
235 (S.fold (FL.findIndex (== (value + 1))))
239 (S.fold (FL.elemIndex (value + 1)))
240 , benchIOSink value "null" (S.fold FL.null)
241 , benchIOSink value "elem" (S.fold (FL.elem (value + 1)))
242 , benchIOSink value "notElem" (S.fold (FL.notElem (value + 1)))
243 , benchIOSink value "all" $ all value
244 , benchIOSink value "any" $ any value
245 , benchIOSink value "take" $ take value
246 , benchIOSink value "takeEndBy_" $ takeEndBy_ value
247 , benchIOSink value "and" (S.fold FL.and . S.map (<= (value + 1)))
248 , benchIOSink value "or" (S.fold FL.or . S.map (> (value + 1)))
252o_1_space_serial_transformation :: Int -> [Benchmark]
253o_1_space_serial_transformation value =
254 [ bgroup "transformation"
255 [ benchIOSink value "map" (S.fold (FL.map (+ 1) FL.drain))
256 , let f x = if even x then Just x else Nothing
257 fld = FL.mapMaybe f FL.drain
258 in benchIOSink value "mapMaybe" (S.fold fld)
262 (S.fold (FL.rmapM id (return <$> FL.drain)))
263 , benchIOSink value "rmapM" (S.fold (FL.rmapM return FL.drain))
269 (Pipe.mapM (\x -> return $ x + 1))
274o_1_space_serial_composition :: Int -> [Benchmark]
275o_1_space_serial_composition value =
278 [ benchIOSink value "serialWith (all, any)" $ splitAllAny value
279 , benchIOSink value "tee (all, any)" $ teeAllAny value
280 , benchIOSink value "many drain (take 1)" many
281 , benchIOSink value "tee (sum, length)" teeSumLength
282 , benchIOSink value "distribute [sum, length]" distribute
283 , benchIOSink value "partition (sum, length)" partition
284 , benchIOSink value "unzip (sum, length)" unzip
285 , benchIOSink value "demuxDefaultWith [sum, length] sum"
286 $ demuxDefaultWith fn mp
287 , benchIOSink value "demuxWith [sum, length]" $ demuxWith fn mp
288 , benchIOSink value "classifyWith sum" $ classifyWith (fst . fn)
294 -- We use three keys 0, 1, and 2. 0 and 1 are mapped and 3 is unmapped.
295 fn x = (x `mod` 3, x)
297 mp = Map.fromList [(0, FL.sum), (1, FL.length)]
299o_n_space_serial :: Int -> [Benchmark]
300o_n_space_serial value =
301 [ benchIOSink value "sequence_/100" $ S.fold (sequence_ (value `div` 100))
304o_n_heap_serial :: Int -> [Benchmark]
305o_n_heap_serial value =
306 [ bgroup "elimination"
307 -- Left folds for building a structure are inherently non-streaming
308 -- as the structure cannot be lazily consumed until fully built.
310 benchIOSink value "toList" (S.fold FL.toList)
311 , benchIOSink value "toListRev" (S.fold FL.toListRev)
312 , benchIOSink value "toStream" (S.fold FL.toStream)
313 , benchIOSink value "toStreamRev" (S.fold FL.toStreamRev)
317-------------------------------------------------------------------------------
319-------------------------------------------------------------------------------
322main = runWithCLIOpts defaultStreamSize allBenchmarks
326 allBenchmarks value =
327 [ bgroup (o_1_space_prefix moduleName) $ concat
328 [ o_1_space_serial_elimination value
329 , o_1_space_serial_transformation value
330 , o_1_space_serial_composition value
332 , bgroup (o_n_space_prefix moduleName) (o_n_space_serial value)
333 , bgroup (o_n_heap_prefix moduleName) (o_n_heap_serial value)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list