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
28-- Input and output file handles
29data Handles = Handles Handle Handle
32scratchDir = "benchmark/scratch/"
35infile = scratchDir ++ "in-100MB.txt"
38outfile = scratchDir ++ "out.txt"
40blockSize, blockCount :: Int
46fileSize = blockSize * blockCount
48foreign import ccall unsafe "u_iswspace"
49 iswspace :: Int -> Int
51-- Code copied from base/Data.Char to INLINE it
53isSpace :: Char -> Bool
54-- isSpace includes non-breaking space
55-- The magic 0x377 isn't really that magical. As of 2014, all the codepoints
56-- at or below 0x377 have been assigned, so we shouldn't have to worry about
57-- any new spaces appearing below there. It would probably be best to
58-- use branchless ||, but currently the eqLit transformation will undo that,
59-- so we'll do it like this until there's a way around that.
61 | uc <= 0x377 = uc == 32 || uc - 0x9 <= 4 || uc == 0xa0
62 | otherwise = iswspace (ord c) /= 0
64 uc = fromIntegral (ord c) :: Word
69 -- XXX will this work on windows/msys?
70 let cmd = "mkdir -p " ++ scratchDir
71 ++ "; test -e " ++ infile
72 ++ " || { echo \"creating input file " ++ infile
73 ++ "\" && dd if=/dev/random of=" ++ infile
74 ++ " bs=" ++ show blockSize
75 ++ " count=" ++ show blockCount
78 runProcess_ (shell cmd)
79 inHandle <- openFile infile ReadMode
80 outHandle <- openFile outfile WriteMode
81 href <- newIORef $ Handles inHandle outHandle
83-- This is a 500MB text file for text processing benchmarks. We cannot have it
84-- in the repo, therefore we use it locally with DEVBUILD conditional (enabled
85-- by "dev" cabal flag).
87 inText <- openFile "benchmark/text-processing/gutenberg-500.txt" ReadMode
92 [ mkBench "last" href $ do
93 Handles inh _ <- readIORef href
94 let s = FH.readArrays inh
98 Just arr -> A.readIndex arr (A.length arr - 1)
99 -- Note: this cannot be fairly compared with GNU wc -c or wc -m as
100 -- wc uses lseek to just determine the file size rather than reading
101 -- and counting characters.
102 , mkBench "length (bytecount)" href $ do
103 Handles inh _ <- readIORef href
104 let s = FH.readArrays inh
105 S.sum (S.map A.length s)
106 , mkBench "sum" href $ do
107 let foldlArr' f z = runIdentity . S.foldl' f z . A.read
108 Handles inh _ <- readIORef href
109 let s = FH.readArrays inh
110 S.foldl' (\acc arr -> acc + foldlArr' (+) 0 arr) 0 s
112 , bgroup "readStream"
113 [ mkBench "last" href $ do
114 Handles inh _ <- readIORef href
116 , mkBench "length (bytecount)" href $ do
117 Handles inh _ <- readIORef href
118 S.length $ FH.read inh
119 , mkBench "sum" href $ do
120 Handles inh _ <- readIORef href
124 [ mkBench "copy" href $ do
125 Handles inh outh <- readIORef href
126 let s = FH.readArrays inh
127 FH.writeArrays outh s
130 -- This takes a little longer therefore put under the dev conditional
131 , bgroup "copyStream"
132 [ mkBench "fromToHandle" href $ do
133 Handles inh outh <- readIORef href
134 FH.write outh (FH.read inh)
137 [ mkBench "chunksOf 1 (toArray)" href $ do
138 Handles inh _ <- readIORef href
139 S.length $ FL.chunksOf fileSize (A.toArrayN fileSize)
142 , mkBench "chunksOf 1" href $ do
143 Handles inh _ <- readIORef href
144 S.length $ FL.chunksOf 1 FL.drain (FH.read inh)
145 , mkBench "chunksOf 10" href $ do
146 Handles inh _ <- readIORef href
147 S.length $ FL.chunksOf 10 FL.drain (FH.read inh)
148 , mkBench "chunksOf 1000" href $ do
149 Handles inh _ <- readIORef href
150 S.length $ FL.chunksOf 1000 FL.drain (FH.read inh)
153 , let lf = fromIntegral (ord '\n')
154 lfarr = A.fromList [lf]
155 isSp = isSpace . chr . fromIntegral
156 toarr = A.fromList . map (fromIntegral . ord)
157 in bgroup "splitting"
159 [ mkBenchText "splitBy \\n (line count)" inText $ do
160 (S.length $ FL.splitBy (== lf) FL.drain
161 $ FH.read inText) >>= print
162 , mkBenchText "splitSuffixBy \\n (line count)" inText $ do
163 (S.length $ FL.splitSuffixBy (== lf) FL.drain
164 $ FH.read inText) >>= print
165 , mkBenchText "wordsBy isSpace (word count)" inText $ do
166 (S.length $ FL.wordsBy isSp FL.drain
167 $ FH.read inText) >>= print
170 , bgroup "empty-pattern"
171 [ mkBenchText "splitOn \"\"" inText $ do
172 (S.length $ FL.splitOn (A.fromList []) FL.drain
173 $ FH.read inText) >>= print
174 , mkBenchText "splitSuffixOn \"\"" inText $ do
175 (S.length $ FL.splitSuffixOn (A.fromList []) FL.drain
176 $ FH.read inText) >>= print
178 , bgroup "short-pattern"
179 [ mkBenchText "splitOn \\n (line count)" inText $ do
180 (S.length $ FL.splitOn lfarr FL.drain
181 $ FH.read inText) >>= print
182 , mkBenchText "splitSuffixOn \\n (line count)" inText $ do
183 (S.length $ FL.splitSuffixOn lfarr FL.drain
184 $ FH.read inText) >>= print
185 , mkBenchText "splitOn a" inText $ do
186 (S.length $ FL.splitOn (toarr "a") FL.drain
187 $ FH.read inText) >>= print
188 , mkBenchText "splitOn \\r\\n" inText $ do
189 (S.length $ FL.splitOn (toarr "\r\n") FL.drain
190 $ FH.read inText) >>= print
191 , mkBenchText "splitSuffixOn \\r\\n)" inText $ do
192 (S.length $ FL.splitSuffixOn (toarr "\r\n") FL.drain
193 $ FH.read inText) >>= print
194 , mkBenchText "splitOn aa" inText $ do
195 (S.length $ FL.splitOn (toarr "aa") FL.drain
196 $ FH.read inText) >>= print
197 , mkBenchText "splitOn aaaa" inText $ do
198 (S.length $ FL.splitOn (toarr "aaaa") FL.drain
199 $ FH.read inText) >>= print
200 , mkBenchText "splitOn abcdefgh" inText $ do
201 (S.length $ FL.splitOn (toarr "abcdefgh") FL.drain
202 $ FH.read inText) >>= print
204 , bgroup "long-pattern"
205 [ mkBenchText "splitOn abcdefghi" inText $ do
206 (S.length $ FL.splitOn (toarr "abcdefghi") FL.drain
207 $ FH.read inText) >>= print
208 , mkBenchText "splitOn catcatcatcatcat" inText $ do
209 (S.length $ FL.splitOn (toarr "catcatcatcatcat") FL.drain
210 $ FH.read inText) >>= print
211 , mkBenchText "splitOn abc...xyz" inText $ do
212 (S.length $ FL.splitOn
213 (toarr "abcdefghijklmnopqrstuvwxyz")
215 $ FH.read inText) >>= print
216 , mkBenchText "splitSuffixOn abc...xyz" inText $ do
217 (S.length $ FL.splitSuffixOn
218 (toarr "abcdefghijklmnopqrstuvwxyz")
220 $ FH.read inText) >>= print
228 mkBench :: NFData b => String -> IORef Handles -> IO b -> Benchmark
229 mkBench name ref action =
230 bench name $ perRunEnv (do
231 (Handles inh outh) <- readIORef ref
234 inHandle <- openFile infile ReadMode
235 outHandle <- openFile outfile WriteMode
236 writeIORef ref (Handles inHandle outHandle)
241 mkBenchText name h action =
242 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