packages feed

streamly-0.11.1: benchmark/Streamly/Benchmark/Data/Stream/Split.hs

-- |
-- Module      : Stream.Split
-- Copyright   : (c) 2019 Composewell Technologies
-- License     : BSD-3-Clause
-- Maintainer  : streamly@composewell.com
-- Stability   : experimental
-- Portability : GHC

{-# LANGUAGE CPP #-}
{-# LANGUAGE ScopedTypeVariables #-}

#ifdef __HADDOCK_VERSION__
#undef INSPECTION
#endif

#ifdef INSPECTION
{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS_GHC -fplugin Test.Inspection.Plugin #-}
#endif

module Stream.Split (benchmarks) where

import Data.Char (ord)
import Data.Word (Word8)
import System.IO (Handle)
import Streamly.Internal.Data.Array (Array)

import qualified Streamly.Internal.Data.Array as Array
import qualified Streamly.Internal.Data.Fold as Fold
import qualified Streamly.Internal.Data.Stream as Stream
import qualified Streamly.Internal.FileSystem.Handle as Handle
import qualified Streamly.Internal.Unicode.Stream as Unicode

import Test.Tasty.Bench hiding (env)
import Prelude hiding (last, length)
import Streamly.Benchmark.Common
import Streamly.Benchmark.Common.Handle

#ifdef INSPECTION
import Streamly.Internal.Data.Stream (Step(..))

import qualified Streamly.Internal.Data.MutArray as MutArray
import qualified Streamly.Internal.Data.Unfold as Unfold

import Test.Inspection
#endif

 -------------------------------------------------------------------------------
-- reduce with splitting transformations
-------------------------------------------------------------------------------

lf :: Word8
lf = fromIntegral (ord '\n')

toarr :: String -> Array Word8
toarr = Array.fromList . map (fromIntegral . ord)

-- | Split on line feed.
splitOn :: Handle -> IO Int
splitOn inh =
    (Stream.fold Fold.length
        $ Stream.splitSepBy_ (== lf) Fold.drain
        $ Handle.read inh) -- >>= print

#ifdef INSPECTION
inspect $ hasNoTypeClasses 'splitOn
inspect $ 'splitOn `hasNoType` ''Step
inspect $ 'splitOn `hasNoType` ''Unfold.ConcatState -- FH.read/UF.many
inspect $ 'splitOn `hasNoType` ''MutArray.ArrayUnsafe  -- FH.read/A.read
#endif

-- | Words by space
wordsBy :: Handle -> IO Int
wordsBy inh =
    Stream.fold Fold.length
        $ Stream.wordsBy isSp Fold.drain
        $ Handle.read inh -- >>= print

#ifdef INSPECTION
inspect $ hasNoTypeClasses 'wordsBy
inspect $ 'wordsBy `hasNoType` ''Step
inspect $ 'wordsBy `hasNoType` ''Unfold.ConcatState -- FH.read/UF.many
inspect $ 'wordsBy `hasNoType` ''MutArray.ArrayUnsafe  -- FH.read/A.read
#endif

-- | Split on a word8 sequence.
splitOnSeq :: String -> Handle -> IO Int
splitOnSeq str inh =
    (Stream.fold Fold.length $ Stream.splitSepBySeq_ (toarr str) Fold.drain
        $ Handle.read inh) -- >>= print

#ifdef INSPECTION
-- inspect $ hasNoTypeClasses 'splitOnSeq
-- inspect $ 'splitOnSeq `hasNoType` ''Step
#endif

takeEndBy :: Word8 -> Handle -> IO Int
takeEndBy c inh =
    (Stream.fold Fold.length
        $ Stream.takeEndBy (== c)
        $ Stream.filter (/= fromIntegral (ord 'a'))
        $ Handle.read inh) -- >>= print

takeEndBy_ :: Word8 -> Handle -> IO Int
takeEndBy_ c inh =
    (Stream.fold Fold.length
        $ Stream.takeEndBy_ (== c)
        $ Stream.filter (/= fromIntegral (ord 'a'))
        $ Handle.read inh) -- >>= print

takeEndBySeq :: String -> Handle -> IO Int
takeEndBySeq str inh =
    (Stream.fold Fold.length
        $ Stream.takeEndBySeq (toarr str)
        $ Stream.filter (/= fromIntegral (ord 'a'))
        $ Handle.read inh) -- >>= print

takeEndBySeq_ :: String -> Handle -> IO Int
takeEndBySeq_ str inh =
    (Stream.fold Fold.length
        $ Stream.takeEndBySeq_ (toarr str)
        $ Stream.filter (/= fromIntegral (ord 'a'))
        $ Handle.read inh) -- >>= print

takeEndBySeq100k :: Handle -> IO Int
takeEndBySeq100k inh = do
    arr <- Stream.fold Array.create $ Stream.replicate 100000 123
    (Stream.fold Fold.length
        $ Stream.takeEndBySeq arr
        $ Stream.filter (/= fromIntegral (ord 'a'))
        $ Handle.read inh) -- >>= print

takeEndBySeq_100k :: Handle -> IO Int
takeEndBySeq_100k inh = do
    arr <- Stream.fold Array.create $ Stream.replicate 100000 123
    (Stream.fold Fold.length
        $ Stream.takeEndBySeq_ arr
        $ Stream.filter (/= fromIntegral (ord 'a'))
        $ Handle.read inh) -- >>= print

-- | Split on a word8 sequence.
splitOnSeq100k :: Handle -> IO Int
splitOnSeq100k inh = do
    arr <- Stream.fold Array.create $ Stream.replicate 100000 123
    (Stream.fold Fold.length
        $ Stream.splitSepBySeq_ arr Fold.drain
        $ Handle.read inh) -- >>= print

-- | Split on suffix sequence.
splitOnSuffixSeq :: String -> Handle -> IO Int
splitOnSuffixSeq str inh =
    (Stream.fold Fold.length
        $ Stream.splitOnSuffixSeq False (toarr str) Fold.drain
        $ Handle.read inh) -- >>= print

#ifdef INSPECTION
-- inspect $ hasNoTypeClasses 'splitOnSuffixSeq
-- inspect $ 'splitOnSuffixSeq `hasNoType` ''Step
#endif

-- | Split on suffix sequence.
splitWithSuffixSeq :: String -> Handle -> IO Int
splitWithSuffixSeq str inh =
    Stream.fold Fold.length
        $ Stream.splitOnSuffixSeq True (toarr str) Fold.drain
        $ Handle.read inh -- >>= print

o_1_space_reduce_read_split :: BenchEnv -> [Benchmark]
o_1_space_reduce_read_split env =
    -- NOTE: keep the benchmark names consistent with Data.Fold.takeEndBy*
    [ bgroup "FileSplitElem"
        [ mkBench "splitOn infix lf" env $ \inh _ ->
            splitOn inh
        ]
    -- splitting on a sequence
    , bgroup "FileSplitSeq"
        [
          mkBench "wordsBy infix isSpace" env $ \inh _ ->
            wordsBy inh

        -- Infix
        , mkBench "splitOnSeq empty infix" env $ \inh _ ->
            splitOnSeq "" inh
        , mkBench "splitOnSeq single infix lf" env $ \inh _ ->
            splitOnSeq "\n" inh
        , mkBench "splitOnSeq single infix a" env $ \inh _ ->
            splitOnSeq "a" inh
        , mkBench "splitOnSeq word infix crlf" env $ \inh _ ->
            splitOnSeq "\r\n" inh
        , mkBench "splitOnSeq word infix aa" env $ \inh _ ->
            splitOnSeq "aa" inh
        , mkBench "splitOnSeq word infix aaaa" env $ \inh _ ->
            splitOnSeq "aaaa" inh
        , mkBench "splitOnSeq word infix abcdefgh" env $ \inh _ ->
            splitOnSeq "abcdefgh" inh
        , mkBench "splitOnSeq KR infix abcdefghi" env $ \inh _ ->
            splitOnSeq "abcdefghi" inh
        , mkBench "splitOnSeq KR infix catcatcatcatcat" env $ \inh _ ->
            splitOnSeq "catcatcatcatcat" inh
        , mkBench "splitOnSeq KR infix abcdefghijklmnopqrstuvwxyz"
            env $ \inh _ -> splitOnSeq "abcdefghijklmnopqrstuvwxyz" inh
        , mkBench "splitOnSeq KR infix 100k long pattern"
            env $ \inh _ -> splitOnSeq100k inh

        -- Suffix
        , mkBench "splitOnSuffixSeq empty suffix" env $ \inh _ ->
            splitOnSuffixSeq "" inh
        , mkBench "splitOnSuffixSeq single suffix lf" env $ \inh _ ->
            splitOnSuffixSeq "\n" inh
        , mkBench "splitOnSuffixSeq word suffix crlf" env $ \inh _ ->
            splitOnSuffixSeq "\r\n" inh
        , mkBenchSmall "splitOnSuffixSeq KR suffix abcdefghijklmnopqrstuvwxyz"
            env $ \inh _ -> splitOnSuffixSeq "abcdefghijklmnopqrstuvwxyz" inh

        -- Suffix with separator
        , mkBench "splitWithSuffixSeq single suffix lf" env $ \inh _ ->
            splitWithSuffixSeq "\n" inh
        , mkBench "splitWithSuffixSeq word suffix crlf" env $ \inh _ ->
            splitWithSuffixSeq "\r\n" inh
        , mkBench "splitWithSuffixSeq KR suffix abcdefghi" env $ \inh _ ->
            splitWithSuffixSeq "abcdefghi" inh
        , mkBenchSmall "splitWithSuffixSeq KR suffix abcdefghijklmnopqrstuvwxyz"
            env $ \inh _ -> splitWithSuffixSeq "abcdefghijklmnopqrstuvwxyz" inh
        ]

    , bgroup "FileTakeSeq"
        [
        {-
          mkBench "takeEndBySeq empty" env $ \inh _ ->
                takeEndBySeq "" inh
        -}
        -- IMPORTANT: the pattern must contain a, because we filter a's out
        -- from the stream so that we do not terminate too early and
        -- unpredictably.
          mkBench "takeEndBy" env $ \inh _ ->
            takeEndBy (fromIntegral $ ord 'a') inh
        , mkBench "takeEndBy_" env $ \inh _ ->
            takeEndBy_ (fromIntegral $ ord 'a') inh
        , mkBench "takeEndBySeq single a" env $ \inh _ ->
            takeEndBySeq "a" inh
        , mkBench "takeEndBySeq word aa" env $ \inh _ ->
            takeEndBySeq "aa" inh
        , mkBench "takeEndBySeq word aaaa" env $ \inh _ ->
            takeEndBySeq "aaaa" inh
        , mkBench "takeEndBySeq word abcdefgh" env $ \inh _ ->
            takeEndBySeq "abcdefgh" inh
        , mkBench "takeEndBySeq KR abcdefghi" env $ \inh _ ->
            takeEndBySeq "abcdefghi" inh
        , mkBench "takeEndBySeq KR catcatcatcatcat" env $ \inh _ ->
            takeEndBySeq "catcatcatcatcat" inh
        , mkBench "takeEndBySeq KR abcdefghijklmnopqrstuvwxyz"
            env $ \inh _ -> takeEndBySeq "abcdefghijklmnopqrstuvwxyz" inh
        , mkBench "takeEndBySeq KR 100k long pattern"
            env $ \inh _ -> takeEndBySeq100k inh

        {-
        , mkBench "takeEndBySeq_ empty" env $ \inh _ ->
            takeEndBySeq_ "" inh
        -}
        , mkBench "takeEndBySeq_ single a" env $ \inh _ ->
            takeEndBySeq_ "a" inh
        , mkBench "takeEndBySeq_ word aa" env $ \inh _ ->
            takeEndBySeq_ "aa" inh
        , mkBench "takeEndBySeq_ word aaaa" env $ \inh _ ->
            takeEndBySeq_ "aaaa" inh
        , mkBench "takeEndBySeq_ word abcdefgh" env $ \inh _ ->
            takeEndBySeq_ "abcdefgh" inh
        , mkBench "takeEndBySeq_ KR abcdefghi" env $ \inh _ ->
            takeEndBySeq_ "abcdefghi" inh
        , mkBench "takeEndBySeq_ KR catcatcatcatcat" env $ \inh _ ->
            takeEndBySeq_ "catcatcatcatcat" inh
        , mkBench "takeEndBySeq_ KR abcdefghijklmnopqrstuvwxyz"
            env $ \inh _ -> takeEndBySeq_ "abcdefghijklmnopqrstuvwxyz" inh
        , mkBench "takeEndBySeq_ KR 100k long pattern"
            env $ \inh _ -> takeEndBySeq_100k inh
        ]
    ]

-- | Split on a character sequence.
splitOnSeqUtf8 :: String -> Handle -> IO Int
splitOnSeqUtf8 str inh =
    (Stream.fold Fold.length
        $ Stream.splitSepBySeq_ (Array.fromList str) Fold.drain
        $ Unicode.decodeUtf8Chunks
        $ Handle.readChunks inh) -- >>= print

o_1_space_reduce_toChunks_split :: BenchEnv -> [Benchmark]
o_1_space_reduce_toChunks_split env =
    [ bgroup "FileSplitSeqUtf8"
        [ mkBenchSmall "splitOnSeqUtf8 word abcdefgh"
            env $ \inh _ -> splitOnSeqUtf8 "abcdefgh" inh
        , mkBenchSmall "splitOnSeqUtf8 KR abcdefghijklmnopqrstuvwxyz"
            env $ \inh _ -> splitOnSeqUtf8 "abcdefghijklmnopqrstuvwxyz" inh
        ]
    ]

benchmarks :: String -> BenchEnv -> [Benchmark]
benchmarks moduleName env =
        [ bgroup (o_1_space_prefix moduleName) $ concat
            [ o_1_space_reduce_read_split env
            , o_1_space_reduce_toChunks_split env
            ]
        ]