on master help sign in
Move array stream bench/tests to separate modules
commitcd916f54bb35705c36d613f937a2d5baa1593c1e authorAdithya Kumar <adithya@composewell.com> dateSat 6 Feb 2021, 15:06 (6 years ago) parente0471f2
7 files changed+231−145
benchmark/Streamly/Benchmark/Data/Array/Stream/Foreign.hs+207 benchmark/Streamly/Benchmark/Data/Parser/ParserD.hs+5−4 benchmark/Streamly/Benchmark/FileSystem/Handle/Read.hs+1−95 benchmark/Streamly/Benchmark/FileSystem/Handle/ReadWrite.hs−45 benchmark/streamly-benchmarks.cabal+15−1 hie.yaml+2 streamly.cabal+1
benchmark/Streamly/Benchmark/Data/Array/Stream/Foreign.hsnew file+207
@@ -0,0 +1,207 @@
1+-- |
2+-- Module : Streamly.Benchmark.Data.ParserD
3+-- Copyright : (c) 2020 Composewell Technologies
4+--
5+-- License : BSD-3-Clause
6+-- Maintainer : streamly@composewell.com
7+
8+{-# LANGUAGE CPP #-}
9+{-# LANGUAGE FlexibleContexts #-}
10+{-# LANGUAGE ScopedTypeVariables #-}
11+
12+#ifdef __HADDOCK_VERSION__
13+#undef INSPECTION
14+#endif
15+
16+#ifdef INSPECTION
17+{-# LANGUAGE TemplateHaskell #-}
18+{-# OPTIONS_GHC -fplugin Test.Inspection.Plugin #-}
19+#endif
20+
21+module Main
22+ (
23+ main
24+ ) where
25+
26+import Data.Functor.Identity (runIdentity)
27+import Data.Word (Word8)
28+import System.IO (Handle)
29+import Prelude hiding ()
30+
31+import qualified Streamly.Prelude as Stream
32+import qualified Streamly.Internal.Data.Array.Foreign as Array
33+import qualified Streamly.Internal.Data.Array.Stream.Foreign as ArrayStream
34+import qualified Streamly.Internal.FileSystem.Handle as Handle
35+import qualified Streamly.Internal.Unicode.Stream as Unicode
36+
37+import Gauge hiding (env)
38+import Streamly.Benchmark.Common
39+import Streamly.Benchmark.Common.Handle
40+
41+#ifdef INSPECTION
42+import Foreign.Storable (Storable)
43+import Streamly.Internal.Data.Stream.StreamD.Type (Step(..))
44+import Test.Inspection
45+#endif
46+
47+-------------------------------------------------------------------------------
48+-- read chunked using toChunks
49+-------------------------------------------------------------------------------
50+
51+-- | Get the last byte from a file bytestream.
52+toChunksLast :: Handle -> IO (Maybe Word8)
53+toChunksLast inh = do
54+ let s = Handle.toChunks inh
55+ larr <- Stream.last s
56+ return $ case larr of
57+ Nothing -> Nothing
58+ Just arr -> Array.readIndex arr (Array.length arr - 1)
59+
60+#ifdef INSPECTION
61+inspect $ hasNoTypeClasses 'toChunksLast
62+inspect $ 'toChunksLast `hasNoType` ''Step
63+#endif
64+
65+-- | Count the number of bytes in a file.
66+toChunksSumLengths :: Handle -> IO Int
67+toChunksSumLengths inh =
68+ let s = Handle.toChunks inh
69+ in Stream.sum (Stream.map Array.length s)
70+
71+#ifdef INSPECTION
72+inspect $ hasNoTypeClasses 'toChunksSumLengths
73+inspect $ 'toChunksSumLengths `hasNoType` ''Step
74+#endif
75+
76+-- | Sum the bytes in a file.
77+toChunksCountBytes :: Handle -> IO Word8
78+toChunksCountBytes inh = do
79+ let foldlArr' f z = runIdentity . Stream.foldl' f z . Array.toStream
80+ let s = Handle.toChunks inh
81+ Stream.foldl' (\acc arr -> acc + foldlArr' (+) 0 arr) 0 s
82+
83+#ifdef INSPECTION
84+inspect $ hasNoTypeClasses 'toChunksCountBytes
85+inspect $ 'toChunksCountBytes `hasNoType` ''Step
86+#endif
87+
88+toChunksDecodeUtf8Arrays :: Handle -> IO ()
89+toChunksDecodeUtf8Arrays =
90+ Stream.drain . Unicode.decodeUtf8Arrays . Handle.toChunks
91+
92+#ifdef INSPECTION
93+inspect $ hasNoTypeClasses 'toChunksDecodeUtf8Arrays
94+-- inspect $ 'toChunksDecodeUtf8ArraysLenient `hasNoType` ''Step
95+#endif
96+
97+-------------------------------------------------------------------------------
98+-- Splitting
99+-------------------------------------------------------------------------------
100+
101+-- | Count the number of lines in a file.
102+toChunksSplitOnSuffix :: Handle -> IO Int
103+toChunksSplitOnSuffix =
104+ Stream.length . ArrayStream.splitOnSuffix 10 . Handle.toChunks
105+
106+#ifdef INSPECTION
107+inspect $ hasNoTypeClasses 'toChunksSplitOnSuffix
108+inspect $ 'toChunksSplitOnSuffix `hasNoType` ''Step
109+#endif
110+
111+-- XXX use a word splitting combinator instead of splitOn and test it.
112+-- | Count the number of words in a file.
113+toChunksSplitOn :: Handle -> IO Int
114+toChunksSplitOn = Stream.length . ArrayStream.splitOn 32 . Handle.toChunks
115+
116+#ifdef INSPECTION
117+inspect $ hasNoTypeClasses 'toChunksSplitOn
118+inspect $ 'toChunksSplitOn `hasNoType` ''Step
119+#endif
120+
121+o_1_space_read_chunked :: BenchEnv -> [Benchmark]
122+o_1_space_read_chunked env =
123+ -- read using toChunks instead of read
124+ [ bgroup "reduce/toChunks"
125+ [ mkBench "Stream.last" env $ \inH _ ->
126+ toChunksLast inH
127+ -- Note: this cannot be fairly compared with GNU wc -c or wc -m as
128+ -- wc uses lseek to just determine the file size rather than reading
129+ -- and counting characters.
130+ , mkBench "Stream.sum . Stream.map Array.length" env $ \inH _ ->
131+ toChunksSumLengths inH
132+ , mkBench "splitOnSuffix" env $ \inH _ ->
133+ toChunksSplitOnSuffix inH
134+ , mkBench "splitOn" env $ \inH _ ->
135+ toChunksSplitOn inH
136+ , mkBench "countBytes" env $ \inH _ ->
137+ toChunksCountBytes inH
138+ , mkBenchSmall "decodeUtf8Arrays" env $ \inH _ ->
139+ toChunksDecodeUtf8Arrays inH
140+ ]
141+ ]
142+
143+-------------------------------------------------------------------------------
144+-- copy with group/ungroup transformations
145+-------------------------------------------------------------------------------
146+
147+-- | Lines and unlines
148+{-# NOINLINE copyChunksSplitInterposeSuffix #-}
149+copyChunksSplitInterposeSuffix :: Handle -> Handle -> IO ()
150+copyChunksSplitInterposeSuffix inh outh =
151+ Stream.fold (Handle.write outh)
152+ $ ArrayStream.interposeSuffix 10
153+ $ ArrayStream.splitOnSuffix 10
154+ $ Handle.toChunks inh
155+
156+#ifdef INSPECTION
157+inspect $ hasNoTypeClassesExcept 'copyChunksSplitInterposeSuffix [''Storable]
158+inspect $ 'copyChunksSplitInterposeSuffix `hasNoType` ''Step
159+#endif
160+
161+-- | Words and unwords
162+{-# NOINLINE copyChunksSplitInterpose #-}
163+copyChunksSplitInterpose :: Handle -> Handle -> IO ()
164+copyChunksSplitInterpose inh outh =
165+ Stream.fold (Handle.write outh)
166+ $ ArrayStream.interpose 32
167+ -- XXX this is not correct word splitting combinator
168+ $ ArrayStream.splitOn 32
169+ $ Handle.toChunks inh
170+
171+#ifdef INSPECTION
172+inspect $ hasNoTypeClassesExcept 'copyChunksSplitInterpose [''Storable]
173+inspect $ 'copyChunksSplitInterpose `hasNoType` ''Step
174+#endif
175+
176+o_1_space_copy_toChunks_group_ungroup :: BenchEnv -> [Benchmark]
177+o_1_space_copy_toChunks_group_ungroup env =
178+ [ bgroup "copy/toChunks/group-ungroup"
179+ [ mkBench "interposeSuffix . splitOnSuffix" env $ \inh outh ->
180+ copyChunksSplitInterposeSuffix inh outh
181+ , mkBenchSmall "interpose . splitOn" env $ \inh outh ->
182+ copyChunksSplitInterpose inh outh
183+ ]
184+ ]
185+
186+-------------------------------------------------------------------------------
187+-- Driver
188+-------------------------------------------------------------------------------
189+
190+moduleName :: String
191+moduleName = "Data.Array.Stream.Foreign"
192+
193+main :: IO ()
194+main = do
195+ (value, cfg, benches) <- parseCLIOpts defaultStreamSize
196+ env <- mkHandleBenchEnv
197+ value `seq` runMode (mode cfg) cfg benches
198+ (allBenchmarks env)
199+
200+ where
201+
202+ allBenchmarks env =
203+ [ bgroup (o_1_space_prefix moduleName)
204+ ( o_1_space_read_chunked env
205+ ++ o_1_space_copy_toChunks_group_ungroup env
206+ )
207+ ]
benchmark/Streamly/Benchmark/Data/Parser/ParserD.hs+5−4
@@ -361,17 +361,18 @@ o_n_space_serial value =
361361main :: IO ()
362362main = do
363363 (value, cfg, benches) <- parseCLIOpts defaultStreamSize
364− arrays <- IP.toList $ IP.arraysOf 100 $ sourceUnfoldrM value 0
365− value `seq` runMode (mode cfg) cfg benches (allBenchmarks value arrays)
364+ arraysSmall <- IP.toList $ IP.arraysOf 100 $ sourceUnfoldrM value 0
365+ value `seq` runMode (mode cfg) cfg benches
366+ (allBenchmarks value arraysSmall)
366367
367368 where
368369
369− allBenchmarks value arrays =
370+ allBenchmarks value arraysSmall =
370371 [ bgroup (o_1_space_prefix moduleName) (o_1_space_serial value)
371372 , bgroup (o_1_space_prefix moduleName) (o_1_space_serial_spanning value)
372373 , bgroup (o_1_space_prefix moduleName) (o_1_space_serial_nested value)
373374 , bgroup (o_1_space_prefix moduleName)
374− (o_1_space_serial_unfold value arrays)
375+ (o_1_space_serial_unfold value arraysSmall)
375376 , bgroup (o_n_heap_prefix moduleName) (o_n_heap_serial value)
376377 , bgroup (o_n_space_prefix moduleName) (o_n_space_serial value)
377378 ]
benchmark/Streamly/Benchmark/FileSystem/Handle/Read.hs+1−95
@@ -22,7 +22,6 @@ module Handle.Read
2222 (allBenchmarks)
2323where
2424
25−import Data.Functor.Identity (runIdentity)
2625import Data.Word (Word8)
2726import GHC.Magic (inline)
2827#if __GLASGOW_HASKELL__ >= 802
@@ -38,7 +37,6 @@ import qualified Streamly.Internal.Data.Array.Foreign.Type as AT
3837import qualified Streamly.Internal.Data.Fold as FL
3938import qualified Streamly.Internal.Data.Stream.IsStream as IP
4039import qualified Streamly.Internal.FileSystem.Handle as IFH
41−import qualified Streamly.Internal.Data.Array.Stream.Foreign as AS
4240import qualified Streamly.Internal.Unicode.Stream as IUS
4341import qualified Streamly.Prelude as S
4442import qualified Streamly.Unicode.Stream as SS
@@ -57,97 +55,6 @@ import qualified Streamly.Internal.Data.Unfold as IUF
5755import Test.Inspection
5856#endif
5957
60−-------------------------------------------------------------------------------
61−-- read chunked using toChunks
62−-------------------------------------------------------------------------------
63−
64−-- | Get the last byte from a file bytestream.
65−toChunksLast :: Handle -> IO (Maybe Word8)
66−toChunksLast inh = do
67− let s = IFH.toChunks inh
68− larr <- S.last s
69− return $ case larr of
70− Nothing -> Nothing
71− Just arr -> A.readIndex arr (A.length arr - 1)
72−
73−#ifdef INSPECTION
74−inspect $ hasNoTypeClasses 'toChunksLast
75−inspect $ 'toChunksLast `hasNoType` ''Step
76−#endif
77−
78−-- | Count the number of bytes in a file.
79−toChunksSumLengths :: Handle -> IO Int
80−toChunksSumLengths inh =
81− let s = IFH.toChunks inh
82− in S.sum (S.map A.length s)
83−
84−#ifdef INSPECTION
85−inspect $ hasNoTypeClasses 'toChunksSumLengths
86−inspect $ 'toChunksSumLengths `hasNoType` ''Step
87−#endif
88−
89−-- | Count the number of lines in a file.
90−toChunksSplitOnSuffix :: Handle -> IO Int
91−toChunksSplitOnSuffix = S.length . AS.splitOnSuffix 10 . IFH.toChunks
92−
93−#ifdef INSPECTION
94−inspect $ hasNoTypeClasses 'toChunksSplitOnSuffix
95−inspect $ 'toChunksSplitOnSuffix `hasNoType` ''Step
96−#endif
97−
98−-- XXX use a word splitting combinator instead of splitOn and test it.
99−-- | Count the number of words in a file.
100−toChunksSplitOn :: Handle -> IO Int
101−toChunksSplitOn = S.length . AS.splitOn 32 . IFH.toChunks
102−
103−#ifdef INSPECTION
104−inspect $ hasNoTypeClasses 'toChunksSplitOn
105−inspect $ 'toChunksSplitOn `hasNoType` ''Step
106−#endif
107−
108−-- | Sum the bytes in a file.
109−toChunksCountBytes :: Handle -> IO Word8
110−toChunksCountBytes inh = do
111− let foldlArr' f z = runIdentity . S.foldl' f z . A.toStream
112− let s = IFH.toChunks inh
113− S.foldl' (\acc arr -> acc + foldlArr' (+) 0 arr) 0 s
114−
115−#ifdef INSPECTION
116−inspect $ hasNoTypeClasses 'toChunksCountBytes
117−inspect $ 'toChunksCountBytes `hasNoType` ''Step
118−#endif
119−
120−toChunksDecodeUtf8Arrays :: Handle -> IO ()
121−toChunksDecodeUtf8Arrays =
122− S.drain . IUS.decodeUtf8Arrays . IFH.toChunks
123−
124−#ifdef INSPECTION
125−inspect $ hasNoTypeClasses 'toChunksDecodeUtf8Arrays
126−-- inspect $ 'toChunksDecodeUtf8ArraysLenient `hasNoType` ''Step
127−#endif
128−
129−o_1_space_read_chunked :: BenchEnv -> [Benchmark]
130−o_1_space_read_chunked env =
131− -- read using toChunks instead of read
132− [ bgroup "reduce/toChunks"
133− [ mkBench "S.last" env $ \inH _ ->
134− toChunksLast inH
135− -- Note: this cannot be fairly compared with GNU wc -c or wc -m as
136− -- wc uses lseek to just determine the file size rather than reading
137− -- and counting characters.
138− , mkBench "S.sum . S.map A.length" env $ \inH _ ->
139− toChunksSumLengths inH
140− , mkBench "AS.splitOnSuffix" env $ \inH _ ->
141− toChunksSplitOnSuffix inH
142− , mkBench "AS.splitOn" env $ \inH _ ->
143− toChunksSplitOn inH
144− , mkBench "countBytes" env $ \inH _ ->
145− toChunksCountBytes inH
146− , mkBenchSmall "US.decodeUtf8Arrays" env $ \inH _ ->
147− toChunksDecodeUtf8Arrays inH
148− ]
149− ]
150−
15158-- TBD reading with unfold
15259
15360-------------------------------------------------------------------------------
@@ -388,8 +295,7 @@ o_1_space_reduce_read_grouped env =
388295
389296allBenchmarks :: BenchEnv -> [Benchmark]
390297allBenchmarks env = Prelude.concat
391− [ o_1_space_read_chunked env
392− , o_1_space_reduce_read env
298+ [ o_1_space_reduce_read env
393299 , o_1_space_reduce_toBytes env
394300 , o_1_space_reduce_read_grouped env
395301 ]
benchmark/Streamly/Benchmark/FileSystem/Handle/ReadWrite.hs−45
@@ -29,7 +29,6 @@ import Streamly.Internal.Data.Array.Foreign.Type (defaultChunkSize)
2929import qualified Streamly.FileSystem.Handle as FH
3030import qualified Streamly.Internal.Data.Unfold as IUF
3131import qualified Streamly.Internal.FileSystem.Handle as IFH
32−import qualified Streamly.Internal.Data.Array.Stream.Foreign as AS
3332import qualified Streamly.Data.Array.Foreign as A
3433import qualified Streamly.Prelude as S
3534
@@ -37,7 +36,6 @@ import Gauge hiding (env)
3736import Streamly.Benchmark.Common.Handle
3837
3938#ifdef INSPECTION
40−import Foreign.Storable (Storable)
4139import Streamly.Internal.Data.Stream.StreamD.Type (Step(..))
4240
4341import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
@@ -206,48 +204,6 @@ o_1_space_copy env =
206204 ]
207205 ]
208206
209−-------------------------------------------------------------------------------
210−-- copy with group/ungroup transformations
211−-------------------------------------------------------------------------------
212−
213−-- | Lines and unlines
214−copyChunksSplitInterposeSuffix :: Handle -> Handle -> IO ()
215−copyChunksSplitInterposeSuffix inh outh =
216− S.fold (IFH.write outh)
217− $ AS.interposeSuffix 10
218− $ AS.splitOnSuffix 10
219− $ IFH.toChunks inh
220−
221−#ifdef INSPECTION
222−inspect $ hasNoTypeClassesExcept 'copyChunksSplitInterposeSuffix [''Storable]
223−inspect $ 'copyChunksSplitInterposeSuffix `hasNoType` ''Step
224−#endif
225−
226−-- | Words and unwords
227−copyChunksSplitInterpose :: Handle -> Handle -> IO ()
228−copyChunksSplitInterpose inh outh =
229− S.fold (IFH.write outh)
230− $ AS.interpose 32
231− -- XXX this is not correct word splitting combinator
232− $ AS.splitOn 32
233− $ IFH.toChunks inh
234−
235−#ifdef INSPECTION
236−inspect $ hasNoTypeClassesExcept 'copyChunksSplitInterpose [''Storable]
237−inspect $ 'copyChunksSplitInterpose `hasNoType` ''Step
238−#endif
239−
240−o_1_space_copy_toChunks_group_ungroup :: BenchEnv -> [Benchmark]
241−o_1_space_copy_toChunks_group_ungroup env =
242− [ bgroup "copy/toChunks/group-ungroup"
243− [ mkBench "AS.interposeSuffix . AS.splitOnSuffix" env $ \inh outh ->
244− copyChunksSplitInterposeSuffix inh outh
245− , mkBenchSmall "AS.interpose . AS.splitOn" env $ \inh outh ->
246− copyChunksSplitInterpose inh outh
247− ]
248− ]
249−
250−
251207-------------------------------------------------------------------------------
252208--
253209-------------------------------------------------------------------------------
@@ -258,5 +214,4 @@ allBenchmarks env = Prelude.concat
258214 , o_1_space_copy_read env
259215 , o_1_space_copy_fromBytes env
260216 , o_1_space_copy env
261− , o_1_space_copy_toChunks_group_ungroup env
262217 ]
benchmark/streamly-benchmarks.cabal+15−1
@@ -299,7 +299,6 @@ benchmark Data.Parser.ParserD
299299 if flag(limit-build-mem)
300300 ghc-options: +RTS -M750M -RTS
301301
302−
303302benchmark Data.Parser.ParserK
304303 import: bench-options
305304 type: exitcode-stdio-1.0
@@ -458,6 +457,21 @@ benchmark Data.Array.Foreign
458457 other-modules: Streamly.Benchmark.Data.ArrayOps
459458 cpp-options: -DMEMORY_ARRAY
460459
460+-------------------------------------------------------------------------------
461+-- Array Stream Benchmarks
462+-------------------------------------------------------------------------------
463+
464+benchmark Data.Array.Stream.Foreign
465+ import: bench-options
466+ type: exitcode-stdio-1.0
467+ hs-source-dirs: Streamly/Benchmark/Data/Array/Stream
468+ main-is: Foreign.hs
469+ if impl(ghcjs)
470+ buildable: False
471+ else
472+ buildable: True
473+ build-depends: exceptions >= 0.8 && < 0.11
474+
461475-------------------------------------------------------------------------------
462476-- FileIO Benchmarks
463477-------------------------------------------------------------------------------
hie.yaml+2
@@ -4,6 +4,8 @@ cradle:
44 config:
55 cradle:
66 cabal:
7+ - path: "./benchmark/Streamly/Benchmark/Data/Array/Stream/Foreign.hs"
8+ component: "bench:Data.Array.Stream.Foreign"
79 - path: "./benchmark/Streamly/Benchmark/Data/Fold.hs"
810 component: "bench:Data.Fold"
911 - path: "./benchmark/Streamly/Benchmark/Data/Parser.hs"
streamly.cabal+1
@@ -71,6 +71,7 @@ extra-source-files:
7171 benchmark/*.hs
7272 benchmark/README.md
7373 benchmark/Streamly/Benchmark/Data/*.hs
74+ benchmark/Streamly/Benchmark/Data/Array/Stream/Foreign.hs
7475 benchmark/Streamly/Benchmark/Data/Parser/*.hs
7576 benchmark/Streamly/Benchmark/Data/Stream/*.hs
7677 benchmark/Streamly/Benchmark/FileSystem/*.hs