on 5d096df help sign in
305 lines 10 kB history raw
1{-# OPTIONS_GHC -Wno-deprecations #-}
3-- |
4-- Module : Streamly.Benchmark.FileSystem.Handle
5-- Copyright : (c) 2019 Composewell Technologies
6-- License : BSD-3-Clause
7-- Maintainer : streamly@composewell.com
8-- Stability : experimental
9-- Portability : GHC
11{-# LANGUAGE CPP #-}
12{-# LANGUAGE ScopedTypeVariables #-}
14#ifdef __HADDOCK_VERSION__
15#undef INSPECTION
16#endif
18#ifdef INSPECTION
19{-# LANGUAGE TemplateHaskell #-}
20{-# OPTIONS_GHC -fplugin Test.Inspection.Plugin #-}
21#endif
23module Handle.Read
24 (allBenchmarks)
25where
27import Data.Word (Word8)
28import GHC.Magic (inline)
29import GHC.Magic (noinline)
30import System.IO (Handle)
32import qualified Streamly.Data.Stream as Stream
33import qualified Streamly.Data.Fold as Fold
34import qualified Streamly.FileSystem.Handle as FH
35import qualified Streamly.Internal.Data.Array as A
36import qualified Streamly.Internal.Data.Fold as FL
37import qualified Streamly.Internal.Data.Stream as IP
38import qualified Streamly.Internal.FileSystem.Handle as IFH
39import qualified Streamly.Internal.Unicode.Stream as IUS
40import qualified Streamly.Data.Stream.Prelude as S
41import qualified Streamly.Unicode.Stream as SS
43import Test.Tasty.Bench hiding (env)
44import Prelude hiding (last, length)
45import Streamly.Benchmark.Common.Handle
47#ifdef INSPECTION
48import Streamly.Internal.Data.Stream (Step(..), FoldMany)
50import qualified Streamly.Internal.Data.MutArray as MutArray
51import qualified Streamly.Internal.Data.Stream as D
52import qualified Streamly.Internal.Data.Unfold as IUF
54import Test.Inspection
55#endif
57-- TBD reading with unfold
59-------------------------------------------------------------------------------
60-- unfold read
61-------------------------------------------------------------------------------
63-- | Get the last byte from a file bytestream.
64readLast :: Handle -> IO (Maybe Word8)
65readLast = S.fold Fold.last . S.unfold FH.reader
67#ifdef INSPECTION
68inspect $ hasNoTypeClasses 'readLast
69inspect $ 'readLast `hasNoType` ''Step -- S.unfold
70inspect $ 'readLast `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
71inspect $ 'readLast `hasNoType` ''MutArray.ArrayUnsafe -- FH.read/A.read
72#endif
74-- assert that flattenArrays constructors are not present
75-- | Count the number of bytes in a file.
76readCountBytes :: Handle -> IO Int
77readCountBytes = S.fold Fold.length . S.unfold FH.reader
79#ifdef INSPECTION
80inspect $ hasNoTypeClasses 'readCountBytes
81inspect $ 'readCountBytes `hasNoType` ''Step -- S.unfold
82inspect $ 'readCountBytes `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
83inspect $ 'readCountBytes `hasNoType` ''MutArray.ArrayUnsafe -- FH.read/A.read
84#endif
86-- | Count the number of lines in a file.
87readCountLines :: Handle -> IO Int
88readCountLines =
89 S.fold Fold.length
90 . IUS.lines FL.drain
91 . SS.decodeLatin1
92 . S.unfold FH.reader
94#ifdef INSPECTION
95inspect $ hasNoTypeClasses 'readCountLines
96inspect $ 'readCountLines `hasNoType` ''Step
97inspect $ 'readCountLines `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
98inspect $ 'readCountLines `hasNoType` ''MutArray.ArrayUnsafe -- FH.read/A.read
99#endif
101-- | Count the number of words in a file.
102readCountWords :: Handle -> IO Int
103readCountWords =
104 S.fold Fold.length
105 . IUS.words FL.drain
106 . SS.decodeLatin1
107 . S.unfold FH.reader
109#ifdef INSPECTION
110inspect $ hasNoTypeClasses 'readCountWords
111-- inspect $ 'readCountWords `hasNoType` ''Step
112#endif
114-- | Sum the bytes in a file.
115readSumBytes :: Handle -> IO Word8
116readSumBytes = S.fold Fold.sum . S.unfold FH.reader
118#ifdef INSPECTION
119inspect $ hasNoTypeClasses 'readSumBytes
120inspect $ 'readSumBytes `hasNoType` ''Step
121inspect $ 'readSumBytes `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
122inspect $ 'readSumBytes `hasNoType` ''MutArray.ArrayUnsafe -- FH.read/A.read
123#endif
125-- XXX When we mark this with INLINE and we have two benchmarks using S.drain
126-- in one benchmark group then somehow GHC ends up delaying the inlining of
127-- readDrain. Since S.drain has an INLINE[2] for proper rule firing, that does
128-- not work well because of delyaed inlining and the code does not fuse. We
129-- need some way of propagating the inline phase information up so that we can
130-- expedite inlining of the callers too automatically. The minimal example for
131-- the problem can be created by using just two benchmarks in a bench group
132-- both using "readDrain". Either GHC should be fixed or we can use
133-- fusion-plugin to propagate INLINE phase information such that this problem
134-- does not occur.
135readDrain :: Handle -> IO ()
136readDrain inh = S.fold Fold.drain $ S.unfold FH.reader inh
138-- XXX investigate why we need an INLINE in this case (GHC)
139{-# INLINE readDecodeLatin1 #-}
140readDecodeLatin1 :: Handle -> IO ()
141readDecodeLatin1 inh =
142 S.fold Fold.drain
143 $ SS.decodeLatin1
144 $ S.unfold FH.reader inh
146readDecodeUtf8 :: Handle -> IO ()
147readDecodeUtf8 inh =
148 S.fold Fold.drain
149 $ SS.decodeUtf8
150 $ S.unfold FH.reader inh
152#ifdef INSPECTION
153inspect $ hasNoTypeClasses 'readDecodeUtf8
154-- inspect $ 'readDecodeUtf8Lax `hasNoType` ''Step
155#endif
157o_1_space_reduce_read :: BenchEnv -> [Benchmark]
158o_1_space_reduce_read env =
159 [ bgroup "reduce/read"
160 [ -- read raw bytes without any decoding
161 mkBench "S.drain" env $ \inh _ ->
162 readDrain inh
163 , mkBench "S.last" env $ \inh _ ->
164 readLast inh
165 , mkBench "S.sum" env $ \inh _ ->
166 readSumBytes inh
168 -- read with Latin1 decoding
169 , mkBench "SS.decodeLatin1" env $ \inh _ ->
170 readDecodeLatin1 inh
171 , mkBench "S.length" env $ \inh _ ->
172 readCountBytes inh
173 , mkBench "US.lines . SS.decodeLatin1" env $ \inh _ ->
174 readCountLines inh
175 , mkBench "US.words . SS.decodeLatin1" env $ \inh _ ->
176 readCountWords inh
178 -- read with utf8 decoding
179 , mkBenchSmall "SS.decodeUtf8" env $ \inh _ ->
180 readDecodeUtf8 inh
181 ]
182 ]
184-------------------------------------------------------------------------------
185-- stream toBytes
186-------------------------------------------------------------------------------
188-- | Count the number of lines in a file.
189getChunksConcatUnfoldCountLines :: Handle -> IO Int
190getChunksConcatUnfoldCountLines inh =
191 S.fold Fold.length
192 $ IUS.lines FL.drain
193 $ SS.decodeLatin1
194 -- XXX replace with toBytes
195 $ S.unfoldMany A.reader (IFH.readChunks inh)
197#ifdef INSPECTION
198inspect $ hasNoTypeClasses 'getChunksConcatUnfoldCountLines
199inspect $ 'getChunksConcatUnfoldCountLines `hasNoType` ''Step
200inspect $ 'getChunksConcatUnfoldCountLines `hasNoType` ''D.ConcatMapUState
201#endif
203o_1_space_reduce_toBytes :: BenchEnv -> [Benchmark]
204o_1_space_reduce_toBytes env =
205 [ bgroup "reduce/toBytes"
206 [ mkBench "US.lines . SS.decodeLatin1" env $ \inh _ ->
207 getChunksConcatUnfoldCountLines inh
208 ]
209 ]
211-------------------------------------------------------------------------------
212-- reduce after grouping in chunks
213-------------------------------------------------------------------------------
215chunksOfSum :: Int -> Handle -> IO Int
216chunksOfSum n inh =
217 S.fold Fold.length $ IP.groupsOf n FL.sum (S.unfold FH.reader inh)
219foldManyPostChunksOfSum :: Int -> Handle -> IO Int
220foldManyPostChunksOfSum n inh =
221 S.fold Fold.length
222 $ IP.foldManyPost (FL.take n FL.sum) (S.unfold FH.reader inh)
224foldManyChunksOfSum :: Int -> Handle -> IO Int
225foldManyChunksOfSum n inh =
226 S.fold Fold.length
227 $ IP.foldMany (FL.take n FL.sum) (S.unfold FH.reader inh)
229-- XXX investigate why we need an INLINE in this case (GHC)
230-- Even though allocations remain the same in both cases inlining improves time
231-- by 4x.
232-- | Slice in chunks of size n and get the count of chunks.
233{-# INLINE groupsOf #-}
234groupsOf :: Int -> Handle -> IO Int
235groupsOf n inh =
236 -- writeNUnsafe gives 2.5x boost here over writeN.
237 S.fold Fold.length
238 $ IP.groupsOf n (A.writeNUnsafe n) (S.unfold FH.reader inh)
240#ifdef INSPECTION
241inspect $ hasNoTypeClasses 'groupsOf
242inspect $ 'groupsOf `hasNoType` ''Step
243inspect $ 'groupsOf `hasNoType` ''FoldMany
244inspect $ 'groupsOf `hasNoType` ''MutArray.ArrayUnsafe -- AT.writeNUnsafe
245 -- FH.read/A.read
246inspect $ 'groupsOf `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
247#endif
249{-# INLINE chunksOf #-}
250chunksOf :: Int -> Handle -> IO Int
251chunksOf n inh =
252 S.fold Fold.length $ Stream.chunksOf n (S.unfold FH.reader inh)
254o_1_space_reduce_read_grouped :: BenchEnv -> [Benchmark]
255o_1_space_reduce_read_grouped env =
256 [ bgroup "reduce/read/chunks"
257 [ mkBench ("S.groupsOf " ++ show (bigSize env) ++ " FL.sum") env $
258 \inh _ ->
259 chunksOfSum (bigSize env) inh
260 , mkBench "S.groupsOf 1 FL.sum" env $ \inh _ ->
261 chunksOfSum 1 inh
263 -- XXX investigate why we need inline/noinline in these cases (GHC)
264 -- Chunk using parsers
265 , mkBench
266 ("S.foldManyPost (FL.take " ++ show (bigSize env) ++ " FL.sum)")
267 env
268 $ \inh _ -> noinline foldManyPostChunksOfSum (bigSize env) inh
269 , mkBench
270 "S.foldManyPost (FL.take 1 FL.sum)"
271 env
272 $ \inh _ -> inline foldManyPostChunksOfSum 1 inh
273 , mkBench
274 ("S.foldMany (FL.take " ++ show (bigSize env) ++ " FL.sum)")
275 env
276 $ \inh _ -> noinline foldManyChunksOfSum (bigSize env) inh
277 , mkBench
278 "S.foldMany (FL.take 1 FL.sum)"
279 env
280 $ \inh _ -> inline foldManyChunksOfSum 1 inh
282 -- folding chunks to arrays
283 , mkBenchSmall "S.groupsOf 1" env $ \inh _ ->
284 groupsOf 1 inh
285 , mkBench "S.groupsOf 10" env $ \inh _ ->
286 groupsOf 10 inh
287 , mkBench "S.groupsOf 1000" env $ \inh _ ->
288 groupsOf 1000 inh
290 -- chunksOf may use a different impl than groupsOf
291 , mkBenchSmall "S.chunksOf 1" env $ \inh _ ->
292 chunksOf 1 inh
293 , mkBench "S.chunksOf 10" env $ \inh _ ->
294 chunksOf 10 inh
295 , mkBench "S.chunksOf 1000" env $ \inh _ ->
296 chunksOf 1000 inh
297 ]
298 ]
300allBenchmarks :: BenchEnv -> [Benchmark]
301allBenchmarks env = Prelude.concat
302 [ o_1_space_reduce_read env
303 , o_1_space_reduce_toBytes env
304 , o_1_space_reduce_read_grouped env
305 ]