on a05abfc help sign in
302 lines 11 kB history raw
1--
2-- Module : Streamly.Unicode.Stream
3-- Copyright : (c) 2019 Composewell Technologies
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
6-- Stability : experimental
7-- Portability : GHC
9{-# LANGUAGE CPP #-}
10{-# LANGUAGE ScopedTypeVariables #-}
12#ifdef __HADDOCK_VERSION__
13#undef INSPECTION
14#endif
16#ifdef INSPECTION
17{-# LANGUAGE TemplateHaskell #-}
18{-# OPTIONS_GHC -fplugin Test.Inspection.Plugin #-}
19#endif
21import Prelude hiding (last, length)
22import System.IO (Handle)
24import qualified Streamly.Data.Array.Foreign as Array
25import qualified Streamly.Data.Fold as Fold
26import qualified Streamly.FileSystem.Handle as Handle
27import qualified Streamly.Internal.Data.Stream.IsStream as Stream
28import qualified Streamly.Internal.Data.Unfold as Unfold
29import qualified Streamly.Internal.FileSystem.Handle as Handle
30import qualified Streamly.Internal.Unicode.Array.Char as UnicodeArr
31import qualified Streamly.Internal.Unicode.Stream as Unicode
33import Gauge hiding (env)
34import Streamly.Benchmark.Common
35import Streamly.Benchmark.Common.Handle
37#ifdef INSPECTION
38import Foreign.Storable (Storable)
39import Streamly.Internal.Data.Stream.StreamD.Type (Step(..))
40import qualified Streamly.Internal.Data.Fold.Type as Fold
41import qualified Streamly.Internal.Data.Tuple.Strict as Strict
42import qualified Streamly.Internal.Data.Array.Foreign.Type as Array
43import qualified Streamly.Internal.Data.Array.Foreign.Mut.Type as MArray
45import Test.Inspection
46#endif
48moduleName :: String
49moduleName = "Unicode.Stream"
51-- | Copy file
52{-# NOINLINE copyCodecUtf8ArraysLenient #-}
53copyCodecUtf8ArraysLenient :: Handle -> Handle -> IO ()
54copyCodecUtf8ArraysLenient inh outh =
55 Stream.fold (Handle.write outh)
56 $ Unicode.encodeUtf8'
57 $ Unicode.decodeUtf8Arrays
58 $ Handle.toChunks inh
60#ifdef INSPECTION
61inspect $ hasNoTypeClasses 'copyCodecUtf8ArraysLenient
62-- inspect $ 'copyCodecUtf8ArraysLenient `hasNoType` ''Step
63#endif
65o_1_space_decode_encode_chunked :: BenchEnv -> [Benchmark]
66o_1_space_decode_encode_chunked env =
67 [ bgroup "decode-encode/toChunks"
68 [
69 mkBenchSmall "encodeUtf8' . decodeUtf8Arrays" env $ \inH outH ->
70 copyCodecUtf8ArraysLenient inH outH
71 ]
72 ]
74-------------------------------------------------------------------------------
75-- copy with group/ungroup transformations
76-------------------------------------------------------------------------------
78{-# NOINLINE linesUnlinesCopy #-}
79linesUnlinesCopy :: Handle -> Handle -> IO ()
80linesUnlinesCopy inh outh =
81 Stream.fold (Handle.write outh)
82 $ Unicode.encodeLatin1'
83 $ Unicode.unlines Unfold.fromList
84 $ Stream.splitOnSuffix (== '\n') Fold.toList
85 $ Unicode.decodeLatin1
86 $ Stream.unfold Handle.read inh
88{-# NOINLINE linesUnlinesArrayWord8Copy #-}
89linesUnlinesArrayWord8Copy :: Handle -> Handle -> IO ()
90linesUnlinesArrayWord8Copy inh outh =
91 Stream.fold (Handle.write outh)
92 $ Stream.interposeSuffix 10 Array.read
93 $ Stream.splitOnSuffix (== 10) Array.write
94 $ Stream.unfold Handle.read inh
96-- XXX splitSuffixOn requires -funfolding-use-threshold=150 for better fusion
97-- | Lines and unlines
98{-# NOINLINE linesUnlinesArrayCharCopy #-}
99linesUnlinesArrayCharCopy :: Handle -> Handle -> IO ()
100linesUnlinesArrayCharCopy inh outh =
101 Stream.fold (Handle.write outh)
102 $ Unicode.encodeLatin1'
103 $ UnicodeArr.unlines
104 $ UnicodeArr.lines
105 $ Unicode.decodeLatin1
106 $ Stream.unfold Handle.read inh
108#ifdef INSPECTION
109inspect $ hasNoTypeClassesExcept 'linesUnlinesArrayCharCopy [''Storable]
110-- inspect $ 'linesUnlinesArrayCharCopy `hasNoType` ''Step
111#endif
113-- XXX to write this we need to be able to map decodeUtf8 on the Array.read fold.
114-- For that we have to write decodeUtf8 as a Pipe.
115{-
116{-# INLINE linesUnlinesArrayUtf8Copy #-}
117linesUnlinesArrayUtf8Copy :: Handle -> Handle -> IO ()
118linesUnlinesArrayUtf8Copy inh outh =
119 Stream.fold (Handle.write outh)
120 $ Unicode.encodeLatin1'
121 $ Stream.intercalate (Array.fromList [10]) (pipe Unicode.decodeUtf8P Array.read)
122 $ Stream.splitOnSuffix (== '\n') (IFold.map Unicode.encodeUtf8' Array.write)
123 $ Unicode.decodeLatin1
124 $ Stream.unfold Handle.read inh
125-}
127-- | Word, unwords and copy
128{-# NOINLINE wordsUnwordsCopyWord8 #-}
129wordsUnwordsCopyWord8 :: Handle -> Handle -> IO ()
130wordsUnwordsCopyWord8 inh outh =
131 Stream.fold (Handle.write outh)
132 $ Stream.interposeSuffix 32 Unfold.fromList
133 $ Stream.wordsBy isSp Fold.toList
134 $ Stream.unfold Handle.read inh
136#ifdef INSPECTION
137inspect $ hasNoTypeClasses 'wordsUnwordsCopyWord8
138-- inspect $ 'wordsUnwordsCopyWord8 `hasNoType` ''Step
139#endif
141-- | Word, unwords and copy
142{-# NOINLINE wordsUnwordsCopy #-}
143wordsUnwordsCopy :: Handle -> Handle -> IO ()
144wordsUnwordsCopy inh outh =
145 Stream.fold (Handle.write outh)
146 $ Unicode.encodeLatin1'
147 $ Unicode.unwords Unfold.fromList
148 -- XXX This pipeline does not fuse with wordsBy but fuses with splitOn
149 -- with -funfolding-use-threshold=300. With wordsBy it does not fuse
150 -- even with high limits for inlining and spec-constr ghc options. With
151 -- -funfolding-use-threshold=400 it performs pretty well and there
152 -- is no evidence in the core that a join point involving Step
153 -- constructors is not getting inlined. Not being able to fuse at all in
154 -- this case could be an unknown issue, need more investigation.
155 $ Stream.wordsBy isSpace Fold.toList
156 -- -- $ Stream.splitOn isSpace Fold.toList
157 $ Unicode.decodeLatin1
158 $ Stream.unfold Handle.read inh
160#ifdef INSPECTION
161-- inspect $ hasNoTypeClasses 'wordsUnwordsCopy
162-- inspect $ 'wordsUnwordsCopy `hasNoType` ''Step
163#endif
165{-# NOINLINE wordsUnwordsCharArrayCopy #-}
166wordsUnwordsCharArrayCopy :: Handle -> Handle -> IO ()
167wordsUnwordsCharArrayCopy inh outh =
168 Stream.fold (Handle.write outh)
169 $ Unicode.encodeLatin1'
170 $ UnicodeArr.unwords
171 $ UnicodeArr.words
172 $ Unicode.decodeLatin1
173 $ Stream.unfold Handle.read inh
175o_1_space_copy_read_group_ungroup :: BenchEnv -> [Benchmark]
176o_1_space_copy_read_group_ungroup env =
177 [ bgroup "ungroup-group"
178 [ mkBenchSmall "unlines . splitOnSuffix ([Word8])" env
179 $ \inh outh -> linesUnlinesCopy inh outh
180 , mkBenchSmall "interposeSuffix . splitOnSuffix (Array Word8)" env
181 $ \inh outh -> linesUnlinesArrayWord8Copy inh outh
182 , mkBenchSmall "UnicodeArr.unlines . UnicodeArr.lines (Array Char)" env
183 $ \inh outh -> linesUnlinesArrayCharCopy inh outh
185 , mkBenchSmall "interposeSuffix . wordsBy ([Word8])" env
186 $ \inh outh -> wordsUnwordsCopyWord8 inh outh
187 , mkBenchSmall "unwords . wordsBy ([Char])" env
188 $ \inh outh -> wordsUnwordsCopy inh outh
189 , mkBenchSmall "UnicodeArr.unwords . UnicodeArr.words (Array Char)" env
190 $ \inh outh -> wordsUnwordsCharArrayCopy inh outh
191 ]
192 ]
194-------------------------------------------------------------------------------
195-- copy unfold
196-------------------------------------------------------------------------------
198-- | Copy file (encodeLatin1')
199{-# NOINLINE copyStreamLatin1' #-}
200copyStreamLatin1' :: Handle -> Handle -> IO ()
201copyStreamLatin1' inh outh =
202 Stream.fold (Handle.write outh)
203 $ Unicode.encodeLatin1'
204 $ Unicode.decodeLatin1
205 $ Stream.unfold Handle.read inh
207#ifdef INSPECTION
208inspect $ hasNoTypeClasses 'copyStreamLatin1'
209inspect $ 'copyStreamLatin1' `hasNoType` ''Step
210inspect $ 'copyStreamLatin1' `hasNoType` ''Unfold.ConcatState -- Handle.read/UF.many
211inspect $ 'copyStreamLatin1' `hasNoType` ''MArray.ReadUState -- Handle.read/Array.read
213inspect $ 'copyStreamLatin1' `hasNoType` ''Fold.Step
214inspect $ 'copyStreamLatin1' `hasNoType` ''Array.ArrayUnsafe -- Handle.write/writeNUnsafe
215inspect $ 'copyStreamLatin1' `hasNoType` ''Strict.Tuple3' -- Handle.write/chunksOf
216#endif
218-- | Copy file (encodeLatin1)
219{-# NOINLINE copyStreamLatin1 #-}
220copyStreamLatin1 :: Handle -> Handle -> IO ()
221copyStreamLatin1 inh outh =
222 Stream.fold (Handle.write outh)
223 $ Unicode.encodeLatin1
224 $ Unicode.decodeLatin1
225 $ Stream.unfold Handle.read inh
227#ifdef INSPECTION
228inspect $ hasNoTypeClasses 'copyStreamLatin1
229inspect $ 'copyStreamLatin1 `hasNoType` ''Step
230inspect $ 'copyStreamLatin1 `hasNoType` ''Unfold.ConcatState -- Handle.read/UF.many
231inspect $ 'copyStreamLatin1 `hasNoType` ''MArray.ReadUState -- Handle.read/Array.read
233inspect $ 'copyStreamLatin1 `hasNoType` ''Fold.ManyState
234inspect $ 'copyStreamLatin1 `hasNoType` ''Fold.Step
235inspect $ 'copyStreamLatin1 `hasNoType` ''Array.ArrayUnsafe -- Handle.write/writeNUnsafe
236inspect $ 'copyStreamLatin1 `hasNoType` ''Strict.Tuple3' -- Handle.write/chunksOf
237#endif
239-- | Copy file
240_copyStreamUtf8' :: Handle -> Handle -> IO ()
241_copyStreamUtf8' inh outh =
242 Stream.fold (Handle.write outh)
243 $ Unicode.encodeUtf8'
244 $ Unicode.decodeUtf8'
245 $ Stream.unfold Handle.read inh
247#ifdef INSPECTION
248inspect $ hasNoTypeClasses '_copyStreamUtf8'
249-- inspect $ '_copyStreamUtf8 `hasNoType` ''Step
250-- inspect $ '_copyStreamUtf8 `hasNoType` ''Array.FlattenState
251-- inspect $ '_copyStreamUtf8 `hasNoType` ''D.ConcatMapUState
252#endif
254-- | Copy file
255{-# NOINLINE copyStreamUtf8 #-}
256copyStreamUtf8 :: Handle -> Handle -> IO ()
257copyStreamUtf8 inh outh =
258 Stream.fold (Handle.write outh)
259 $ Unicode.encodeUtf8
260 $ Unicode.decodeUtf8
261 $ Stream.unfold Handle.read inh
263#ifdef INSPECTION
264inspect $ hasNoTypeClasses 'copyStreamUtf8
265-- inspect $ 'copyStreamUtf8Lax `hasNoType` ''Step
266-- inspect $ 'copyStreamUtf8Lax `hasNoType` ''Array.FlattenState
267-- inspect $ 'copyStreamUtf8Lax `hasNoType` ''D.ConcatMapUState
268#endif
270o_1_space_decode_encode_read :: BenchEnv -> [Benchmark]
271o_1_space_decode_encode_read env =
272 [ bgroup "decode-encode"
273 [
274 -- This needs an ascii file, as decode just errors out.
275 mkBench "encodeLatin1' . decodeLatin1" env $ \inh outh ->
276 copyStreamLatin1' inh outh
277 , mkBench "encodeLatin1 . decodeLatin1" env $ \inh outh ->
278 copyStreamLatin1 inh outh
279#ifdef DEVBUILD
280 -- Requires valid unicode input
281 , mkBench "encodeUtf8' . decodeUtf8'" env $ \inh outh ->
282 _copyStreamUtf8' inh outh
283#endif
284 , mkBenchSmall "encodeUtf8 . decodeUtf8" env $ \inh outh ->
285 copyStreamUtf8 inh outh
286 ]
287 ]
289main :: IO ()
290main = do
291 env <- mkHandleBenchEnv
292 defaultMain (allBenchmarks env)
294 where
296 allBenchmarks env =
297 [ bgroup (o_1_space_prefix moduleName) $ Prelude.concat
298 [ o_1_space_copy_read_group_ungroup env
299 , o_1_space_decode_encode_chunked env
300 , o_1_space_decode_encode_read env
301 ]
302 ]