@@ -25,11 +25,13 @@ module Streamly.Benchmark.FileIO.Stream
25 , countBytes
25 , countBytes
26 , countLines
26 , countLines
27 , countLinesU
27 , countLinesU
28 , sumBytes
29 , sumBytes
30 , catStreamWrite
31 , catStreamWrite
32 , linesUnlinesCopy
33 , linesUnlinesCopy
34+ , wordsUnwordsCopyWord8
33 , wordsUnwordsCopy
35 , wordsUnwordsCopy
34 , copyCodecChar8
36 , copyCodecChar8
35 , copyCodecUtf8
37 , copyCodecUtf8
@@ -105,6 +107,21 @@ inspect $ 'countLines `hasNoType` ''AT.FlattenState
105inspect $ 'countLines `hasNoType` ''D.ConcatMapUState
107inspect $ 'countLines `hasNoType` ''D.ConcatMapUState
110+-- | Count the number of words in a file.
111+{-# INLINE countWords #-}
112+countWords :: Handle -> IO Int
115+ . SS.foldWords FL.drain
120+inspect $ hasNoTypeClasses 'countWords
121+-- inspect $ 'countWords `hasNoType` ''Step
122+-- inspect $ 'countWords `hasNoType` ''D.ConcatMapUState
108-- | Count the number of lines in a file.
125-- | Count the number of lines in a file.
109{-# INLINE countLinesU #-}
126{-# INLINE countLinesU #-}
110countLinesU :: Handle -> IO Int
127countLinesU :: Handle -> IO Int
@@ -253,14 +270,67 @@ inspect $ hasNoTypeClassesExcept 'linesUnlinesCopy [''Storable]
253-- inspect $ 'linesUnlinesCopy `hasNoType` ''D.ConcatMapUState
270-- inspect $ 'linesUnlinesCopy `hasNoType` ''D.ConcatMapUState
273+foreign import ccall unsafe "u_iswspace"
274+ iswspace :: Int -> Int
276+-- Code copied from base/Data.Char to INLINE it
277+{-# INLINE isSpace #-}
278+isSpace :: Char -> Bool
279+-- isSpace includes non-breaking space
280+-- The magic 0x377 isn't really that magical. As of 2014, all the codepoints
281+-- at or below 0x377 have been assigned, so we shouldn't have to worry about
282+-- any new spaces appearing below there. It would probably be best to
283+-- use branchless ||, but currently the eqLit transformation will undo that,
284+-- so we'll do it like this until there's a way around that.
286+ | uc <= 0x377 = uc == 32 || uc - 0x9 <= 4 || uc == 0xa0
287+ | otherwise = iswspace (ord c) /= 0
289+ uc = fromIntegral (ord c) :: Word
292+isSp :: Word8 -> Bool
293+isSp = isSpace . chr . fromIntegral
295+-- | Word, unwords and copy
296+{-# INLINE wordsUnwordsCopyWord8 #-}
297+wordsUnwordsCopyWord8 :: Handle -> Handle -> IO ()
298+wordsUnwordsCopyWord8 inh outh =
299+ S.runFold (FH.write outh)
300+ $ Internal.concatMapU Internal.fromList
301+ $ S.intersperse [32]
302+ $ S.wordsBy isSp FL.toList
306+inspect $ hasNoTypeClasses 'wordsUnwordsCopyWord8
307+-- inspect $ 'wordsUnwordsCopyWord8 `hasNoType` ''Step
308+-- inspect $ 'wordsUnwordsCopyWord8 `hasNoType` ''D.ConcatMapUState
256-- | Word, unwords and copy
311-- | Word, unwords and copy
257{-# INLINE wordsUnwordsCopy #-}
312{-# INLINE wordsUnwordsCopy #-}
258wordsUnwordsCopy :: Handle -> Handle -> IO ()
313wordsUnwordsCopy :: Handle -> Handle -> IO ()
259wordsUnwordsCopy inh outh =
314wordsUnwordsCopy inh outh =
260 S.runFold (FH.write outh)
315 S.runFold (FH.write outh)
261 $ SS.encodeChar8
316 $ SS.encodeChar8
317+ $ Internal.concatMapU Internal.fromList
319+ -- Array allocation is too expensive for such small strings. So just use
262− $ SS.unwords
322+ -- -- $ SS.unwords
263− $ SS.words
323+ -- -- $ SS.words
325+ -- XXX This pipeline does not fuse with wordsBy but fuses with splitOn
326+ -- with -funfolding-use-threshold=300. With wordsBy it does not fuse
327+ -- even with high limits for inlining and spec-constr ghc options. With
328+ -- -funfolding-use-threshold=400 it performs pretty well and there
329+ -- is no evidence in the core that a join point involving Step
330+ -- constructors is not getting inlined. Not being able to fuse at all in
331+ -- this case could be an unknown issue, need more investigation.
332+ $ S.wordsBy isSpace FL.toList
333+ -- -- $ S.splitOn isSpace FL.toList
264 $ SS.decodeChar8
334 $ SS.decodeChar8
265 $ FH.read inh
335 $ FH.read inh
@@ -277,27 +347,6 @@ lf = fromIntegral (ord '\n')
277toarr :: String -> A.Array Word8
347toarr :: String -> A.Array Word8
278toarr = A.fromList . map (fromIntegral . ord)
348toarr = A.fromList . map (fromIntegral . ord)
280−foreign import ccall unsafe "u_iswspace"
281− iswspace :: Int -> Int
283−-- Code copied from base/Data.Char to INLINE it
284−{-# INLINE isSpace #-}
285−isSpace :: Char -> Bool
286−-- isSpace includes non-breaking space
287−-- The magic 0x377 isn't really that magical. As of 2014, all the codepoints
288−-- at or below 0x377 have been assigned, so we shouldn't have to worry about
289−-- any new spaces appearing below there. It would probably be best to
290−-- use branchless ||, but currently the eqLit transformation will undo that,
291−-- so we'll do it like this until there's a way around that.
293− | uc <= 0x377 = uc == 32 || uc - 0x9 <= 4 || uc == 0xa0
294− | otherwise = iswspace (ord c) /= 0
296− uc = fromIntegral (ord c) :: Word
298−isSp :: Word8 -> Bool
299−isSp = isSpace . chr . fromIntegral
301-- | Split on line feed.
350-- | Split on line feed.
302{-# INLINE splitOn #-}
351{-# INLINE splitOn #-}
303splitOn :: Handle -> IO Int
352splitOn :: Handle -> IO Int