@@ -0,0 +1,1351 @@
2+-- Module : Streamly.Benchmark.FileSystem.Handle
3+-- Copyright : (c) 2019 Composewell Technologies
6+-- Maintainer : streamly@composewell.com
7+-- Stability : experimental
11+{-# LANGUAGE ScopedTypeVariables #-}
13+#ifdef __HADDOCK_VERSION__
18+{-# LANGUAGE TemplateHaskell #-}
19+{-# OPTIONS_GHC -fplugin Test.Inspection.Plugin #-}
22+import Control.DeepSeq (NFData)
23+import Control.Exception (SomeException)
24+import Data.Char (ord, chr)
25+import Data.Functor.Identity (runIdentity)
26+import Data.Word (Word8)
27+import System.Directory (getFileSize)
28+import System.Environment (lookupEnv)
29+import System.IO (openFile, IOMode(..), Handle, hClose)
30+import System.Process.Typed (shell, runProcess_)
31+import Prelude hiding (last, length)
33+import qualified Streamly.Data.Fold as FL
34+import qualified Streamly.Data.Unicode.Stream as SS
35+import qualified Streamly.FileSystem.Handle as FH
36+-- import qualified Streamly.Internal.Data.Fold as IFL
37+import qualified Streamly.Internal.Data.Parser as PR
38+import qualified Streamly.Internal.Data.Stream.StreamD as D
39+import qualified Streamly.Internal.Data.Unfold as IUF
40+import qualified Streamly.Internal.Data.Unicode.Stream as IUS
41+import qualified Streamly.Internal.FileSystem.Handle as IFH
42+import qualified Streamly.Internal.Memory.Array as IA
43+import qualified Streamly.Internal.Memory.Array.Types as AT
44+import qualified Streamly.Internal.Memory.ArrayStream as AS
45+import qualified Streamly.Internal.Memory.Unicode.Array as IUA
46+import qualified Streamly.Internal.Prelude as IP
47+import qualified Streamly.Memory.Array as A
48+import qualified Streamly.Prelude as S
51+import Gauge hiding (env)
52+import Streamly.Benchmark.Common
55+import Foreign.Storable (Storable)
56+import Streamly.Internal.Data.Stream.StreamD.Type (Step(..), GroupState)
57+import Test.Inspection
60+-------------------------------------------------------------------------------
62+-------------------------------------------------------------------------------
65+moduleName = "FileSystem.Handle"
67+-------------------------------------------------------------------------------
69+-------------------------------------------------------------------------------
72+scratchDir = "benchmark-tmp"
75+outfile = scratchDir ++ "out.txt"
78+smallFileSize = 10 * 1024 * 1024
81+inFileSmall = scratchDir ++ "in-10MB.txt"
84+bigFileSize = 100 * 1024 * 1024
87+inFileBig = scratchDir ++ "in-100MB.txt"
92+blockCount :: Int -> Int
93+blockCount size = (size + blockSize - 1) `div` blockSize
95+data Handles = Handles Handle Handle
97+mkBench :: NFData b => String -> IORef Handles -> IO b -> Benchmark
98+mkBench name href action =
99+ bench name $ perRunEnv (do
100+ Handles inH outH <- readIORef href
102+ -- close old handles
107+ inh <- openFile inFileBig ReadMode
108+ outh <- openFile outfile WriteMode
111+ writeIORef href $ Handles inh outh
115+-------------------------------------------------------------------------------
116+-- read chunked using toChunks
117+-------------------------------------------------------------------------------
119+-- | Get the last byte from a file bytestream.
120+{-# INLINE toChunksLast #-}
121+toChunksLast :: Handle -> IO (Maybe Word8)
122+toChunksLast inh = do
123+ let s = IFH.toChunks inh
125+ return $ case larr of
127+ Just arr -> IA.readIndex arr (A.length arr - 1)
130+inspect $ hasNoTypeClasses 'toChunksLast
131+inspect $ 'toChunksLast `hasNoType` ''Step
134+-- | Count the number of bytes in a file.
135+{-# INLINE toChunksSumLengths #-}
136+toChunksSumLengths :: Handle -> IO Int
137+toChunksSumLengths inh =
138+ let s = IFH.toChunks inh
139+ in S.sum (S.map A.length s)
142+inspect $ hasNoTypeClasses 'toChunksSumLengths
143+inspect $ 'toChunksSumLengths `hasNoType` ''Step
146+-- | Count the number of lines in a file.
147+{-# INLINE toChunksSplitOnSuffix #-}
148+toChunksSplitOnSuffix :: Handle -> IO Int
149+toChunksSplitOnSuffix = S.length . AS.splitOnSuffix 10 . IFH.toChunks
152+inspect $ hasNoTypeClasses 'toChunksSplitOnSuffix
153+inspect $ 'toChunksSplitOnSuffix `hasNoType` ''Step
156+-- XXX use a word splitting combinator instead of splitOn and test it.
157+-- | Count the number of words in a file.
158+{-# INLINE toChunksSplitOn #-}
159+toChunksSplitOn :: Handle -> IO Int
160+toChunksSplitOn = S.length . AS.splitOn 32 . IFH.toChunks
163+inspect $ hasNoTypeClasses 'toChunksSplitOn
164+inspect $ 'toChunksSplitOn `hasNoType` ''Step
167+-- | Sum the bytes in a file.
168+{-# INLINE toChunksCountBytes #-}
169+toChunksCountBytes :: Handle -> IO Word8
170+toChunksCountBytes inh = do
171+ let foldlArr' f z = runIdentity . S.foldl' f z . IA.toStream
172+ let s = IFH.toChunks inh
173+ S.foldl' (\acc arr -> acc + foldlArr' (+) 0 arr) 0 s
176+inspect $ hasNoTypeClasses 'toChunksCountBytes
177+inspect $ 'toChunksCountBytes `hasNoType` ''Step
180+{-# INLINE toChunksWithBufferOfDecodeUtf8ArraysLenient #-}
181+toChunksWithBufferOfDecodeUtf8ArraysLenient :: Handle -> IO ()
182+toChunksWithBufferOfDecodeUtf8ArraysLenient inh =
184+ $ IUS.decodeUtf8ArraysLenient
185+ $ IFH.toChunksWithBufferOf (1024*1024) inh
188+inspect $ hasNoTypeClasses 'decodeUtf8Lenient
189+-- inspect $ 'decodeUtf8Lenient `hasNoType` ''Step
190+-- inspect $ 'decodeUtf8Lenient `hasNoType` ''AT.FlattenState
191+-- inspect $ 'decodeUtf8Lenient `hasNoType` ''D.ConcatMapUState
194+o_1_space_read_chunked :: IORef Handles -> Handle -> Int -> [Benchmark]
195+o_1_space_read_chunked href _ _ =
196+ -- read using toChunks instead of read
197+ [ bgroup "reduce/toChunks"
198+ [ mkBench "S.last (32K)" href $ do
199+ Handles inh _ <- readIORef href
201+ -- Note: this cannot be fairly compared with GNU wc -c or wc -m as
202+ -- wc uses lseek to just determine the file size rather than reading
203+ -- and counting characters.
204+ , mkBench "S.sum . S.map A.length (32K)" href $ do
205+ Handles inh _ <- readIORef href
206+ toChunksSumLengths inh
207+ , mkBench "AS.splitOnSuffix (32K)" href $ do
208+ Handles inh _ <- readIORef href
209+ toChunksSplitOnSuffix inh
210+ , mkBench "AS.splitOn (32K)" href $ do
211+ Handles inh _ <- readIORef href
213+ , mkBench "countBytes (32K)" href $ do
214+ Handles inh _ <- readIORef href
215+ toChunksCountBytes inh
216+ , mkBench "US.decodeUtf8ArraysLenient (1MB)" href $ do
217+ Handles inh _ <- readIORef href
218+ toChunksWithBufferOfDecodeUtf8ArraysLenient inh
222+-- TBD reading with unfold
224+-------------------------------------------------------------------------------
226+-------------------------------------------------------------------------------
228+-- | Send the file contents to /dev/null
229+{-# INLINE toChunksWithBufferOf #-}
230+toChunksWithBufferOf :: Handle -> Handle -> IO ()
231+toChunksWithBufferOf devNull inh =
232+ S.fold (IFH.writeChunks devNull) $ IFH.toChunksWithBufferOf (256*1024) inh
235+inspect $ hasNoTypeClasses 'toChunksWithBufferOf
236+inspect $ 'toChunksWithBufferOf `hasNoType` ''Step
240+{-# INLINE copyChunks #-}
241+copyChunks :: Handle -> Handle -> IO ()
242+copyChunks inh outh =
243+ let s = IFH.toChunks inh
244+ in S.fold (IFH.writeChunks outh) s
247+inspect $ hasNoTypeClasses 'toChunksWriteChunks
248+inspect $ 'toChunksWriteChunks `hasNoType` ''Step
252+{-# INLINE copyCodecUtf8ArraysLenient #-}
253+copyCodecUtf8ArraysLenient :: Handle -> Handle -> IO ()
254+copyCodecUtf8ArraysLenient inh outh =
255+ S.fold (FH.write outh)
257+ $ IUS.decodeUtf8ArraysLenient
258+ $ IFH.toChunksWithBufferOf (1024*1024) inh
261+inspect $ hasNoTypeClasses 'copyCodecUtf8ArraysLenient
262+-- inspect $ 'copyCodecUtf8ArraysLenient `hasNoType` ''Step
263+-- inspect $ 'copyCodecUtf8ArraysLenient `hasNoType` ''AT.FlattenState
264+-- inspect $ 'copyCodecUtf8ArraysLenient `hasNoType` ''D.ConcatMapUState
267+o_1_space_copy_chunked :: IORef Handles -> Handle -> Int -> [Benchmark]
268+o_1_space_copy_chunked href devNull _ =
269+ [ bgroup "copy/toChunks"
270+ [ mkBench "toNull (256K)" href $ do
271+ Handles inh _ <- readIORef href
272+ toChunksWithBufferOf devNull inh
273+ , mkBench "raw" href $ do
274+ Handles inh outh <- readIORef href
276+ , mkBench "decodeEncodeUtf8Lenient" href $ do
277+ Handles inh outh <- readIORef href
278+ copyCodecUtf8ArraysLenient inh outh
282+-- TBD reading with unfold
284+-------------------------------------------------------------------------------
285+-- Exceptions readChunks
286+-------------------------------------------------------------------------------
288+-- | Send the file contents to /dev/null with exception handling
289+{-# INLINE readChunksWithBufferOfOnExceptionUnfold #-}
290+readChunksWithBufferOfOnExceptionUnfold :: Handle -> Handle -> IO ()
291+readChunksWithBufferOfOnExceptionUnfold devNull inh =
292+ let readEx = IUF.onException (\_ -> hClose inh)
293+ (IUF.supplyFirst FH.readChunksWithBufferOf (256*1024))
294+ in IUF.fold readEx (IFH.writeChunks devNull) inh
297+inspect $ hasNoTypeClasses 'readChunksWithBufferOfOnExceptionUnfold
298+-- inspect $ 'readChunksWithBufferOfOnExceptionUnfold `hasNoType` ''Step
301+-- | Send the file contents to /dev/null with exception handling
302+{-# INLINE readChunksWithBufferOfBracketUnfold #-}
303+readChunksWithBufferOfBracketUnfold :: Handle -> Handle -> IO ()
304+readChunksWithBufferOfBracketUnfold devNull inh =
305+ let readEx = IUF.bracket return (\_ -> hClose inh)
306+ (IUF.supplyFirst FH.readChunksWithBufferOf (256*1024))
307+ in IUF.fold readEx (IFH.writeChunks devNull) inh
310+inspect $ hasNoTypeClasses 'readChunksWithBufferOfBracketUnfold
311+-- inspect $ 'readChunksWithBufferOfBracketUnfold `hasNoType` ''Step
314+{-# INLINE readChunksWithBufferOfBracketIOUnfold #-}
315+readChunksWithBufferOfBracketIOUnfold :: Handle -> Handle -> IO ()
316+readChunksWithBufferOfBracketIOUnfold devNull inh =
317+ let readEx = IUF.bracketIO return (\_ -> hClose inh)
318+ (IUF.supplyFirst FH.readChunksWithBufferOf (256*1024))
319+ in IUF.fold readEx (IFH.writeChunks devNull) inh
321+o_1_space_copy_exceptions_readChunks :: IORef Handles -> Handle -> Int -> [Benchmark]
322+o_1_space_copy_exceptions_readChunks href devNull _ =
323+ [ bgroup "copy/exceptions/unfold/readChunks"
324+ [ mkBench "onException (256K)" href $ do
325+ Handles inh _ <- readIORef href
326+ readChunksWithBufferOfOnExceptionUnfold devNull inh
327+ , mkBench "bracket (256K)" href $ do
328+ Handles inh _ <- readIORef href
329+ readChunksWithBufferOfBracketUnfold devNull inh
330+ , mkBench "bracketIO (256K)" href $ do
331+ Handles inh _ <- readIORef href
332+ readChunksWithBufferOfBracketIOUnfold devNull inh
336+-------------------------------------------------------------------------------
337+-- Exceptions toChunks
338+-------------------------------------------------------------------------------
340+-- | Send the file contents to /dev/null with exception handling
341+{-# INLINE toChunksWithBufferOfBracketStream #-}
342+toChunksWithBufferOfBracketStream :: Handle -> Handle -> IO ()
343+toChunksWithBufferOfBracketStream devNull inh =
344+ let readEx = S.bracket (return ()) (\_ -> hClose inh)
345+ (\_ -> IFH.toChunksWithBufferOf (256*1024) inh)
346+ in S.fold (IFH.writeChunks devNull) $ readEx
349+inspect $ hasNoTypeClasses 'toChunksWithBufferOfBracketStream
350+-- inspect $ 'toChunksWithBufferOfBracketStream `hasNoType` ''Step
353+{-# INLINE toChunksWithBufferOfBracketIOStream #-}
354+toChunksWithBufferOfBracketIOStream :: Handle -> Handle -> IO ()
355+toChunksWithBufferOfBracketIOStream devNull inh =
356+ let readEx = IP.bracketIO (return ()) (\_ -> hClose inh)
357+ (\_ -> IFH.toChunksWithBufferOf (256*1024) inh)
358+ in S.fold (IFH.writeChunks devNull) $ readEx
360+o_1_space_copy_exceptions_toChunks :: IORef Handles -> Handle -> Int -> [Benchmark]
361+o_1_space_copy_exceptions_toChunks href devNull _ =
362+ [ bgroup "copy/exceptions/stream/toChunks"
363+ [ mkBench "bracket (256K)" href $ do
364+ Handles inh _ <- readIORef href
365+ toChunksWithBufferOfBracketStream devNull inh
366+ , mkBench "bracketIO (256K)" href $ do
367+ Handles inh _ <- readIORef href
368+ toChunksWithBufferOfBracketIOStream devNull inh
372+-------------------------------------------------------------------------------
374+-------------------------------------------------------------------------------
376+-- | Get the last byte from a file bytestream.
377+{-# INLINE readLast #-}
378+readLast :: Handle -> IO (Maybe Word8)
379+readLast = S.last . S.unfold FH.read
382+inspect $ hasNoTypeClasses 'readLast
383+inspect $ 'readLast `hasNoType` ''Step
384+inspect $ 'readLast `hasNoType` ''AT.FlattenState
385+inspect $ 'readLast `hasNoType` ''D.ConcatMapUState
388+-- assert that flattenArrays constructors are not present
389+-- | Count the number of bytes in a file.
390+{-# INLINE readCountBytes #-}
391+readCountBytes :: Handle -> IO Int
392+readCountBytes = S.length . S.unfold FH.read
395+inspect $ hasNoTypeClasses 'countBytes
396+inspect $ 'countBytes `hasNoType` ''Step
397+inspect $ 'countBytes `hasNoType` ''AT.FlattenState
398+inspect $ 'countBytes `hasNoType` ''D.ConcatMapUState
401+-- | Count the number of lines in a file.
402+{-# INLINE readCountLines #-}
403+readCountLines :: Handle -> IO Int
406+ . IUS.lines FL.drain
411+inspect $ hasNoTypeClasses 'readCountLines
412+inspect $ 'readCountLines `hasNoType` ''Step
413+inspect $ 'readCountLines `hasNoType` ''AT.FlattenState
414+inspect $ 'readCountLines `hasNoType` ''D.ConcatMapUState
417+-- | Count the number of words in a file.
418+{-# INLINE readCountWords #-}
419+readCountWords :: Handle -> IO Int
422+ . IUS.words FL.drain
427+inspect $ hasNoTypeClasses 'readCountWords
428+-- inspect $ 'readCountWords `hasNoType` ''Step
429+-- inspect $ 'readCountWords `hasNoType` ''D.ConcatMapUState
432+-- | Sum the bytes in a file.
433+{-# INLINE readSumBytes #-}
434+readSumBytes :: Handle -> IO Word8
435+readSumBytes = S.sum . S.unfold FH.read
438+inspect $ hasNoTypeClasses 'readSumBytes
439+inspect $ 'readSumBytes `hasNoType` ''Step
440+inspect $ 'readSumBytes `hasNoType` ''AT.FlattenState
441+inspect $ 'readSumBytes `hasNoType` ''D.ConcatMapUState
444+{-# INLINE readDrain #-}
445+readDrain :: Handle -> IO ()
446+readDrain inh = S.drain $ S.unfold FH.read inh
448+{-# INLINE readDecodeLatin1 #-}
449+readDecodeLatin1 :: Handle -> IO ()
450+readDecodeLatin1 inh =
453+ $ S.unfold FH.read inh
455+{-# INLINE readDecodeUtf8Lax #-}
456+readDecodeUtf8Lax :: Handle -> IO ()
457+readDecodeUtf8Lax inh =
460+ $ S.unfold FH.read inh
463+inspect $ hasNoTypeClasses 'readDecodeUtf8Lax
464+-- inspect $ 'readDecodeUtf8Lax `hasNoType` ''Step
465+-- inspect $ 'readDecodeUtf8Lax `hasNoType` ''AT.FlattenState
466+-- inspect $ 'readDecodeUtf8Lax `hasNoType` ''D.ConcatMapUState
469+o_1_space_reduce_read :: IORef Handles -> Handle -> Int -> [Benchmark]
470+o_1_space_reduce_read href _ _ =
471+ [ bgroup "reduce/read"
472+ [ -- read raw bytes without any decoding
473+ mkBench "S.drain" href $ do
474+ Handles inh _ <- readIORef href
476+ , mkBench "S.last" href $ do
477+ Handles inh _ <- readIORef href
479+ , mkBench "S.sum" href $ do
480+ Handles inh _ <- readIORef href
483+ -- read with Latin1 decoding
484+ , mkBench "SS.decodeLatin1" href $ do
485+ Handles inh _ <- readIORef href
486+ readDecodeLatin1 inh
487+ , mkBench "S.length" href $ do
488+ Handles inh _ <- readIORef href
490+ , mkBench "US.lines . SS.decodeLatin1" href $ do
491+ Handles inh _ <- readIORef href
493+ , mkBench "US.words . SS.decodeLatin1" href $ do
494+ Handles inh _ <- readIORef href
497+ -- read with utf8 decoding
498+ , mkBench "SS.decodeUtf8Lax" href $ do
499+ Handles inh _ <- readIORef href
500+ readDecodeUtf8Lax inh
504+-------------------------------------------------------------------------------
506+-------------------------------------------------------------------------------
508+-- | Count the number of lines in a file.
509+{-# INLINE toChunksConcatUnfoldCountLines #-}
510+toChunksConcatUnfoldCountLines :: Handle -> IO Int
511+toChunksConcatUnfoldCountLines inh =
513+ $ IUS.lines FL.drain
515+ -- XXX replace with toBytes
516+ $ S.concatUnfold A.read (IFH.toChunks inh)
519+inspect $ hasNoTypeClasses 'countLinesU
520+inspect $ 'countLinesU `hasNoType` ''Step
521+inspect $ 'countLinesU `hasNoType` ''D.ConcatMapUState
524+o_1_space_reduce_toBytes :: IORef Handles -> Handle -> Int -> [Benchmark]
525+o_1_space_reduce_toBytes href _ _ =
526+ [ bgroup "reduce/toBytes"
527+ [ mkBench "US.lines . SS.decodeLatin1" href $ do
528+ Handles inh _ <- readIORef href
529+ toChunksConcatUnfoldCountLines inh
533+-------------------------------------------------------------------------------
534+-- reduce after grouping in chunks
535+-------------------------------------------------------------------------------
537+{-# INLINE chunksOfSum #-}
538+chunksOfSum :: Int -> Handle -> IO Int
539+chunksOfSum n inh = S.length $ S.chunksOf n FL.sum (S.unfold FH.read inh)
541+{-# INLINE parseManyChunksOfSum #-}
542+parseManyChunksOfSum :: Int -> Handle -> IO Int
543+parseManyChunksOfSum n inh =
544+ S.length $ IP.parseMany (PR.take n FL.sum) (S.unfold FH.read inh)
546+-- | Slice in chunks of size n and get the count of chunks.
547+{-# INLINE chunksOf #-}
548+chunksOf :: Int -> Handle -> IO Int
550+ -- writeNUnsafe gives 2.5x boost here over writeN.
551+ -- XXX replace with S.arraysOf
552+ S.length $ S.chunksOf n (AT.writeNUnsafe n) (S.unfold FH.read inh)
555+inspect $ hasNoTypeClasses 'chunksOf
556+inspect $ 'chunksOf `hasNoType` ''Step
557+inspect $ 'chunksOf `hasNoType` ''AT.FlattenState
558+inspect $ 'chunksOf `hasNoType` ''D.ConcatMapUState
559+inspect $ 'chunksOf `hasNoType` ''GroupState
562+-- This is to make sure that the concatMap in FH.read, groupsOf and foldlM'
563+-- together can fuse.
565+-- | Slice in chunks of size n and get the count of chunks.
566+{-# INLINE _chunksOfD #-}
567+_chunksOfD :: Int -> Handle -> IO Int
569+ D.foldlM' (\i _ -> return $ i + 1) (return 0)
570+ $ D.groupsOf n (AT.writeNUnsafe n)
571+ $ D.fromStreamK (S.unfold FH.read inh)
574+inspect $ hasNoTypeClasses '_chunksOfD
575+inspect $ '_chunksOfD `hasNoType` ''Step
576+inspect $ '_chunksOfD `hasNoType` ''GroupState
577+inspect $ '_chunksOfD `hasNoType` ''AT.FlattenState
578+inspect $ '_chunksOfD `hasNoType` ''D.ConcatMapUState
581+o_1_space_reduce_read_grouped :: IORef Handles -> Handle -> Int -> [Benchmark]
582+o_1_space_reduce_read_grouped href _ value =
583+ [ bgroup "reduce/read/chunks"
584+ [ mkBench ("S.chunksOf " ++ show value ++ " FL.sum") href $ do
585+ Handles inh _ <- readIORef href
586+ chunksOfSum value inh
587+ , mkBench "S.chunksOf 1 FL.sum" href $ do
588+ Handles inh _ <- readIORef href
591+ -- Chunk using parsers
592+ , mkBench ("S.parseMany (PR.take " ++ show value ++ " FL.sum)")
594+ Handles inh _ <- readIORef href
595+ parseManyChunksOfSum value inh
596+ , mkBench "S.parseMany (PR.take 1 FL.sum)" href $ do
597+ Handles inh _ <- readIORef href
598+ parseManyChunksOfSum 1 inh
600+ -- folding chunks to arrays
601+ , mkBench "S.arraysOf 1" href $ do
602+ Handles inh _ <- readIORef href
604+ , mkBench "S.arraysOf 10" href $ do
605+ Handles inh _ <- readIORef href
607+ , mkBench "S.arraysOf 1000" href $ do
608+ Handles inh _ <- readIORef href
613+-------------------------------------------------------------------------------
615+-------------------------------------------------------------------------------
617+-- | Send the file contents to /dev/null
618+{-# INLINE readWriteNull #-}
619+readWriteNull :: Handle -> Handle -> IO ()
620+readWriteNull devNull inh = S.fold (FH.write devNull) $ S.unfold FH.read inh
623+inspect $ hasNoTypeClasses 'readWriteNull
624+inspect $ 'readWriteNull `hasNoType` ''Step
625+inspect $ 'readWriteNull `hasNoType` ''AT.FlattenState
626+inspect $ 'readWriteNull `hasNoType` ''D.ConcatMapUState
630+{-# INLINE copyStream #-}
631+copyStream :: Handle -> Handle -> IO ()
632+copyStream inh outh = S.fold (FH.write outh) (S.unfold FH.read inh)
635+inspect $ hasNoTypeClasses 'copyStream
636+inspect $ 'copyStream `hasNoType` ''Step
637+inspect $ 'copyStream `hasNoType` ''AT.FlattenState
638+inspect $ 'copyStream `hasNoType` ''D.ConcatMapUState
642+{-# INLINE copyStreamLatin1 #-}
643+copyStreamLatin1 :: Handle -> Handle -> IO ()
644+copyStreamLatin1 inh outh =
645+ S.fold (FH.write outh)
648+ $ S.unfold FH.read inh
651+inspect $ hasNoTypeClasses 'copyStreamLatin1
652+inspect $ 'copyStreamLatin1 `hasNoType` ''Step
653+inspect $ 'copyStreamLatin1 `hasNoType` ''AT.FlattenState
654+inspect $ 'copyStreamLatin1 `hasNoType` ''D.ConcatMapUState
658+{-# INLINE _copyStreamUtf8 #-}
659+_copyStreamUtf8 :: Handle -> Handle -> IO ()
660+_copyStreamUtf8 inh outh =
661+ S.fold (FH.write outh)
664+ $ S.unfold FH.read inh
667+inspect $ hasNoTypeClasses '_copyStreamUtf8
668+-- inspect $ '_copyStreamUtf8 `hasNoType` ''Step
669+-- inspect $ '_copyStreamUtf8 `hasNoType` ''AT.FlattenState
670+-- inspect $ '_copyStreamUtf8 `hasNoType` ''D.ConcatMapUState
674+{-# INLINE copyStreamUtf8Lax #-}
675+copyStreamUtf8Lax :: Handle -> Handle -> IO ()
676+copyStreamUtf8Lax inh outh =
677+ S.fold (FH.write outh)
680+ $ S.unfold FH.read inh
683+inspect $ hasNoTypeClasses 'copyStreamUtf8Lax
684+-- inspect $ 'copyStreamUtf8Lax `hasNoType` ''Step
685+-- inspect $ 'copyStreamUtf8Lax `hasNoType` ''AT.FlattenState
686+-- inspect $ 'copyStreamUtf8Lax `hasNoType` ''D.ConcatMapUState
689+o_1_space_copy_read :: IORef Handles -> Handle -> Int -> [Benchmark]
690+o_1_space_copy_read href devNull _ =
691+ [ bgroup "copy/read"
692+ [ mkBench "rawToNull" href $ do
693+ Handles inh _ <- readIORef href
694+ readWriteNull devNull inh
695+ , mkBench "rawToFile" href $ do
696+ Handles inh outh <- readIORef href
698+ -- This needs an ascii file, as decode just errors out.
699+ , mkBench "SS.encodeLatin1 . SS.decodeLatin1" href $ do
700+ Handles inh outh <- readIORef href
701+ copyStreamLatin1 inh outh
703+ , mkBench "copyUtf8" href $ do
704+ Handles inh outh <- readIORef href
705+ _copyStreamUtf8 inh outh
707+ , mkBench "SS.encodeUtf8 . SS.decodeUtf8Lax" href $ do
708+ Handles inh outh <- readIORef href
709+ copyStreamUtf8Lax inh outh
713+-------------------------------------------------------------------------------
715+-------------------------------------------------------------------------------
717+-- | Send the file contents to /dev/null
718+{-# INLINE readFromBytesNull #-}
719+readFromBytesNull :: Handle -> Handle -> IO ()
720+readFromBytesNull devNull inh = IFH.fromBytes devNull $ S.unfold FH.read inh
723+inspect $ hasNoTypeClasses 'catStreamWrite
724+inspect $ 'catStreamWrite `hasNoType` ''Step
725+inspect $ 'catStreamWrite `hasNoType` ''AT.FlattenState
726+inspect $ 'catStreamWrite `hasNoType` ''D.ConcatMapUState
729+o_1_space_copy_fromBytes :: IORef Handles -> Handle -> Int -> [Benchmark]
730+o_1_space_copy_fromBytes href devNull _ =
731+ [ bgroup "copy/fromBytes"
732+ [ mkBench "rawToNull" href $ do
733+ Handles inh _ <- readIORef href
734+ readFromBytesNull devNull inh
738+-------------------------------------------------------------------------------
740+-------------------------------------------------------------------------------
742+-- | Send the file contents to /dev/null with exception handling
743+{-# INLINE readWriteOnExceptionUnfold #-}
744+readWriteOnExceptionUnfold :: Handle -> Handle -> IO ()
745+readWriteOnExceptionUnfold devNull inh =
746+ let readEx = IUF.onException (\_ -> hClose inh) FH.read
747+ in S.fold (FH.write devNull) $ S.unfold readEx inh
750+inspect $ hasNoTypeClasses 'readWriteOnExceptionUnfold
751+-- inspect $ 'readWriteOnExceptionUnfold `hasNoType` ''Step
752+-- inspect $ 'readWriteOnExceptionUnfold `hasNoType` ''AT.FlattenState
753+-- inspect $ 'readWriteOnExceptionUnfold `hasNoType` ''D.ConcatMapUState
756+-- | Send the file contents to /dev/null with exception handling
757+{-# INLINE readWriteHandleExceptionUnfold #-}
758+readWriteHandleExceptionUnfold :: Handle -> Handle -> IO ()
759+readWriteHandleExceptionUnfold devNull inh =
760+ let handler (_e :: SomeException) = hClose inh >> return 10
761+ readEx = IUF.handle (IUF.singletonM handler) FH.read
762+ in S.fold (FH.write devNull) $ S.unfold readEx inh
765+inspect $ hasNoTypeClasses 'readWriteHandleExceptionUnfold
766+-- inspect $ 'readWriteHandleExceptionUnfold `hasNoType` ''Step
767+-- inspect $ 'readWriteHandleExceptionUnfold `hasNoType` ''AT.FlattenState
768+-- inspect $ 'readWriteHandleExceptionUnfold `hasNoType` ''D.ConcatMapUState
771+-- | Send the file contents to /dev/null with exception handling
772+{-# INLINE readWriteFinallyUnfold #-}
773+readWriteFinallyUnfold :: Handle -> Handle -> IO ()
774+readWriteFinallyUnfold devNull inh =
775+ let readEx = IUF.finally (\_ -> hClose inh) FH.read
776+ in S.fold (FH.write devNull) $ S.unfold readEx inh
779+inspect $ hasNoTypeClasses 'readWriteFinallyUnfold
780+-- inspect $ 'readWriteFinallyUnfold `hasNoType` ''Step
781+-- inspect $ 'readWriteFinallyUnfold `hasNoType` ''AT.FlattenState
782+-- inspect $ 'readWriteFinallyUnfold `hasNoType` ''D.ConcatMapUState
785+{-# INLINE readWriteFinallyIOUnfold #-}
786+readWriteFinallyIOUnfold :: Handle -> Handle -> IO ()
787+readWriteFinallyIOUnfold devNull inh =
788+ let readEx = IUF.finallyIO (\_ -> hClose inh) FH.read
789+ in S.fold (FH.write devNull) $ S.unfold readEx inh
791+-- | Send the file contents to /dev/null with exception handling
792+{-# INLINE readWriteBracketUnfold #-}
793+readWriteBracketUnfold :: Handle -> Handle -> IO ()
794+readWriteBracketUnfold devNull inh =
795+ let readEx = IUF.bracket return (\_ -> hClose inh) FH.read
796+ in S.fold (FH.write devNull) $ S.unfold readEx inh
799+inspect $ hasNoTypeClasses 'readWriteBracketUnfold
800+-- inspect $ 'readWriteBracketUnfold `hasNoType` ''Step
801+-- inspect $ 'readWriteBracketUnfold `hasNoType` ''AT.FlattenState
802+-- inspect $ 'readWriteBracketUnfold `hasNoType` ''D.ConcatMapUState
805+{-# INLINE readWriteBracketIOUnfold #-}
806+readWriteBracketIOUnfold :: Handle -> Handle -> IO ()
807+readWriteBracketIOUnfold devNull inh =
808+ let readEx = IUF.bracketIO return (\_ -> hClose inh) FH.read
809+ in S.fold (FH.write devNull) $ S.unfold readEx inh
811+o_1_space_copy_read_exceptions ::
812+ IORef Handles -> Handle -> Int -> [Benchmark]
813+o_1_space_copy_read_exceptions href devNull _ =
814+ [ bgroup "copy/read/exceptions"
815+ [ mkBench "UF.onException" href $ do
816+ Handles inh _ <- readIORef href
817+ readWriteOnExceptionUnfold devNull inh
818+ , mkBench "UF.handle" href $ do
819+ Handles inh _ <- readIORef href
820+ readWriteHandleExceptionUnfold devNull inh
821+ , mkBench "UF.finally" href $ do
822+ Handles inh _ <- readIORef href
823+ readWriteFinallyUnfold devNull inh
824+ , mkBench "UF.finallyIO" href $ do
825+ Handles inh _ <- readIORef href
826+ readWriteFinallyIOUnfold devNull inh
827+ , mkBench "UF.bracket" href $ do
828+ Handles inh _ <- readIORef href
829+ readWriteBracketUnfold devNull inh
830+ , mkBench "UF.bracketIO" href $ do
831+ Handles inh _ <- readIORef href
832+ readWriteBracketIOUnfold devNull inh
836+-------------------------------------------------------------------------------
838+-------------------------------------------------------------------------------
840+-- | Send the file contents to /dev/null with exception handling
841+{-# INLINE readWriteOnExceptionStream #-}
842+readWriteOnExceptionStream :: Handle -> Handle -> IO ()
843+readWriteOnExceptionStream devNull inh =
844+ let readEx = S.onException (hClose inh) (S.unfold FH.read inh)
845+ in S.fold (FH.write devNull) $ readEx
847+-- | Send the file contents to /dev/null with exception handling
848+{-# INLINE readWriteHandleExceptionStream #-}
849+readWriteHandleExceptionStream :: Handle -> Handle -> IO ()
850+readWriteHandleExceptionStream devNull inh =
851+ let handler (_e :: SomeException) = S.yieldM (hClose inh >> return 10)
852+ readEx = S.handle handler (S.unfold FH.read inh)
853+ in S.fold (FH.write devNull) $ readEx
855+-- | Send the file contents to /dev/null with exception handling
856+{-# INLINE readWriteFinallyStream #-}
857+readWriteFinallyStream :: Handle -> Handle -> IO ()
858+readWriteFinallyStream devNull inh =
859+ let readEx = S.finally (hClose inh) (S.unfold FH.read inh)
860+ in S.fold (FH.write devNull) readEx
862+{-# INLINE readWriteFinallyIOStream #-}
863+readWriteFinallyIOStream :: Handle -> Handle -> IO ()
864+readWriteFinallyIOStream devNull inh =
865+ let readEx = IP.finallyIO (hClose inh) (S.unfold FH.read inh)
866+ in S.fold (FH.write devNull) readEx
868+-- | Send the file contents to /dev/null with exception handling
869+{-# INLINE fromToBytesBracketStream #-}
870+fromToBytesBracketStream :: Handle -> Handle -> IO ()
871+fromToBytesBracketStream devNull inh =
872+ let readEx = S.bracket (return ()) (\_ -> hClose inh)
873+ (\_ -> IFH.toBytes inh)
874+ in IFH.fromBytes devNull $ readEx
877+inspect $ hasNoTypeClasses 'readWriteBracketStream
878+-- inspect $ 'readWriteBracketStream `hasNoType` ''Step
881+{-# INLINE fromToBytesBracketIOStream #-}
882+fromToBytesBracketIOStream :: Handle -> Handle -> IO ()
883+fromToBytesBracketIOStream devNull inh =
884+ let readEx = IP.bracketIO (return ()) (\_ -> hClose inh)
885+ (\_ -> IFH.toBytes inh)
886+ in IFH.fromBytes devNull $ readEx
888+o_1_space_copy_stream_exceptions ::
889+ IORef Handles -> Handle -> Int -> [Benchmark]
890+o_1_space_copy_stream_exceptions href devNull _ =
891+ [ bgroup "copy/read/exceptions"
892+ [ mkBench "S.onException" href $ do
893+ Handles inh _ <- readIORef href
894+ readWriteOnExceptionStream devNull inh
895+ , mkBench "S.handle" href $ do
896+ Handles inh _ <- readIORef href
897+ readWriteHandleExceptionStream devNull inh
898+ , mkBench "S.finally" href $ do
899+ Handles inh _ <- readIORef href
900+ readWriteFinallyStream devNull inh
901+ , mkBench "S.finallyIO" href $ do
902+ Handles inh _ <- readIORef href
903+ readWriteFinallyIOStream devNull inh
905+ , bgroup "copy/fromToBytes/exceptions"
906+ [ mkBench "S.bracket" href $ do
907+ Handles inh _ <- readIORef href
908+ fromToBytesBracketStream devNull inh
909+ , mkBench "S.bracketIO" href $ do
910+ Handles inh _ <- readIORef href
911+ fromToBytesBracketIOStream devNull inh
915+-------------------------------------------------------------------------------
916+-- copy with group/ungroup transformations
917+-------------------------------------------------------------------------------
919+-- | Lines and unlines
920+{-# INLINE copyChunksSplitInterposeSuffix #-}
921+copyChunksSplitInterposeSuffix :: Handle -> Handle -> IO ()
922+copyChunksSplitInterposeSuffix inh outh =
923+ S.fold (IFH.writeWithBufferOf (1024*1024) outh)
924+ $ AS.interposeSuffix 10
925+ $ AS.splitOnSuffix 10
926+ $ IFH.toChunksWithBufferOf (1024*1024) inh
929+inspect $ hasNoTypeClassesExcept 'copyChunksSplitInterposeSuffix [''Storable]
930+-- inspect $ 'copyChunksSplitInterposeSuffix `hasNoType` ''Step
933+-- | Words and unwords
934+{-# INLINE copyChunksSplitInterpose #-}
935+copyChunksSplitInterpose :: Handle -> Handle -> IO ()
936+copyChunksSplitInterpose inh outh =
937+ S.fold (IFH.writeWithBufferOf (1024*1024) outh)
939+ -- XXX this is not correct word splitting combinator
941+ $ IFH.toChunksWithBufferOf (1024*1024) inh
944+inspect $ hasNoTypeClassesExcept 'copyChunksSplitInterpose [''Storable]
945+-- inspect $ 'copyChunksSplitInterpose `hasNoType` ''Step
948+o_1_space_copy_toChunks_group_ungroup ::
949+ IORef Handles -> Handle -> Int -> [Benchmark]
950+o_1_space_copy_toChunks_group_ungroup href _ _ =
951+ [ bgroup "copy/toChunks/group-ungroup"
952+ [ mkBench "AS.interposeSuffix . AS.splitOnSuffix" href $ do
953+ Handles inh outh <- readIORef href
954+ copyChunksSplitInterposeSuffix inh outh
955+ , mkBench "AS.interpose . AS.splitOn" href $ do
956+ Handles inh outh <- readIORef href
957+ copyChunksSplitInterpose inh outh
961+-------------------------------------------------------------------------------
962+-- copy with group/ungroup transformations
963+-------------------------------------------------------------------------------
965+{-# INLINE linesUnlinesCopy #-}
966+linesUnlinesCopy :: Handle -> Handle -> IO ()
967+linesUnlinesCopy inh outh =
968+ S.fold (FH.write outh)
970+ $ IUS.unlines IUF.fromList
971+ $ S.splitOnSuffix (== '\n') FL.toList
973+ $ S.unfold FH.read inh
975+{-# INLINE linesUnlinesArrayWord8Copy #-}
976+linesUnlinesArrayWord8Copy :: Handle -> Handle -> IO ()
977+linesUnlinesArrayWord8Copy inh outh =
978+ S.fold (FH.write outh)
979+ $ IP.interposeSuffix 10 A.read
980+ $ S.splitOnSuffix (== 10) A.write
981+ $ S.unfold FH.read inh
983+-- XXX splitSuffixOn requires -funfolding-use-threshold=150 for better fusion
984+-- | Lines and unlines
985+{-# INLINE linesUnlinesArrayCharCopy #-}
986+linesUnlinesArrayCharCopy :: Handle -> Handle -> IO ()
987+linesUnlinesArrayCharCopy inh outh =
988+ S.fold (FH.write outh)
993+ $ S.unfold FH.read inh
996+inspect $ hasNoTypeClassesExcept 'linesUnlinesArrayCharCopy [''Storable]
997+-- inspect $ 'linesUnlinesArrayCharCopy `hasNoType` ''Step
998+-- inspect $ 'linesUnlinesArrayCharCopy `hasNoType` ''AT.FlattenState
999+-- inspect $ 'linesUnlinesArrayCharCopy `hasNoType` ''D.ConcatMapUState
1002+-- XXX to write this we need to be able to map decodeUtf8 on the A.read fold.
1003+-- For that we have to write decodeUtf8 as a Pipe.
1005+{-# INLINE linesUnlinesArrayUtf8Copy #-}
1006+linesUnlinesArrayUtf8Copy :: Handle -> Handle -> IO ()
1007+linesUnlinesArrayUtf8Copy inh outh =
1008+ S.fold (FH.write outh)
1010+ $ IP.intercalate (A.fromList [10]) (pipe SS.decodeUtf8P A.read)
1011+ $ S.splitOnSuffix (== '\n') (IFL.lmap SS.encodeUtf8 A.write)
1013+ $ S.unfold FH.read inh
1016+foreign import ccall unsafe "u_iswspace"
1017+ iswspace :: Int -> Int
1019+-- Code copied from base/Data.Char to INLINE it
1020+{-# INLINE isSpace #-}
1021+isSpace :: Char -> Bool
1022+-- isSpace includes non-breaking space
1023+-- The magic 0x377 isn't really that magical. As of 2014, all the codepoints
1024+-- at or below 0x377 have been assigned, so we shouldn't have to worry about
1025+-- any new spaces appearing below there. It would probably be best to
1026+-- use branchless ||, but currently the eqLit transformation will undo that,
1027+-- so we'll do it like this until there's a way around that.
1029+ | uc <= 0x377 = uc == 32 || uc - 0x9 <= 4 || uc == 0xa0
1030+ | otherwise = iswspace (ord c) /= 0
1032+ uc = fromIntegral (ord c) :: Word
1035+isSp :: Word8 -> Bool
1036+isSp = isSpace . chr . fromIntegral
1038+-- | Word, unwords and copy
1039+{-# INLINE wordsUnwordsCopyWord8 #-}
1040+wordsUnwordsCopyWord8 :: Handle -> Handle -> IO ()
1041+wordsUnwordsCopyWord8 inh outh =
1042+ S.fold (FH.write outh)
1043+ $ IP.interposeSuffix 32 IUF.fromList
1044+ $ S.wordsBy isSp FL.toList
1045+ $ S.unfold FH.read inh
1048+inspect $ hasNoTypeClasses 'wordsUnwordsCopyWord8
1049+-- inspect $ 'wordsUnwordsCopyWord8 `hasNoType` ''Step
1050+-- inspect $ 'wordsUnwordsCopyWord8 `hasNoType` ''D.ConcatMapUState
1053+-- | Word, unwords and copy
1054+{-# INLINE wordsUnwordsCopy #-}
1055+wordsUnwordsCopy :: Handle -> Handle -> IO ()
1056+wordsUnwordsCopy inh outh =
1057+ S.fold (FH.write outh)
1059+ $ IUS.unwords IUF.fromList
1060+ -- XXX This pipeline does not fuse with wordsBy but fuses with splitOn
1061+ -- with -funfolding-use-threshold=300. With wordsBy it does not fuse
1062+ -- even with high limits for inlining and spec-constr ghc options. With
1063+ -- -funfolding-use-threshold=400 it performs pretty well and there
1064+ -- is no evidence in the core that a join point involving Step
1065+ -- constructors is not getting inlined. Not being able to fuse at all in
1066+ -- this case could be an unknown issue, need more investigation.
1067+ $ S.wordsBy isSpace FL.toList
1068+ -- -- $ S.splitOn isSpace FL.toList
1070+ $ S.unfold FH.read inh
1073+-- inspect $ hasNoTypeClasses 'wordsUnwordsCopy
1074+-- inspect $ 'wordsUnwordsCopy `hasNoType` ''Step
1075+-- inspect $ 'wordsUnwordsCopy `hasNoType` ''AT.FlattenState
1076+-- inspect $ 'wordsUnwordsCopy `hasNoType` ''D.ConcatMapUState
1079+{-# INLINE wordsUnwordsCharArrayCopy #-}
1080+wordsUnwordsCharArrayCopy :: Handle -> Handle -> IO ()
1081+wordsUnwordsCharArrayCopy inh outh =
1082+ S.fold (FH.write outh)
1087+ $ S.unfold FH.read inh
1089+o_1_space_copy_read_group_ungroup ::
1090+ IORef Handles -> Handle -> Int -> [Benchmark]
1091+o_1_space_copy_read_group_ungroup href _ _ =
1092+ [ bgroup "copy/read/group-ungroup"
1093+ [ mkBench "US.unlines . S.splitOnSuffix ([Word8])" href $ do
1094+ Handles inh outh <- readIORef href
1095+ linesUnlinesCopy inh outh
1096+ , mkBench "S.interposeSuffix . S.splitOnSuffix(Array Word8)" href $ do
1097+ Handles inh outh <- readIORef href
1098+ linesUnlinesArrayWord8Copy inh outh
1099+ , mkBench "UA.unlines . UA.lines (Array Char)" href $ do
1100+ Handles inh outh <- readIORef href
1101+ linesUnlinesArrayCharCopy inh outh
1103+ , mkBench "S.interposeSuffix . S.wordsBy ([Word8])" href $ do
1104+ Handles inh outh <- readIORef href
1105+ wordsUnwordsCopyWord8 inh outh
1106+ , mkBench "US.unwords . S.wordsBy ([Char])" href $ do
1107+ Handles inh outh <- readIORef href
1108+ wordsUnwordsCopy inh outh
1109+ , mkBench "UA.unwords . UA.words (Array Char)" href $ do
1110+ Handles inh outh <- readIORef href
1111+ wordsUnwordsCharArrayCopy inh outh
1115+-------------------------------------------------------------------------------
1116+-- reduce with splitting transformations
1117+-------------------------------------------------------------------------------
1120+lf = fromIntegral (ord '\n')
1122+toarr :: String -> A.Array Word8
1123+toarr = A.fromList . map (fromIntegral . ord)
1125+-- | Split on line feed.
1126+{-# INLINE splitOn #-}
1127+splitOn :: Handle -> IO Int
1129+ (S.length $ S.splitOn (== lf) FL.drain
1130+ $ S.unfold FH.read inh) -- >>= print
1133+inspect $ hasNoTypeClasses 'splitOn
1134+inspect $ 'splitOn `hasNoType` ''Step
1135+inspect $ 'splitOn `hasNoType` ''AT.FlattenState
1136+inspect $ 'splitOn `hasNoType` ''D.ConcatMapUState
1139+-- | Split suffix on line feed.
1140+{-# INLINE splitOnSuffix #-}
1141+splitOnSuffix :: Handle -> IO Int
1143+ (S.length $ S.splitOnSuffix (== lf) FL.drain
1144+ $ S.unfold FH.read inh) -- >>= print
1147+inspect $ hasNoTypeClasses 'splitOnSuffix
1148+inspect $ 'splitOnSuffix `hasNoType` ''Step
1149+inspect $ 'splitOnSuffix `hasNoType` ''AT.FlattenState
1150+inspect $ 'splitOnSuffix `hasNoType` ''D.ConcatMapUState
1153+-- | Split on line feed.
1154+{-# INLINE parseManySepBy #-}
1155+parseManySepBy :: Handle -> IO Int
1156+parseManySepBy inh =
1157+ (S.length $ IP.parseMany (PR.sliceSepBy (== lf) FL.drain)
1158+ (S.unfold FH.read inh)) -- >>= print
1161+{-# INLINE wordsBy #-}
1162+wordsBy :: Handle -> IO Int
1164+ (S.length $ S.wordsBy isSp FL.drain
1165+ $ S.unfold FH.read inh) -- >>= print
1168+inspect $ hasNoTypeClasses 'wordsBy
1169+inspect $ 'wordsBy `hasNoType` ''Step
1170+inspect $ 'wordsBy `hasNoType` ''AT.FlattenState
1171+inspect $ 'wordsBy `hasNoType` ''D.ConcatMapUState
1174+-- | Split on a word8 sequence.
1175+{-# INLINE splitOnSeq #-}
1176+splitOnSeq :: String -> Handle -> IO Int
1177+splitOnSeq str inh =
1178+ (S.length $ IP.splitOnSeq (toarr str) FL.drain
1179+ $ S.unfold FH.read inh) -- >>= print
1182+-- inspect $ hasNoTypeClasses 'splitOnSeq
1183+-- inspect $ 'splitOnSeq `hasNoType` ''Step
1184+-- inspect $ 'splitOnSeq `hasNoType` ''AT.FlattenState
1185+-- inspect $ 'splitOnSeq `hasNoType` ''D.ConcatMapUState
1188+-- | Split on suffix sequence.
1189+{-# INLINE splitOnSuffixSeq #-}
1190+splitOnSuffixSeq :: String -> Handle -> IO Int
1191+splitOnSuffixSeq str inh =
1192+ (S.length $ IP.splitOnSuffixSeq (toarr str) FL.drain
1193+ $ S.unfold FH.read inh) -- >>= print
1196+-- inspect $ hasNoTypeClasses 'splitOnSuffixSeq
1197+-- inspect $ 'splitOnSuffixSeq `hasNoType` ''Step
1198+-- inspect $ 'splitOnSuffixSeq `hasNoType` ''AT.FlattenState
1199+-- inspect $ 'splitOnSuffixSeq `hasNoType` ''D.ConcatMapUState
1202+o_1_space_reduce_read_split ::
1203+ IORef Handles -> Handle -> Int -> [Benchmark]
1204+o_1_space_reduce_read_split href _ _ =
1205+ [ bgroup "reduce/read"
1206+ [ mkBench "S.parseMany (PR.sliceSepBy (== lf) FL.drain)" href $ do
1207+ Handles inh _ <- readIORef href
1209+ , mkBench "S.wordsBy isSpace FL.drain" href $ do
1210+ Handles inh _ <- readIORef href
1212+ , mkBench "S.splitOn (== lf) FL.drain" href $ do
1213+ Handles inh _ <- readIORef href
1215+ , mkBench "S.splitOnSuffix (== lf) FL.drain" href $ do
1216+ Handles inh _ <- readIORef href
1218+ , mkBench "S.splitOnSeq \"\" FL.drain" href $ do
1219+ Handles inh _ <- readIORef href
1221+ , mkBench "S.splitOnSuffixSeq \"\" FL.drain" href $ do
1222+ Handles inh _ <- readIORef href
1223+ splitOnSuffixSeq "" inh
1224+ , mkBench "S.splitOnSeq \"\\n\" FL.drain" href $ do
1225+ Handles inh _ <- readIORef href
1226+ splitOnSeq "\n" inh
1227+ , mkBench "S.splitOnSuffixSeq \"\\n\" FL.drain" href $ do
1228+ Handles inh _ <- readIORef href
1229+ splitOnSuffixSeq "\n" inh
1230+ , mkBench "S.splitOnSeq \"a\" FL.drain" href $ do
1231+ Handles inh _ <- readIORef href
1233+ , mkBench "S.splitOnSeq \"\\r\\n\" FL.drain" href $ do
1234+ Handles inh _ <- readIORef href
1235+ splitOnSeq "\r\n" inh
1236+ , mkBench "S.splitOnSuffixSeq \"\\r\\n\" FL.drain" href $ do
1237+ Handles inh _ <- readIORef href
1238+ splitOnSuffixSeq "\r\n" inh
1239+ , mkBench "S.splitOnSeq \"aa\" FL.drain" href $ do
1240+ Handles inh _ <- readIORef href
1241+ splitOnSeq "aa" inh
1242+ , mkBench "S.splitOnSeq \"aaaa\" FL.drain" href $ do
1243+ Handles inh _ <- readIORef href
1244+ splitOnSeq "aaaa" inh
1245+ , mkBench "S.splitOnSeq \"abcdefgh\" FL.drain" href $ do
1246+ Handles inh _ <- readIORef href
1247+ splitOnSeq "abcdefgh" inh
1248+ , mkBench "S.splitOnSeq \"abcdefghi\" FL.drain" href $ do
1249+ Handles inh _ <- readIORef href
1250+ splitOnSeq "abcdefghi" inh
1251+ , mkBench "S.splitOnSeq \"catcatcatcatcat\" FL.drain" href $ do
1252+ Handles inh _ <- readIORef href
1253+ splitOnSeq "catcatcatcatcat" inh
1254+ , mkBench "S.splitOnSeq \"abcdefghijklmnopqrstuvwxyz\" FL.drain"
1256+ Handles inh _ <- readIORef href
1257+ splitOnSeq "abcdefghijklmnopqrstuvwxyz" inh
1258+ , mkBench "S.splitOnSuffixSeq \"abcdefghijklmnopqrstuvwxyz\" FL.drain"
1260+ Handles inh _ <- readIORef href
1261+ splitOnSuffixSeq "abcdefghijklmnopqrstuvwxyz" inh
1265+-- | Split on a character sequence.
1266+{-# INLINE splitOnSeqUtf8 #-}
1267+splitOnSeqUtf8 :: String -> Handle -> IO Int
1268+splitOnSeqUtf8 str inh =
1269+ (S.length $ IP.splitOnSeq (A.fromList str) FL.drain
1270+ $ IUS.decodeUtf8ArraysLenient
1271+ $ IFH.toChunks inh) -- >>= print
1273+o_1_space_reduce_toChunks_split ::
1274+ IORef Handles -> Handle -> Int -> [Benchmark]
1275+o_1_space_reduce_toChunks_split href _ _ =
1276+ [ bgroup "reduce/toChunks"
1277+ [ mkBench ("S.splitOnSeqUtf8 \"abcdefgh\" FL.drain "
1278+ ++ ". US.decodeUtf8ArraysLenient") href $ do
1279+ Handles inh _ <- readIORef href
1280+ splitOnSeqUtf8 "abcdefgh" inh
1281+ , mkBench "S.splitOnSeqUtf8 \"abcdefghijklmnopqrstuvwxyz\" FL.drain"
1283+ Handles inh _ <- readIORef href
1284+ splitOnSeqUtf8 "abcdefghijklmnopqrstuvwxyz" inh
1288+-------------------------------------------------------------------------------
1290+-------------------------------------------------------------------------------
1294+ (_, cfg, benches) <- parseCLIOpts defaultStreamSize
1296+ -- XXX The file is cached, so if the older file was a different size it
1297+ -- will not be correct
1298+ -- fileSize = blockSize * blockCount
1300+ r <- lookupEnv "Benchmark.FileSystem.Handle.InputFile"
1303+ Just inFileName -> return (inFileName, inFileName)
1305+ -- XXX will this work on windows/msys?
1306+ let cmd infile size =
1307+ "mkdir -p " ++ scratchDir
1308+ ++ "; test -e " ++ infile
1309+ ++ " || { echo \"creating input file " ++ infile
1310+ ++ "\" && dd if=/dev/random of=" ++ infile
1311+ ++ " bs=" ++ show blockSize
1312+ ++ " count=" ++ show (blockCount size)
1314+ runProcess_ (shell (cmd inFileSmall smallFileSize))
1315+ runProcess_ (shell (cmd inFileBig bigFileSize))
1316+ return (inFileSmall, inFileBig)
1318+ -- smallHandle <- openFile small ReadMode
1319+ bigHandle <- openFile big ReadMode
1320+ outHandle <- openFile outfile WriteMode
1321+ devNull <- openFile "/dev/null" WriteMode
1323+ -- ssize <- getFileSize small
1324+ bsize <- getFileSize big
1326+ ref <- newIORef $ Handles bigHandle outHandle
1327+ runMode (mode cfg) cfg benches (allBenchmarks ref devNull (fromIntegral bsize))
1331+ allBenchmarks env devNull bsize =
1332+ [ bgroup (o_1_space_prefix moduleName) $ Prelude.concat
1333+ [ o_1_space_read_chunked env devNull bsize
1334+ , o_1_space_copy_chunked env devNull bsize
1335+ , o_1_space_copy_exceptions_readChunks env devNull bsize
1336+ , o_1_space_copy_exceptions_toChunks env devNull bsize
1338+ , o_1_space_reduce_read env devNull bsize
1339+ , o_1_space_reduce_toBytes env devNull bsize
1340+ , o_1_space_reduce_read_grouped env devNull bsize
1341+ , o_1_space_reduce_read_split env devNull bsize
1342+ , o_1_space_reduce_toChunks_split env devNull bsize
1344+ , o_1_space_copy_read env devNull bsize
1345+ , o_1_space_copy_fromBytes env devNull bsize
1346+ , o_1_space_copy_read_exceptions env devNull bsize
1347+ , o_1_space_copy_stream_exceptions env devNull bsize
1348+ , o_1_space_copy_read_group_ungroup env devNull bsize
1349+ , o_1_space_copy_toChunks_group_ungroup env devNull bsize