2-- Module : Streamly.Benchmark.FileSystem.Handle
3-- Copyright : (c) 2019 Composewell Technologies
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
6-- Stability : experimental
10{-# LANGUAGE ScopedTypeVariables #-}
12#ifdef __HADDOCK_VERSION__
17{-# LANGUAGE TemplateHaskell #-}
18{-# OPTIONS_GHC -fplugin Test.Inspection.Plugin #-}
25import Data.Word (Word8)
26import GHC.Magic (inline)
27import GHC.Magic (noinline)
28import System.IO (Handle)
30import qualified Streamly.FileSystem.Handle as FH
31import qualified Streamly.Internal.Data.Array.Foreign as A
32import qualified Streamly.Internal.Data.Array.Foreign.Type as AT
33import qualified Streamly.Internal.Data.Fold as FL
34import qualified Streamly.Internal.Data.Stream.IsStream as IP
35import qualified Streamly.Internal.FileSystem.Handle as IFH
36import qualified Streamly.Internal.Unicode.Stream as IUS
37import qualified Streamly.Prelude as S
38import qualified Streamly.Unicode.Stream as SS
40import Gauge hiding (env)
41import Prelude hiding (last, length)
42import Streamly.Benchmark.Common.Handle
45import Streamly.Internal.Data.Stream.StreamD.Type (Step(..), FoldMany)
47import qualified Streamly.Internal.Data.Array.Foreign.Mut.Type as MA
48import qualified Streamly.Internal.Data.Stream.StreamD as D
49import qualified Streamly.Internal.Data.Unfold as IUF
54-- TBD reading with unfold
56-------------------------------------------------------------------------------
58-------------------------------------------------------------------------------
60-- | Get the last byte from a file bytestream.
61readLast :: Handle -> IO (Maybe Word8)
62readLast = S.last . S.unfold FH.read
65inspect $ hasNoTypeClasses 'readLast
66inspect $ 'readLast `hasNoType` ''Step -- S.unfold
67inspect $ 'readLast `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
68inspect $ 'readLast `hasNoType` ''MA.ReadUState -- FH.read/A.read
71-- assert that flattenArrays constructors are not present
72-- | Count the number of bytes in a file.
73readCountBytes :: Handle -> IO Int
74readCountBytes = S.length . S.unfold FH.read
77inspect $ hasNoTypeClasses 'readCountBytes
78inspect $ 'readCountBytes `hasNoType` ''Step -- S.unfold
79inspect $ 'readCountBytes `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
80inspect $ 'readCountBytes `hasNoType` ''MA.ReadUState -- FH.read/A.read
83-- | Count the number of lines in a file.
84readCountLines :: Handle -> IO Int
92inspect $ hasNoTypeClasses 'readCountLines
93inspect $ 'readCountLines `hasNoType` ''Step
94inspect $ 'readCountLines `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
95inspect $ 'readCountLines `hasNoType` ''MA.ReadUState -- FH.read/A.read
98-- | Count the number of words in a file.
99readCountWords :: Handle -> IO Int
107inspect $ hasNoTypeClasses 'readCountWords
108-- inspect $ 'readCountWords `hasNoType` ''Step
111-- | Sum the bytes in a file.
112readSumBytes :: Handle -> IO Word8
113readSumBytes = S.sum . S.unfold FH.read
116inspect $ hasNoTypeClasses 'readSumBytes
117inspect $ 'readSumBytes `hasNoType` ''Step
118inspect $ 'readSumBytes `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
119inspect $ 'readSumBytes `hasNoType` ''MA.ReadUState -- FH.read/A.read
122-- XXX When we mark this with INLINE and we have two benchmarks using S.drain
123-- in one benchmark group then somehow GHC ends up delaying the inlining of
124-- readDrain. Since S.drain has an INLINE[2] for proper rule firing, that does
125-- not work well because of delyaed inlining and the code does not fuse. We
126-- need some way of propagating the inline phase information up so that we can
127-- expedite inlining of the callers too automatically. The minimal example for
128-- the problem can be created by using just two benchmarks in a bench group
129-- both using "readDrain". Either GHC should be fixed or we can use
130-- fusion-plugin to propagate INLINE phase information such that this problem
132readDrain :: Handle -> IO ()
133readDrain inh = S.drain $ S.unfold FH.read inh
135-- XXX investigate why we need an INLINE in this case (GHC)
136{-# INLINE readDecodeLatin1 #-}
137readDecodeLatin1 :: Handle -> IO ()
138readDecodeLatin1 inh =
141 $ S.unfold FH.read inh
143readDecodeUtf8 :: Handle -> IO ()
147 $ S.unfold FH.read inh
150inspect $ hasNoTypeClasses 'readDecodeUtf8
151-- inspect $ 'readDecodeUtf8Lax `hasNoType` ''Step
154o_1_space_reduce_read :: BenchEnv -> [Benchmark]
155o_1_space_reduce_read env =
156 [ bgroup "reduce/read"
157 [ -- read raw bytes without any decoding
158 mkBench "S.drain" env $ \inh _ ->
160 , mkBench "S.last" env $ \inh _ ->
162 , mkBench "S.sum" env $ \inh _ ->
165 -- read with Latin1 decoding
166 , mkBench "SS.decodeLatin1" env $ \inh _ ->
168 , mkBench "S.length" env $ \inh _ ->
170 , mkBench "US.lines . SS.decodeLatin1" env $ \inh _ ->
172 , mkBench "US.words . SS.decodeLatin1" env $ \inh _ ->
175 -- read with utf8 decoding
176 , mkBenchSmall "SS.decodeUtf8" env $ \inh _ ->
181-------------------------------------------------------------------------------
183-------------------------------------------------------------------------------
185-- | Count the number of lines in a file.
186getChunksConcatUnfoldCountLines :: Handle -> IO Int
187getChunksConcatUnfoldCountLines inh =
191 -- XXX replace with toBytes
192 $ S.unfoldMany A.read (IFH.getChunks inh)
195inspect $ hasNoTypeClasses 'getChunksConcatUnfoldCountLines
196inspect $ 'getChunksConcatUnfoldCountLines `hasNoType` ''Step
197inspect $ 'getChunksConcatUnfoldCountLines `hasNoType` ''D.ConcatMapUState
200o_1_space_reduce_toBytes :: BenchEnv -> [Benchmark]
201o_1_space_reduce_toBytes env =
202 [ bgroup "reduce/toBytes"
203 [ mkBench "US.lines . SS.decodeLatin1" env $ \inh _ ->
204 getChunksConcatUnfoldCountLines inh
208-------------------------------------------------------------------------------
209-- reduce after grouping in chunks
210-------------------------------------------------------------------------------
212chunksOfSum :: Int -> Handle -> IO Int
213chunksOfSum n inh = S.length $ S.chunksOf n FL.sum (S.unfold FH.read inh)
215foldManyPostChunksOfSum :: Int -> Handle -> IO Int
216foldManyPostChunksOfSum n inh =
217 S.length $ IP.foldManyPost (FL.take n FL.sum) (S.unfold FH.read inh)
219foldManyChunksOfSum :: Int -> Handle -> IO Int
220foldManyChunksOfSum n inh =
221 S.length $ IP.foldMany (FL.take n FL.sum) (S.unfold FH.read inh)
223-- XXX investigate why we need an INLINE in this case (GHC)
224-- Even though allocations remain the same in both cases inlining improves time
226-- | Slice in chunks of size n and get the count of chunks.
227{-# INLINE chunksOf #-}
228chunksOf :: Int -> Handle -> IO Int
230 -- writeNUnsafe gives 2.5x boost here over writeN.
231 S.length $ S.chunksOf n (AT.writeNUnsafe n) (S.unfold FH.read inh)
234inspect $ hasNoTypeClasses 'chunksOf
235inspect $ 'chunksOf `hasNoType` ''Step
236inspect $ 'chunksOf `hasNoType` ''FoldMany
237inspect $ 'chunksOf `hasNoType` ''AT.ArrayUnsafe -- AT.writeNUnsafe
238inspect $ 'chunksOf `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
239inspect $ 'chunksOf `hasNoType` ''MA.ReadUState -- FH.read/A.read
242{-# INLINE arraysOf #-}
243arraysOf :: Int -> Handle -> IO Int
244arraysOf n inh = S.length $ IP.arraysOf n (S.unfold FH.read inh)
246o_1_space_reduce_read_grouped :: BenchEnv -> [Benchmark]
247o_1_space_reduce_read_grouped env =
248 [ bgroup "reduce/read/chunks"
249 [ mkBench ("S.chunksOf " ++ show (bigSize env) ++ " FL.sum") env $
251 chunksOfSum (bigSize env) inh
252 , mkBench "S.chunksOf 1 FL.sum" env $ \inh _ ->
255 -- XXX investigate why we need inline/noinline in these cases (GHC)
256 -- Chunk using parsers
258 ("S.foldManyPost (FL.take " ++ show (bigSize env) ++ " FL.sum)")
260 $ \inh _ -> noinline foldManyPostChunksOfSum (bigSize env) inh
262 "S.foldManyPost (FL.take 1 FL.sum)"
264 $ \inh _ -> inline foldManyPostChunksOfSum 1 inh
266 ("S.foldMany (FL.take " ++ show (bigSize env) ++ " FL.sum)")
268 $ \inh _ -> noinline foldManyChunksOfSum (bigSize env) inh
270 "S.foldMany (FL.take 1 FL.sum)"
272 $ \inh _ -> inline foldManyChunksOfSum 1 inh
274 -- folding chunks to arrays
275 , mkBenchSmall "S.chunksOf 1" env $ \inh _ ->
277 , mkBench "S.chunksOf 10" env $ \inh _ ->
279 , mkBench "S.chunksOf 1000" env $ \inh _ ->
282 -- arraysOf may use a different impl than chunksOf
283 , mkBenchSmall "S.arraysOf 1" env $ \inh _ ->
285 , mkBench "S.arraysOf 10" env $ \inh _ ->
287 , mkBench "S.arraysOf 1000" env $ \inh _ ->
292allBenchmarks :: BenchEnv -> [Benchmark]
293allBenchmarks env = Prelude.concat
294 [ o_1_space_reduce_read env
295 , o_1_space_reduce_toBytes env
296 , o_1_space_reduce_read_grouped env
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