1{-# OPTIONS_GHC -Wno-deprecations #-}
4-- Module : Streamly.Benchmark.Data.Parser
5-- Copyright : (c) 2020 Composewell Technologies
7-- License : BSD-3-Clause
8-- Maintainer : streamly@composewell.com
11{-# LANGUAGE FlexibleContexts #-}
12{-# LANGUAGE ScopedTypeVariables #-}
19import Control.DeepSeq (NFData(..))
20import Control.Monad.Catch (MonadCatch, MonadThrow, try, SomeException)
21import Data.Foldable (asum)
22import Data.Functor (($>))
23import Data.Maybe (fromMaybe)
24import Data.Monoid (Sum(..))
25import GHC.Magic (inline)
26import GHC.Magic (noinline)
27import System.IO (Handle)
28import System.Random (randomRIO)
30 (any, all, take, sequence, sequence_, sequenceA, takeWhile, dropWhile)
32import qualified Data.Traversable as TR
33import qualified Data.Foldable as F
34import qualified Control.Applicative as AP
35import qualified Streamly.FileSystem.Handle as Handle
36import qualified Streamly.Internal.Data.Array as Array
37import qualified Streamly.Internal.Data.Fold as Fold
38import qualified Streamly.Internal.Data.Parser as PR
39import qualified Streamly.Internal.Data.Stream as Stream
40import qualified Streamly.Internal.Data.Stream.IsStream as IsStream (tail)
41import qualified Streamly.Internal.Data.Producer as Producer
42import qualified Streamly.Internal.Data.Producer.Source as Source
44import Gauge hiding (env)
45import Streamly.Internal.Data.Stream (Stream)
46import Streamly.Benchmark.Common
47import Streamly.Benchmark.Common.Handle
49-------------------------------------------------------------------------------
51-------------------------------------------------------------------------------
53-- XXX these can be moved to the common module
55{-# INLINE sourceUnfoldrM #-}
56sourceUnfoldrM :: Monad m => Int -> Int -> Stream m Int
57sourceUnfoldrM value n = Stream.unfoldrM step n
62 else return (Just (cnt, cnt + 1))
64-- | Generates something like this: { { \{ \{ } }. The stream consists of
65-- three parts, the first part is contains a sequence of `{`. The second part
66-- contains a sequence pf escaped values `\{`. The third part contains a
68{-# INLINE sourceEscapedFrames #-}
74sourceEscapedFrames value n = Stream.unfoldrM step n
80 value1 = value `div` 4
83 | cnt > 4 * value1 = return Nothing
84 | cnt <= value1 = return $ Just (cbOpen, cnt + 1)
85 | cnt > 3 * value1 = return $ Just (cbClose, cnt + 1)
89 $ if (cnt - value1) `mod` 2 == 1
91 else (cbOpen, cnt + 1)
93{-# INLINE benchIOSrc #-}
96 => (Int -> Int -> Stream IO a)
99 -> (Stream IO a -> IO b)
101benchIOSrc src value name f =
102 bench name $ nfIO $ randomRIO (1,1) >>= f . src value
104-- | Takes a fold method, and uses it with a default source.
105{-# INLINE benchIOSink #-}
108 => Int -> String -> (Stream IO Int -> IO b) -> Benchmark
109benchIOSink value name f =
110 bench name $ nfIO $ randomRIO (1,1) >>= f . sourceUnfoldrM value
112-------------------------------------------------------------------------------
114-------------------------------------------------------------------------------
117one :: MonadThrow m => Int -> Stream m Int -> m (Maybe Int)
118one value = Stream.parse p
123 m <- PR.fromFold Fold.one
125 Just i -> if i >= value then pure m else p
126 Nothing -> pure Nothing
128{-# INLINE takeBetween #-}
129takeBetween :: MonadThrow m => Int -> Stream m a -> m ()
130takeBetween value = Stream.parse (PR.takeBetween 0 value Fold.drain)
133takeEQ :: MonadThrow m => Int -> Stream m a -> m ()
134takeEQ value = Stream.parse (PR.takeEQ value Fold.drain)
136{-# INLINE dropWhile #-}
137dropWhile :: MonadThrow m => Int -> Stream m Int -> m ()
138dropWhile value = Stream.parse (PR.dropWhile (<= value))
140{-# INLINE takeStartBy #-}
141takeStartBy :: MonadThrow m => Int -> Stream m Int -> m ()
142takeStartBy value stream = do
143 stream1 <- return . fromMaybe (Stream.fromPure (value + 1)) =<< IsStream.tail stream
144 let stream2 = value `Stream.cons` stream1
145 Stream.parse (PR.takeStartBy (== value) Fold.drain) stream2
147takeFramedByEsc_ :: MonadThrow m => Int -> Stream m Char -> m ()
148takeFramedByEsc_ _ = Stream.parse parser
156 parser = PR.takeFramedByEsc_ isEsc isBegin isEnd Fold.drain
158{-# INLINE listEqBy #-}
159listEqBy :: Int -> Stream IO Int -> IO [Int]
160listEqBy len = Stream.parse (PR.listEqBy (==) [1 .. len])
163eqBy :: Int -> Stream IO Int -> IO ()
164eqBy len = Stream.parse (PR.eqBy (==) (Stream.enumerateFromTo 1 len))
166{-# INLINE takeWhile #-}
167takeWhile :: MonadThrow m => Int -> Stream m Int -> m ()
168takeWhile value = Stream.parse (PR.takeWhile (<= value) Fold.drain)
170takeWhileP :: MonadThrow m => Int -> Stream m Int -> m ()
172 Stream.parse (PR.takeWhileP (<= value) (PR.takeWhile (<= value - 1) Fold.drain))
175takeP :: MonadThrow m => Int -> Stream m a -> m ()
176takeP value = Stream.parse (PR.takeP value (PR.fromFold Fold.drain))
178{-# INLINE groupBy #-}
179groupBy :: MonadThrow m => Stream m Int -> m ()
180groupBy = Stream.parse (PR.groupBy (<=) Fold.drain)
182{-# INLINE groupByRolling #-}
183groupByRolling :: MonadThrow m => Stream m Int -> m ()
184groupByRolling = Stream.parse (PR.groupByRolling (<=) Fold.drain)
187wordBy :: MonadThrow m => Int -> Stream m Int -> m ()
188wordBy value = Stream.parse (PR.wordBy (>= value) Fold.drain)
190{-# INLINE sepByWords #-}
191sepByWords :: MonadThrow m => Int -> Stream m Int -> m ()
192sepByWords _ = Stream.parse (wrds even Fold.drain)
194 wrds p = PR.sepBy (PR.takeWhile (not . p) Fold.drain) (PR.dropWhile p)
196{-# INLINE deintercalate #-}
197deintercalate :: MonadThrow m => Int -> Stream m Int -> m ()
198deintercalate _ = Stream.parse (partition even)
204 (PR.takeWhile (not . p) Fold.sum) (PR.takeWhile p Fold.sum) Fold.drain
206{-# INLINE manyWordByEven #-}
207manyWordByEven :: MonadThrow m => Stream m Int -> m ()
208manyWordByEven = Stream.parse (PR.many (PR.wordBy even Fold.drain) Fold.drain)
211many :: MonadThrow m => Stream m Int -> m Int
212many = Stream.parse (PR.many (PR.satisfy (> 0)) Fold.length)
214{-# INLINE manyAlt #-}
215manyAlt :: MonadThrow m => Stream m Int -> m Int
217 x <- Stream.parse (AP.many (PR.satisfy (> 0))) xs
218 return $ Prelude.length x
221some :: MonadThrow m => Stream m Int -> m Int
222some = Stream.parse (PR.some (PR.satisfy (> 0)) Fold.length)
224{-# INLINE someAlt #-}
225someAlt :: MonadThrow m => Stream m Int -> m Int
227 x <- Stream.parse (AP.some (PR.satisfy (> 0))) xs
228 return $ Prelude.length x
230{-# INLINE manyTill #-}
231manyTill :: MonadThrow m => Int -> Stream m Int -> m Int
233 Stream.parse (PR.manyTill (PR.satisfy (> 0)) (PR.satisfy (== value)) Fold.length)
235{-# INLINE splitAp #-}
236splitAp :: MonadThrow m
237 => Int -> Stream m Int -> m ((), ())
241 <$> PR.dropWhile (<= (value `div` 2))
242 <*> PR.dropWhile (<= value)
245{-# INLINE splitApBefore #-}
246splitApBefore :: MonadThrow m
247 => Int -> Stream m Int -> m ()
250 ( PR.dropWhile (<= (value `div` 2))
251 *> PR.dropWhile (<= value)
254{-# INLINE splitApAfter #-}
255splitApAfter :: MonadThrow m
256 => Int -> Stream m Int -> m ()
259 ( PR.dropWhile (<= (value `div` 2))
260 <* PR.dropWhile (<= value)
263{-# INLINE splitWith #-}
264splitWith :: MonadThrow m
265 => Int -> Stream m Int -> m ((), ())
269 (PR.dropWhile (<= (value `div` 2)))
270 (PR.dropWhile (<= value))
274split_ :: MonadThrow m
275 => Int -> Stream m Int -> m ()
279 (PR.dropWhile (<= (value `div` 2)))
280 (PR.dropWhile (<= value))
283{-# INLINE takeEndBy_ #-}
284takeEndBy_ :: MonadThrow m
285 => Int -> Stream m Int -> m()
286takeEndBy_ value = Stream.parse (PR.takeEndBy_ (>= value) (PR.fromFold Fold.drain))
289{-# INLINE teeAllAny #-}
290teeAllAny :: MonadThrow m
291 => Int -> Stream m Int -> m ((), ())
295 (PR.dropWhile (<= value))
296 (PR.dropWhile (<= value))
299{-# INLINE teeFstAllAny #-}
300teeFstAllAny :: MonadThrow m
301 => Int -> Stream m Int -> m ((), ())
305 (PR.dropWhile (<= value))
306 (PR.dropWhile (<= value))
309{-# INLINE shortestAllAny #-}
310shortestAllAny :: MonadThrow m
311 => Int -> Stream m Int -> m ()
312shortestAllAny value =
315 (PR.dropWhile (<= value))
316 (PR.dropWhile (<= value))
319{-# INLINE longestAllAny #-}
320longestAllAny :: MonadThrow m
321 => Int -> Stream m Int -> m ()
325 (PR.dropWhile (<= value))
326 (PR.dropWhile (<= value))
330parseManyChunksOfSum :: Int -> Handle -> IO Int
331parseManyChunksOfSum n inh =
332 Stream.fold Fold.length
334 (PR.fromFold $ Fold.take n Fold.sum)
335 (Stream.unfold Handle.reader inh)
337-------------------------------------------------------------------------------
338-- Parsing with unfolds
339-------------------------------------------------------------------------------
341{-# INLINE parseManyUnfoldArrays #-}
342parseManyUnfoldArrays :: Int -> [Array.Array Int] -> IO ()
343parseManyUnfoldArrays count arrays = do
344 let src = Source.source (Just (Producer.OuterLoop arrays))
345 let parser = PR.fromFold (Fold.take count Fold.drain)
348 $ Producer.concat Producer.fromList Array.producer
350 Producer.simplify (Source.parseMany parser readSrc)
351 Stream.fold Fold.drain $ Stream.unfold streamParser src
353-------------------------------------------------------------------------------
354-- Parsers in which -fspec-constr-recursive=16 is problematic
355-------------------------------------------------------------------------------
357-- XXX -fspec-constr-recursive=16 makes GHC go beserk when compiling these.
358-- We need to fix GHC so that we can have better control over that option or do
359-- not have to rely on it.
361{-# INLINE lookAhead #-}
362lookAhead :: MonadThrow m => Int -> Stream m Int -> m ()
364 Stream.parse (PR.lookAhead (PR.takeWhile (<= value) Fold.drain) $> ())
366{-# INLINE sequenceA #-}
367sequenceA :: MonadThrow m => Int -> Stream m Int -> m Int
368sequenceA value xs = do
369 x <- Stream.parse (TR.sequenceA (replicate value (PR.satisfy (> 0)))) xs
372{-# INLINE sequenceA_ #-}
373sequenceA_ :: MonadThrow m => Int -> Stream m Int -> m ()
375 Stream.parse (F.sequenceA_ $ replicate value (PR.satisfy (> 0)))
377{-# INLINE sequence #-}
378sequence :: MonadThrow m => Int -> Stream m Int -> m Int
379sequence value xs = do
380 x <- Stream.parse (TR.sequence (replicate value (PR.satisfy (> 0)))) xs
383{-# INLINE sequence_ #-}
384sequence_ :: MonadThrow m => Int -> Stream m Int -> m ()
386 Stream.parse (F.sequence_ $ replicate value (PR.satisfy (> 0)))
388{-# INLINE choiceAsum #-}
389choiceAsum :: MonadThrow m => Int -> Stream m Int -> m Int
391 Stream.parse (asum (replicate value (PR.satisfy (< 0)))
392 AP.<|> PR.satisfy (> 0))
396choice :: MonadThrow m => Int -> Stream m Int -> m Int
399 (PR.choice (replicate value (PR.satisfy (< 0))) AP.<|> PR.satisfy (> 0))
402-------------------------------------------------------------------------------
403-- Stream transformation
404-------------------------------------------------------------------------------
406{-# INLINE parseMany #-}
407parseMany :: MonadThrow m => Int -> Stream m Int -> m ()
409 Stream.fold Fold.drain
411 . Stream.parseMany (PR.fromFold $ Fold.take n Fold.mconcat)
414{-# INLINE parseIterate #-}
415parseIterate :: MonadThrow m => Int -> Stream m Int -> m ()
417 Stream.fold Fold.drain
419 . Stream.parseIterate (PR.fromFold . Fold.take n . Fold.sconcat) (Sum 0)
422{-# INLINE parseBreak #-}
423parseBreak :: MonadCatch m => Stream m Int -> m ()
425 r <- try $ Stream.parseBreak PR.one s
427 Left (_ :: SomeException) -> return ()
428 Right (_, s1) -> parseBreak s1
430{-# INLINE concatSequence #-}
431concatSequence :: MonadThrow m => Stream m Int -> m ()
432concatSequence = Stream.parse $ PR.concatSequence Fold.drain $ Stream.repeat PR.one
434{-# INLINE parseManyGroupBy #-}
435parseManyGroupBy :: MonadThrow m => (Int -> Int -> Bool) -> Stream m Int -> m ()
436parseManyGroupBy cmp =
437 Stream.fold Fold.drain . Stream.parseMany (PR.groupBy cmp Fold.drain)
439-------------------------------------------------------------------------------
441-------------------------------------------------------------------------------
444moduleName = "Data.Parser"
446o_1_space_serial :: Int -> [Benchmark]
447o_1_space_serial value =
448 [ benchIOSink value "one (fold)" $ one value
449 , benchIOSink value "takeBetween" $ takeBetween value
450 , benchIOSink value "takeEQ" $ takeEQ value
451 , benchIOSink value "takeWhile" $ takeWhile value
452 , benchIOSink value "takeWhileP" $ takeWhileP value
453 , benchIOSink value "takeP" $ takeP value
454 , benchIOSink value "dropWhile" $ dropWhile value
455 , benchIOSink value "takeStartBy" $ takeStartBy value
456 , benchIOSrc sourceEscapedFrames value "takeFramedByEsc_"
457 $ takeFramedByEsc_ value
458 , benchIOSink value "groupBy" $ groupBy
459 , benchIOSink value "groupByRolling" $ groupByRolling
460 , benchIOSink value "wordBy" $ wordBy value
461 , benchIOSink value "sepBy (words)" $ sepByWords value
462 , benchIOSink value "deintercalate" $ deintercalate value
463 , benchIOSink value "splitAp" $ splitAp value
464 , benchIOSink value "splitApBefore" $ splitApBefore value
465 , benchIOSink value "splitApAfter" $ splitApAfter value
466 , benchIOSink value "splitWith" $ splitWith value
467 , benchIOSink value "takeEndBy_" $ takeEndBy_ value
468 , benchIOSink value "many" many
469 , benchIOSink value "many (wordBy even)" $ manyWordByEven
470 , benchIOSink value "some" some
471 , benchIOSink value "manyTill" $ manyTill value
473 , benchIOSink value "tee" $ teeAllAny value
474 , benchIOSink value "teeFst" $ teeFstAllAny value
475 , benchIOSink value "shortest" $ shortestAllAny value
476 , benchIOSink value "longest" $ longestAllAny value
478 , benchIOSink value "parseBreak (recursive)" parseBreak
479 , benchIOSink value "parseMany (take 1)" (parseMany 1)
480 , benchIOSink value "parseMany (take all)" (parseMany value)
481 , benchIOSink value "parseIterate (take 1)" (parseIterate 1)
482 , benchIOSink value "parseIterate (take all)" (parseIterate value)
483 , benchIOSink value "concatSequence" concatSequence
484 , benchIOSink value "parseMany (groupBy (<))" (parseManyGroupBy (<))
485 , benchIOSink value "parseMany (groupBy (==))" (parseManyGroupBy (==))
486 , benchIOSink value "listEqBy" (listEqBy value)
487 , benchIOSink value "eqBy" (eqBy value)
490o_1_space_filesystem :: BenchEnv -> [Benchmark]
491o_1_space_filesystem env =
492 [ mkBench ("S.parseMany (Fold.take " ++ show (bigSize env) ++ " Fold.sum)") env
493 $ \inh _ -> noinline parseManyChunksOfSum (bigSize env) inh
494 , mkBench "S.parseMany (Fold.take 1 Fold.sum)" env
495 $ \inh _ -> inline parseManyChunksOfSum 1 inh
498o_1_space_serial_unfold :: Int -> [Array.Array Int] -> [Benchmark]
499o_1_space_serial_unfold bound arrays =
500 [ bench "parseMany/Unfold/1000 arrays/take all"
501 $ nfIO $ parseManyUnfoldArrays bound arrays
502 , bench "parseMany/Unfold/1000 arrays/take 1"
503 $ nfIO $ parseManyUnfoldArrays 1 arrays
506o_n_heap_serial :: Int -> [Benchmark]
507o_n_heap_serial value =
509 -- lookahead benchmark holds the entire input till end
510 benchIOSink value "lookAhead" $ lookAhead value
512 -- accumulates the results in a list
513 , benchIOSink value "sequence" $ sequence value
514 , benchIOSink value "sequenceA" $ sequenceA value
516 -- XXX why should this take O(n) heap, it discards the results?
517 , benchIOSink value "sequence_" $ sequence_ value
518 , benchIOSink value "sequenceA_" $ sequenceA_ value
519 -- non-linear time complexity (parserD)
520 , benchIOSink value "split_" $ split_ value
521 -- XXX why O(n) heap?
522 , benchIOSink value "choice (asum)" $ choiceAsum value
523 -- , benchIOSink value "choice" $ choice value
525 -- These show non-linear time complexity.
526 -- They accumulate the results in a list.
527 , benchIOSink value "manyAlt" manyAlt
528 , benchIOSink value "someAlt" someAlt
531-------------------------------------------------------------------------------
533-------------------------------------------------------------------------------
537 env <- mkHandleBenchEnv
538 runWithCLIOptsEnv defaultStreamSize alloc (allBenchmarks env)
542 alloc value = Stream.fold Fold.toList $ Stream.arraysOf 100 $ sourceUnfoldrM value 0
544 allBenchmarks env arrays value =
545 [ bgroup (o_1_space_prefix moduleName) (o_1_space_serial value)
547 (o_1_space_prefix moduleName ++ "/filesystem")
548 (o_1_space_filesystem env)
549 , bgroup (o_1_space_prefix moduleName)
550 (o_1_space_serial_unfold value arrays)
551 , 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