streamly-process-0.1.0: Benchmark/System/Process.hs
{-# LANGUAGE ScopedTypeVariables #-}
module Main (main) where
import Control.Exception (finally)
import Data.Either (isRight, fromRight, isLeft, fromLeft)
import Data.Word (Word8)
import Gauge (defaultMain, bench, nfIO)
import System.Directory (removeFile, findExecutable)
import System.IO
( Handle
, IOMode(..)
, openFile
, hClose
)
import System.Process (proc, createProcess, waitForProcess, callCommand)
import qualified Streamly.Data.Fold as FL
import qualified Streamly.FileSystem.Handle as FH
import qualified Streamly.Prelude as S
import qualified Streamly.System.Process as Proc
-- Internal imports
import qualified Streamly.Internal.FileSystem.Handle
as FH (toBytes, toChunks, putBytes, putChunks)
import qualified Streamly.Internal.System.Process as Proc
-- XXX replace with streamly versions once they are fixed
{-# INLINE rights #-}
rights :: (S.IsStream t, Monad m, Functor (t m)) => t m (Either a b) -> t m b
rights = fmap (fromRight undefined) . S.filter isRight
{-# INLINE lefts #-}
lefts :: (S.IsStream t, Monad m, Functor (t m)) => t m (Either a b) -> t m a
lefts = fmap (fromLeft undefined) . S.filter isLeft
-------------------------------------------------------------------------------
-- Constants and utils
-------------------------------------------------------------------------------
_a :: Word8
_a = 97
-- XXX portability on macOS
devRandom :: String
devRandom = "/dev/urandom"
devNull :: String
devNull = "/dev/null"
which :: String -> IO FilePath
which cmd = do
r <- findExecutable cmd
case r of
Just path -> return path
_ -> error $ "Required command " ++ cmd ++ " not found"
-------------------------------------------------------------------------------
-- Create a data file filled with random data
-------------------------------------------------------------------------------
ddBlockSize :: Int
ddBlockSize = 1024 * 1024
ddBlockCount :: Int
ddBlockCount = 10
largeByteFile :: String
largeByteFile = "./largeByteFile"
generateByteFile :: IO ()
generateByteFile = do
ddPath <- which "dd"
let procObj = proc ddPath [
"if=" ++ devRandom,
"of=" ++ largeByteFile,
"count=" ++ show ddBlockCount,
"bs=" ++ show ddBlockSize
]
(_, _, _, procHandle) <- createProcess procObj
_ <- waitForProcess procHandle
return ()
-------------------------------------------------------------------------------
-- Create a file filled with ascii chars
-------------------------------------------------------------------------------
largeCharFile :: String
largeCharFile = "./largeCharFile"
numCharInCharFile :: Int
numCharInCharFile = 10 * 1024 * 1024
generateCharFile :: IO ()
generateCharFile = do
handle <- openFile largeCharFile WriteMode
FH.putBytes handle (S.replicate numCharInCharFile _a)
hClose handle
-------------------------------------------------------------------------------
-- Create a utility that writes to stderr
-------------------------------------------------------------------------------
trToStderr :: String
trToStderr = "./writeTrToError.sh"
trToStderrContent :: String
trToStderrContent =
"tr [a-z] [A-Z] <&0 >&2"
createExecutable :: IO ()
createExecutable = do
writeFile trToStderr trToStderrContent
callCommand ("chmod +x " ++ trToStderr)
-------------------------------------------------------------------------------
-- Create and delete the temp data/exec files
-------------------------------------------------------------------------------
generateFiles :: IO ()
generateFiles = do
createExecutable
generateByteFile
generateCharFile
deleteFiles :: IO ()
deleteFiles = do
removeFile trToStderr
removeFile largeByteFile
removeFile largeCharFile
-------------------------------------------------------------------------------
-- Benchmark functions
-------------------------------------------------------------------------------
toBytes' :: String-> Handle -> IO ()
toBytes' catPath outH =
FH.putBytes outH
$ rights
$ Proc.toBytes' catPath [largeByteFile]
toChunks' :: String -> Handle -> IO ()
toChunks' catPath hdl =
FH.putChunks hdl
$ rights
$ Proc.toChunks' catPath [largeByteFile]
processBytes' :: String-> Handle -> IO ()
processBytes' trPath outputHdl = do
inputHdl <- openFile largeCharFile ReadMode
_ <- S.fold (FL.partition (FH.write outputHdl) (FH.write outputHdl))
$ Proc.processBytes'
trPath
["[a-z]", "[A-Z]"]
$ FH.toBytes inputHdl
hClose inputHdl
processBytes :: String-> Handle -> IO ()
processBytes trPath outputHdl = do
inputHdl <- openFile largeCharFile ReadMode
FH.putBytes outputHdl
$ Proc.processBytes
trPath
["[a-z]", "[A-Z]"]
$ FH.toBytes inputHdl
hClose inputHdl
processBytesToStderr :: Handle -> IO ()
processBytesToStderr outputHdl = do
inputHdl <- openFile largeCharFile ReadMode
FH.putBytes outputHdl
$ lefts
$ Proc.processBytes'
trToStderr
["[a-z]", "[A-Z]"]
$ FH.toBytes inputHdl
hClose inputHdl
processChunks :: String -> Handle -> IO ()
processChunks trPath outputHdl = do
inputHdl <- openFile largeCharFile ReadMode
FH.putChunks outputHdl $
Proc.processChunks
trPath
["[a-z]", "[A-Z]"]
$ FH.toChunks inputHdl
hClose inputHdl
processChunks' :: String -> Handle -> IO ()
processChunks' trPath outputHdl = do
inputHdl <- openFile largeCharFile ReadMode
_ <- S.fold
(FL.partition
(FH.writeChunks outputHdl) (FH.writeChunks outputHdl)
)
$ Proc.processChunks'
trPath
["[a-z]", "[A-Z]"]
(FH.toChunks inputHdl)
hClose inputHdl
processChunksToStderr :: Handle -> IO ()
processChunksToStderr outputHdl = do
inputHdl <- openFile largeCharFile ReadMode
FH.putChunks outputHdl
$ lefts
$ Proc.processChunks'
trToStderr
["[a-z]", "[A-Z]"]
(FH.toChunks inputHdl)
hClose inputHdl
-------------------------------------------------------------------------------
-- Main
-------------------------------------------------------------------------------
main :: IO ()
main = do
putStrLn "Generating files..."
generateFiles
trPath <- which "tr"
catPath <- which "cat"
nullH <- openFile devNull WriteMode
putStrLn "Running benchmarks..."
defaultMain
[ bench "toBytes'" $ nfIO $ toBytes' catPath nullH
, bench "toChunks'" $ nfIO $ toChunks' catPath nullH
, bench "processBytes tr" $ nfIO $ processBytes trPath nullH
, bench "processBytes' tr" $ nfIO $ processBytes' trPath nullH
, bench "processBytesToStderr tr" $ nfIO $ processBytesToStderr nullH
, bench "processChunks tr" $ nfIO (processChunks trPath nullH)
, bench "processChunks' tr" $ nfIO (processChunks' trPath nullH)
, bench "processChunksToStderr" $ nfIO $ processChunksToStderr nullH
] `finally` (do
putStrLn "cleanup ..."
hClose nullH
deleteFiles
)