3-- Copyright : (c) 2019 Harendra Kumar
6-- Maintainer : harendra.kumar@gmail.com
10import Control.DeepSeq (NFData)
11import Data.Functor.Identity (runIdentity)
12import System.IO (openFile, IOMode(..), Handle, hClose)
13import System.Process.Typed (shell, runProcess_)
18import qualified Streamly.FileSystem.Handle as FH
19import qualified Streamly.Mem.Array as A
20import qualified Streamly.Prelude as S
23import Data.Char (ord, chr)
24import System.IO (hSeek, SeekMode(..))
25import qualified Streamly.Fold as FL
26import qualified Streamly.String as SS
29-- Input and output file handles
30data Handles = Handles Handle Handle
33scratchDir = "benchmark/scratch/"
36infile = scratchDir ++ "in-100MB.txt"
39outfile = scratchDir ++ "out.txt"
41blockSize, blockCount :: Int
47fileSize = blockSize * blockCount
49foreign import ccall unsafe "u_iswspace"
50 iswspace :: Int -> Int
52-- Code copied from base/Data.Char to INLINE it
54isSpace :: Char -> Bool
55-- isSpace includes non-breaking space
56-- The magic 0x377 isn't really that magical. As of 2014, all the codepoints
57-- at or below 0x377 have been assigned, so we shouldn't have to worry about
58-- any new spaces appearing below there. It would probably be best to
59-- use branchless ||, but currently the eqLit transformation will undo that,
60-- so we'll do it like this until there's a way around that.
62 | uc <= 0x377 = uc == 32 || uc - 0x9 <= 4 || uc == 0xa0
63 | otherwise = iswspace (ord c) /= 0
65 uc = fromIntegral (ord c) :: Word
70 -- XXX will this work on windows/msys?
71 let cmd = "mkdir -p " ++ scratchDir
72 ++ "; test -e " ++ infile
73 ++ " || { echo \"creating input file " ++ infile
74 ++ "\" && dd if=/dev/random of=" ++ infile
75 ++ " bs=" ++ show blockSize
76 ++ " count=" ++ show blockCount
79 runProcess_ (shell cmd)
80 inHandle <- openFile infile ReadMode
81 outHandle <- openFile outfile WriteMode
82 href <- newIORef $ Handles inHandle outHandle
84-- This is a 500MB text file for text processing benchmarks. We cannot have it
85-- in the repo, therefore we use it locally with DEVBUILD conditional (enabled
86-- by "dev" cabal flag).
88 inText <- openFile "benchmark/text-processing/gutenberg-500.txt" ReadMode
93 [ mkBench "last" href $ do
94 Handles inh _ <- readIORef href
95 let s = FH.readArrays inh
98 Nothing -> return Nothing
99 Just arr -> A.readIndex arr (A.length arr - 1)
100 -- Note: this cannot be fairly compared with GNU wc -c or wc -m as
101 -- wc uses lseek to just determine the file size rather than reading
102 -- and counting characters.
103 , mkBench "length (bytecount)" href $ do
104 Handles inh _ <- readIORef href
105 let s = FH.readArrays inh
106 S.sum (S.map A.length s)
107 , mkBench "sum" href $ do
108 let foldlArr' f z = runIdentity . S.foldl' f z . A.read
109 Handles inh _ <- readIORef href
110 let s = FH.readArrays inh
111 S.foldl' (\acc arr -> acc + foldlArr' (+) 0 arr) 0 s
113 , bgroup "readStream"
114 [ mkBench "last" href $ do
115 Handles inh _ <- readIORef href
117 , mkBench "length (bytecount)" href $ do
118 Handles inh _ <- readIORef href
119 S.length $ FH.read inh
120 , mkBench "sum" href $ do
121 Handles inh _ <- readIORef href
125 [ mkBench "copy" href $ do
126 Handles inh outh <- readIORef href
127 let s = FH.readArrays inh
128 FH.writeArrays outh s
131 -- This takes a little longer therefore put under the dev conditional
132 , bgroup "copyStream"
133 [ mkBench "fromToHandle" href $ do
134 Handles inh outh <- readIORef href
135 FH.write outh (FH.read inh)
137 -- This needs an ascii file, as decode just errors out.
138 , bgroup "decode-encode"
139 [ mkBench "encodeChar8-decodeChar8" href $ do
140 Handles inh outh <- readIORef href
145 , mkBench "encodeUtf8-decodeUtf8" href $ do
146 Handles inh outh <- readIORef href
153 [ mkBench "chunksOf 1 (toArray)" href $ do
154 Handles inh _ <- readIORef href
155 S.length $ FL.chunksOf fileSize (A.toArrayN fileSize)
158 , mkBench "chunksOf 1" href $ do
159 Handles inh _ <- readIORef href
160 S.length $ FL.chunksOf 1 FL.drain (FH.read inh)
161 , mkBench "chunksOf 10" href $ do
162 Handles inh _ <- readIORef href
163 S.length $ FL.chunksOf 10 FL.drain (FH.read inh)
164 , mkBench "chunksOf 1000" href $ do
165 Handles inh _ <- readIORef href
166 S.length $ FL.chunksOf 1000 FL.drain (FH.read inh)
168 , bgroup "group-ungroup"
169 [ mkBench "lines-unlines" href $ do
170 Handles inh outh <- readIORef href
177 , mkBench "lines-unlines-arrays" href $ do
178 Handles inh outh <- readIORef href
179 FH.writeArraysPackedUpto (1024*1024) outh
180 $ S.insertAfterEach (return $ A.fromList [10])
182 $ FH.readArraysOfUpto (1024*1024) inh
183 , mkBench "words-unwords" href $ do
184 Handles inh outh <- readIORef href
193 , let lf = fromIntegral (ord '\n')
194 lfarr = A.fromList [lf]
195 isSp = isSpace . chr . fromIntegral
196 toarr = A.fromList . map (fromIntegral . ord)
197 in bgroup "splitting"
199 [ mkBenchText "splitBy \\n (line count)" inText $ do
200 (S.length $ FL.splitBy (== lf) FL.drain
201 $ FH.read inText) >>= print
202 , mkBenchText "splitSuffixBy \\n (line count)" inText $ do
203 (S.length $ FL.splitSuffixBy (== lf) FL.drain
204 $ FH.read inText) >>= print
205 , mkBenchText "wordsBy isSpace (word count)" inText $ do
206 (S.length $ FL.wordsBy isSp FL.drain
207 $ FH.read inText) >>= print
210 , bgroup "empty-pattern"
211 [ mkBenchText "splitOn \"\"" inText $ do
212 (S.length $ FL.splitOn (A.fromList []) FL.drain
213 $ FH.read inText) >>= print
214 , mkBenchText "splitSuffixOn \"\"" inText $ do
215 (S.length $ FL.splitSuffixOn (A.fromList []) FL.drain
216 $ FH.read inText) >>= print
218 , bgroup "short-pattern"
219 [ mkBenchText "splitOn \\n (line count)" inText $ do
220 (S.length $ FL.splitOn lfarr FL.drain
221 $ FH.read inText) >>= print
222 , mkBenchText "splitSuffixOn \\n (line count)" inText $ do
223 (S.length $ FL.splitSuffixOn lfarr FL.drain
224 $ FH.read inText) >>= print
225 , mkBenchText "splitOn a" inText $ do
226 (S.length $ FL.splitOn (toarr "a") FL.drain
227 $ FH.read inText) >>= print
228 , mkBenchText "splitOn \\r\\n" inText $ do
229 (S.length $ FL.splitOn (toarr "\r\n") FL.drain
230 $ FH.read inText) >>= print
231 , mkBenchText "splitSuffixOn \\r\\n)" inText $ do
232 (S.length $ FL.splitSuffixOn (toarr "\r\n") FL.drain
233 $ FH.read inText) >>= print
234 , mkBenchText "splitOn aa" inText $ do
235 (S.length $ FL.splitOn (toarr "aa") FL.drain
236 $ FH.read inText) >>= print
237 , mkBenchText "splitOn aaaa" inText $ do
238 (S.length $ FL.splitOn (toarr "aaaa") FL.drain
239 $ FH.read inText) >>= print
240 , mkBenchText "splitOn abcdefgh" inText $ do
241 (S.length $ FL.splitOn (toarr "abcdefgh") FL.drain
242 $ FH.read inText) >>= print
244 , bgroup "long-pattern"
245 [ mkBenchText "splitOn abcdefghi" inText $ do
246 (S.length $ FL.splitOn (toarr "abcdefghi") FL.drain
247 $ FH.read inText) >>= print
248 , mkBenchText "splitOn catcatcatcatcat" inText $ do
249 (S.length $ FL.splitOn (toarr "catcatcatcatcat") FL.drain
250 $ FH.read inText) >>= print
251 , mkBenchText "splitOn abc...xyz" inText $ do
252 (S.length $ FL.splitOn
253 (toarr "abcdefghijklmnopqrstuvwxyz")
255 $ FH.read inText) >>= print
256 , mkBenchText "splitSuffixOn abc...xyz" inText $ do
257 (S.length $ FL.splitSuffixOn
258 (toarr "abcdefghijklmnopqrstuvwxyz")
260 $ FH.read inText) >>= print
268 mkBench :: NFData b => String -> IORef Handles -> IO b -> Benchmark
269 mkBench name ref action =
270 bench name $ perRunEnv (do
271 (Handles inh outh) <- readIORef ref
274 inHandle <- openFile infile ReadMode
275 outHandle <- openFile outfile WriteMode
276 writeIORef ref (Handles inHandle outHandle)
281 mkBenchText name h action =
282 bench name $ perRunEnv (hSeek h AbsoluteSeek 0) (\_ -> action)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list