2-- Module : Streamly.Benchmark.Data.Parser
3-- Copyright : (c) 2020 Composewell Technologies
5-- License : BSD-3-Clause
6-- Maintainer : streamly@composewell.com
9{-# LANGUAGE FlexibleContexts #-}
10{-# LANGUAGE ScopedTypeVariables #-}
17import Control.DeepSeq (NFData(..))
18import Control.Monad.Catch (MonadCatch, MonadThrow, try, SomeException)
19import Data.Foldable (asum)
20import Data.Functor (($>))
21import Data.Maybe (fromMaybe)
22import Data.Monoid (Sum(..))
23import GHC.Magic (inline)
24import GHC.Magic (noinline)
25import System.IO (Handle)
26import System.Random (randomRIO)
28 (any, all, take, sequence, sequence_, sequenceA, takeWhile, dropWhile)
30import qualified Data.Traversable as TR
31import qualified Data.Foldable as F
32import qualified Control.Applicative as AP
33import qualified Streamly.FileSystem.Handle as Handle
34import qualified Streamly.Internal.Data.Array.Unboxed as Array
35import qualified Streamly.Internal.Data.Fold as Fold
36import qualified Streamly.Internal.Data.Parser as PR
37import qualified Streamly.Internal.Data.Stream as Stream
38import qualified Streamly.Internal.Data.Stream.IsStream as IsStream (tail)
39import qualified Streamly.Internal.Data.Producer as Producer
40import qualified Streamly.Internal.Data.Producer.Source as Source
42import Gauge hiding (env)
43import Streamly.Internal.Data.Stream (Stream)
44import Streamly.Benchmark.Common
45import Streamly.Benchmark.Common.Handle
47-------------------------------------------------------------------------------
49-------------------------------------------------------------------------------
51-- XXX these can be moved to the common module
53{-# INLINE sourceUnfoldrM #-}
54sourceUnfoldrM :: Monad m => Int -> Int -> Stream m Int
55sourceUnfoldrM value n = Stream.unfoldrM step n
60 else return (Just (cnt, cnt + 1))
62-- | Generates something like this: { { \{ \{ } }. The stream consists of
63-- three parts, the first part is contains a sequence of `{`. The second part
64-- contains a sequence pf escaped values `\{`. The third part contains a
66{-# INLINE sourceEscapedFrames #-}
72sourceEscapedFrames value n = Stream.unfoldrM step n
78 value1 = value `div` 4
81 | cnt > 4 * value1 = return Nothing
82 | cnt <= value1 = return $ Just (cbOpen, cnt + 1)
83 | cnt > 3 * value1 = return $ Just (cbClose, cnt + 1)
87 $ if (cnt - value1) `mod` 2 == 1
89 else (cbOpen, cnt + 1)
91{-# INLINE benchIOSrc #-}
94 => (Int -> Int -> Stream IO a)
97 -> (Stream IO a -> IO b)
99benchIOSrc src value name f =
100 bench name $ nfIO $ randomRIO (1,1) >>= f . src value
102-- | Takes a fold method, and uses it with a default source.
103{-# INLINE benchIOSink #-}
106 => Int -> String -> (Stream IO Int -> IO b) -> Benchmark
107benchIOSink value name f =
108 bench name $ nfIO $ randomRIO (1,1) >>= f . sourceUnfoldrM value
110-------------------------------------------------------------------------------
112-------------------------------------------------------------------------------
115one :: MonadThrow m => Int -> Stream m Int -> m (Maybe Int)
116one value = Stream.parse p
121 m <- PR.fromFold Fold.one
123 Just i -> if i >= value then pure m else p
124 Nothing -> pure Nothing
126{-# INLINE takeBetween #-}
127takeBetween :: MonadThrow m => Int -> Stream m a -> m ()
128takeBetween value = Stream.parse (PR.takeBetween 0 value Fold.drain)
131takeEQ :: MonadThrow m => Int -> Stream m a -> m ()
132takeEQ value = Stream.parse (PR.takeEQ value Fold.drain)
134{-# INLINE dropWhile #-}
135dropWhile :: MonadThrow m => Int -> Stream m Int -> m ()
136dropWhile value = Stream.parse (PR.dropWhile (<= value))
138{-# INLINE takeStartBy #-}
139takeStartBy :: MonadThrow m => Int -> Stream m Int -> m ()
140takeStartBy value stream = do
141 stream1 <- return . fromMaybe (Stream.fromPure (value + 1)) =<< IsStream.tail stream
142 let stream2 = value `Stream.cons` stream1
143 Stream.parse (PR.takeStartBy (== value) Fold.drain) stream2
145takeFramedByEsc_ :: MonadThrow m => Int -> Stream m Char -> m ()
146takeFramedByEsc_ _ = Stream.parse parser
154 parser = PR.takeFramedByEsc_ isEsc isBegin isEnd Fold.drain
157eqBy :: Int -> Stream IO Int -> IO ()
158eqBy len = Stream.parse (PR.eqBy (==) [1 .. len])
160{-# INLINE matchBy #-}
161matchBy :: Int -> Stream IO Int -> IO ()
162matchBy len = Stream.parse (PR.matchBy (==) (Stream.enumerateFromTo 1 len))
164{-# INLINE takeWhile #-}
165takeWhile :: MonadThrow m => Int -> Stream m Int -> m ()
166takeWhile value = Stream.parse (PR.takeWhile (<= value) Fold.drain)
168takeWhileP :: MonadThrow m => Int -> Stream m Int -> m ()
170 Stream.parse (PR.takeWhileP (<= value) (PR.takeWhile (<= value - 1) Fold.drain))
173takeP :: MonadThrow m => Int -> Stream m a -> m ()
174takeP value = Stream.parse (PR.takeP value (PR.fromFold Fold.drain))
176{-# INLINE groupBy #-}
177groupBy :: MonadThrow m => Stream m Int -> m ()
178groupBy = Stream.parse (PR.groupBy (<=) Fold.drain)
180{-# INLINE groupByRolling #-}
181groupByRolling :: MonadThrow m => Stream m Int -> m ()
182groupByRolling = Stream.parse (PR.groupByRolling (<=) Fold.drain)
185wordBy :: MonadThrow m => Int -> Stream m Int -> m ()
186wordBy value = Stream.parse (PR.wordBy (>= value) Fold.drain)
188{-# INLINE sepByWords #-}
189sepByWords :: MonadThrow m => Int -> Stream m Int -> m ()
190sepByWords _ = Stream.parse (wrds even Fold.drain)
192 wrds p = PR.sepBy (PR.takeWhile (not . p) Fold.drain) (PR.dropWhile p)
194{-# INLINE deintercalate #-}
195deintercalate :: MonadThrow m => Int -> Stream m Int -> m ()
196deintercalate _ = Stream.parse (partition even)
202 (PR.takeWhile (not . p) Fold.sum) (PR.takeWhile p Fold.sum) Fold.drain
204{-# INLINE manyWordByEven #-}
205manyWordByEven :: MonadThrow m => Stream m Int -> m ()
206manyWordByEven = Stream.parse (PR.many (PR.wordBy even Fold.drain) Fold.drain)
209many :: MonadThrow m => Stream m Int -> m Int
210many = Stream.parse (PR.many (PR.satisfy (> 0)) Fold.length)
212{-# INLINE manyAlt #-}
213manyAlt :: MonadThrow m => Stream m Int -> m Int
215 x <- Stream.parse (AP.many (PR.satisfy (> 0))) xs
216 return $ Prelude.length x
219some :: MonadThrow m => Stream m Int -> m Int
220some = Stream.parse (PR.some (PR.satisfy (> 0)) Fold.length)
222{-# INLINE someAlt #-}
223someAlt :: MonadThrow m => Stream m Int -> m Int
225 x <- Stream.parse (AP.some (PR.satisfy (> 0))) xs
226 return $ Prelude.length x
228{-# INLINE manyTill #-}
229manyTill :: MonadThrow m => Int -> Stream m Int -> m Int
231 Stream.parse (PR.manyTill (PR.satisfy (> 0)) (PR.satisfy (== value)) Fold.length)
233{-# INLINE splitAp #-}
234splitAp :: MonadThrow m
235 => Int -> Stream m Int -> m ((), ())
239 <$> PR.dropWhile (<= (value `div` 2))
240 <*> PR.dropWhile (<= value)
243{-# INLINE splitApBefore #-}
244splitApBefore :: MonadThrow m
245 => Int -> Stream m Int -> m ()
248 ( PR.dropWhile (<= (value `div` 2))
249 *> PR.dropWhile (<= value)
252{-# INLINE splitApAfter #-}
253splitApAfter :: MonadThrow m
254 => Int -> Stream m Int -> m ()
257 ( PR.dropWhile (<= (value `div` 2))
258 <* PR.dropWhile (<= value)
261{-# INLINE serialWith #-}
262serialWith :: MonadThrow m
263 => Int -> Stream m Int -> m ((), ())
267 (PR.dropWhile (<= (value `div` 2)))
268 (PR.dropWhile (<= value))
272split_ :: MonadThrow m
273 => Int -> Stream m Int -> m ()
277 (PR.dropWhile (<= (value `div` 2)))
278 (PR.dropWhile (<= value))
281{-# INLINE takeEndBy_ #-}
282takeEndBy_ :: MonadThrow m
283 => Int -> Stream m Int -> m()
284takeEndBy_ value = Stream.parse (PR.takeEndBy_ (>= value) (PR.fromFold Fold.drain))
287{-# INLINE teeAllAny #-}
288teeAllAny :: MonadThrow m
289 => Int -> Stream m Int -> m ((), ())
293 (PR.dropWhile (<= value))
294 (PR.dropWhile (<= value))
297{-# INLINE teeFstAllAny #-}
298teeFstAllAny :: MonadThrow m
299 => Int -> Stream m Int -> m ((), ())
303 (PR.dropWhile (<= value))
304 (PR.dropWhile (<= value))
307{-# INLINE shortestAllAny #-}
308shortestAllAny :: MonadThrow m
309 => Int -> Stream m Int -> m ()
310shortestAllAny value =
313 (PR.dropWhile (<= value))
314 (PR.dropWhile (<= value))
317{-# INLINE longestAllAny #-}
318longestAllAny :: MonadThrow m
319 => Int -> Stream m Int -> m ()
323 (PR.dropWhile (<= value))
324 (PR.dropWhile (<= value))
328parseManyChunksOfSum :: Int -> Handle -> IO Int
329parseManyChunksOfSum n inh =
330 Stream.fold Fold.length
332 (PR.fromFold $ Fold.take n Fold.sum)
333 (Stream.unfold Handle.read inh)
335-------------------------------------------------------------------------------
336-- Parsing with unfolds
337-------------------------------------------------------------------------------
339{-# INLINE parseManyUnfoldArrays #-}
340parseManyUnfoldArrays :: Int -> [Array.Array Int] -> IO ()
341parseManyUnfoldArrays count arrays = do
342 let src = Source.source (Just (Producer.OuterLoop arrays))
343 let parser = PR.fromFold (Fold.take count Fold.drain)
346 $ Producer.concat Producer.fromList Array.producer
348 Producer.simplify (Source.parseMany parser readSrc)
349 Stream.fold Fold.drain $ Stream.unfold streamParser src
351-------------------------------------------------------------------------------
352-- Parsers in which -fspec-constr-recursive=16 is problematic
353-------------------------------------------------------------------------------
355-- XXX -fspec-constr-recursive=16 makes GHC go beserk when compiling these.
356-- We need to fix GHC so that we can have better control over that option or do
357-- not have to rely on it.
359{-# INLINE lookAhead #-}
360lookAhead :: MonadThrow m => Int -> Stream m Int -> m ()
362 Stream.parse (PR.lookAhead (PR.takeWhile (<= value) Fold.drain) $> ())
364{-# INLINE sequenceA #-}
365sequenceA :: MonadThrow m => Int -> Stream m Int -> m Int
366sequenceA value xs = do
367 x <- Stream.parse (TR.sequenceA (replicate value (PR.satisfy (> 0)))) xs
370{-# INLINE sequenceA_ #-}
371sequenceA_ :: MonadThrow m => Int -> Stream m Int -> m ()
373 Stream.parse (F.sequenceA_ $ replicate value (PR.satisfy (> 0)))
375{-# INLINE sequence #-}
376sequence :: MonadThrow m => Int -> Stream m Int -> m Int
377sequence value xs = do
378 x <- Stream.parse (TR.sequence (replicate value (PR.satisfy (> 0)))) xs
381{-# INLINE sequence_ #-}
382sequence_ :: MonadThrow m => Int -> Stream m Int -> m ()
384 Stream.parse (F.sequence_ $ replicate value (PR.satisfy (> 0)))
386{-# INLINE choiceAsum #-}
387choiceAsum :: MonadThrow m => Int -> Stream m Int -> m Int
389 Stream.parse (asum (replicate value (PR.satisfy (< 0)))
390 AP.<|> PR.satisfy (> 0))
394choice :: MonadThrow m => Int -> Stream m Int -> m Int
397 (PR.choice (replicate value (PR.satisfy (< 0))) AP.<|> PR.satisfy (> 0))
400-------------------------------------------------------------------------------
401-- Stream transformation
402-------------------------------------------------------------------------------
404{-# INLINE parseMany #-}
405parseMany :: MonadThrow m => Int -> Stream m Int -> m ()
407 Stream.fold Fold.drain
409 . Stream.parseMany (PR.fromFold $ Fold.take n Fold.mconcat)
412{-# INLINE parseIterate #-}
413parseIterate :: MonadThrow m => Int -> Stream m Int -> m ()
415 Stream.fold Fold.drain
417 . Stream.parseIterate (PR.fromFold . Fold.take n . Fold.sconcat) (Sum 0)
420{-# INLINE parseBreak #-}
421parseBreak :: MonadCatch m => Stream m Int -> m ()
423 r <- try $ Stream.parseBreak PR.one s
425 Left (_ :: SomeException) -> return ()
426 Right (_, s1) -> parseBreak s1
428{-# INLINE concatSequence #-}
429concatSequence :: MonadThrow m => Stream m Int -> m ()
430concatSequence = Stream.parse $ PR.concatSequence Fold.drain $ Stream.repeat PR.one
432{-# INLINE parseManyGroupBy #-}
433parseManyGroupBy :: MonadThrow m => (Int -> Int -> Bool) -> Stream m Int -> m ()
434parseManyGroupBy cmp =
435 Stream.fold Fold.drain . Stream.parseMany (PR.groupBy cmp Fold.drain)
437-------------------------------------------------------------------------------
439-------------------------------------------------------------------------------
442moduleName = "Data.Parser"
444o_1_space_serial :: Int -> [Benchmark]
445o_1_space_serial value =
446 [ benchIOSink value "one (fold)" $ one value
447 , benchIOSink value "takeBetween" $ takeBetween value
448 , benchIOSink value "takeEQ" $ takeEQ value
449 , benchIOSink value "takeWhile" $ takeWhile value
450 , benchIOSink value "takeWhileP" $ takeWhileP value
451 , benchIOSink value "takeP" $ takeP value
452 , benchIOSink value "dropWhile" $ dropWhile value
453 , benchIOSink value "takeStartBy" $ takeStartBy value
454 , benchIOSrc sourceEscapedFrames value "takeFramedByEsc_"
455 $ takeFramedByEsc_ value
456 , benchIOSink value "groupBy" $ groupBy
457 , benchIOSink value "groupByRolling" $ groupByRolling
458 , benchIOSink value "wordBy" $ wordBy value
459 , benchIOSink value "sepBy (words)" $ sepByWords value
460 , benchIOSink value "deintercalate" $ deintercalate value
461 , benchIOSink value "splitAp" $ splitAp value
462 , benchIOSink value "splitApBefore" $ splitApBefore value
463 , benchIOSink value "splitApAfter" $ splitApAfter value
464 , benchIOSink value "serialWith" $ serialWith value
465 , benchIOSink value "takeEndBy_" $ takeEndBy_ value
466 , benchIOSink value "many" many
467 , benchIOSink value "many (wordBy even)" $ manyWordByEven
468 , benchIOSink value "some" some
469 , benchIOSink value "manyTill" $ manyTill value
471 , benchIOSink value "tee" $ teeAllAny value
472 , benchIOSink value "teeFst" $ teeFstAllAny value
473 , benchIOSink value "shortest" $ shortestAllAny value
474 , benchIOSink value "longest" $ longestAllAny value
476 , benchIOSink value "parseBreak (recursive)" parseBreak
477 , benchIOSink value "parseMany (take 1)" (parseMany 1)
478 , benchIOSink value "parseMany (take all)" (parseMany value)
479 , benchIOSink value "parseIterate (take 1)" (parseIterate 1)
480 , benchIOSink value "parseIterate (take all)" (parseIterate value)
481 , benchIOSink value "concatSequence" concatSequence
482 , benchIOSink value "parseMany (groupBy (<))" (parseManyGroupBy (<))
483 , benchIOSink value "parseMany (groupBy (==))" (parseManyGroupBy (==))
484 , benchIOSink value "eqBy" (eqBy value)
485 , benchIOSink value "matchBy" (matchBy value)
488o_1_space_filesystem :: BenchEnv -> [Benchmark]
489o_1_space_filesystem env =
490 [ mkBench ("S.parseMany (Fold.take " ++ show (bigSize env) ++ " Fold.sum)") env
491 $ \inh _ -> noinline parseManyChunksOfSum (bigSize env) inh
492 , mkBench "S.parseMany (Fold.take 1 Fold.sum)" env
493 $ \inh _ -> inline parseManyChunksOfSum 1 inh
496o_1_space_serial_unfold :: Int -> [Array.Array Int] -> [Benchmark]
497o_1_space_serial_unfold bound arrays =
498 [ bench "parseMany/Unfold/1000 arrays/take all"
499 $ nfIO $ parseManyUnfoldArrays bound arrays
500 , bench "parseMany/Unfold/1000 arrays/take 1"
501 $ nfIO $ parseManyUnfoldArrays 1 arrays
504o_n_heap_serial :: Int -> [Benchmark]
505o_n_heap_serial value =
507 -- lookahead benchmark holds the entire input till end
508 benchIOSink value "lookAhead" $ lookAhead value
510 -- accumulates the results in a list
511 , benchIOSink value "sequence" $ sequence value
512 , benchIOSink value "sequenceA" $ sequenceA value
514 -- XXX why should this take O(n) heap, it discards the results?
515 , benchIOSink value "sequence_" $ sequence_ value
516 , benchIOSink value "sequenceA_" $ sequenceA_ value
517 -- non-linear time complexity (parserD)
518 , benchIOSink value "split_" $ split_ value
519 -- XXX why O(n) heap?
520 , benchIOSink value "choice (asum)" $ choiceAsum value
521 -- , benchIOSink value "choice" $ choice value
523 -- These show non-linear time complexity.
524 -- They accumulate the results in a list.
525 , benchIOSink value "manyAlt" manyAlt
526 , benchIOSink value "someAlt" someAlt
529-------------------------------------------------------------------------------
531-------------------------------------------------------------------------------
535 env <- mkHandleBenchEnv
536 runWithCLIOptsEnv defaultStreamSize alloc (allBenchmarks env)
540 alloc value = Stream.fold Fold.toList $ Stream.arraysOf 100 $ sourceUnfoldrM value 0
542 allBenchmarks env arrays value =
543 [ bgroup (o_1_space_prefix moduleName) (o_1_space_serial value)
545 (o_1_space_prefix moduleName ++ "/filesystem")
546 (o_1_space_filesystem env)
547 , bgroup (o_1_space_prefix moduleName)
548 (o_1_space_serial_unfold value arrays)
549 , 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