streamly/benchmark/Chart.hs
2020-07-09 17:40:27 +05:30

195 lines
5.8 KiB
Haskell

{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Main where
import Control.Exception (catch, ErrorCall(..))
import Control.Monad.Trans.State
import Control.Monad.Trans.Maybe
import Data.Char (toLower)
import Data.List
import System.Environment (getArgs)
import Control.Monad.IO.Class (liftIO)
import Control.Monad (mzero)
import BenchShow
------------------------------------------------------------------------------
-- Command line parsing
------------------------------------------------------------------------------
data BenchType
= Compare String
| Standard String
deriving Show
data Options = Options
{ genGraphs :: Bool
, benchType :: Maybe BenchType
, fields :: [String]
} deriving Show
defaultOptions :: Options
defaultOptions = Options False Nothing ["time"]
setGenGraphs :: Monad m => Bool -> StateT (a, Options) m ()
setGenGraphs val = do
(args, opts) <- get
put (args, opts { genGraphs = val })
setBenchType :: Monad m => BenchType -> StateT (a, Options) m ()
setBenchType val = do
(args, opts) <- get
put (args, opts { benchType = Just val })
setFields :: Monad m => [String] -> StateT (a, Options) m ()
setFields val = do
(args, opts) <- get
put (args, opts { fields = val })
-- Like the shell "shift" to shift the command line arguments
shift :: StateT ([String], Options) (MaybeT IO) (Maybe String)
shift = do
s <- get
case s of
([], _) -> return Nothing
(x : xs, opts) -> put (xs, opts) >> return (Just x)
parseBench :: StateT ([String], Options) (MaybeT IO) ()
parseBench = do
x <- shift
case x of
Just str | "_cmp" `isSuffixOf` str -> setBenchType (Compare str)
Just str -> setBenchType (Standard str)
Nothing -> do
liftIO $ putStrLn "please provide a benchmark type "
mzero
parseFields :: StateT ([String], Options) (MaybeT IO) ()
parseFields = do
x <- shift
case x of
Just str -> setFields (words str)
Nothing -> do
liftIO $ putStrLn
"please provide a list of fields after --fields"
mzero
-- totally imperative style option parsing
parseOptions :: IO (Maybe Options)
parseOptions = do
args <- getArgs
runMaybeT $ flip evalStateT (args, defaultOptions) $ do
parseLoop
fmap snd get
where
parseOpt opt =
case opt of
"--graphs" -> setGenGraphs True
"--benchmark" -> parseBench
"--fields" -> parseFields
str -> do
liftIO $ putStrLn $ "Unrecognized option " <> str
mzero
parseLoop = do
next <- shift
case next of
Just opt -> parseOpt opt >> parseLoop
Nothing -> return ()
ignoringErr :: IO () -> IO ()
ignoringErr a = catch a (\(ErrorCall err :: ErrorCall) ->
putStrLn $ "Failed with error:\n" <> err <> "\nSkipping.")
------------------------------------------------------------------------------
-- Generic
------------------------------------------------------------------------------
makeGraphs :: String -> Config -> String -> IO ()
makeGraphs name cfg@Config{..} inputFile =
ignoringErr $ graph inputFile name cfg
------------------------------------------------------------------------------
-- Arrays
------------------------------------------------------------------------------
showComparisons :: Options -> Config -> FilePath -> FilePath -> IO ()
showComparisons Options{..} cfg inp out =
let cfg1 = cfg { classifyBenchmark = classifyComparison }
in if genGraphs
then ignoringErr $ graph inp "comparison"
cfg1 { outputDir = Just out
, presentation = Groups Absolute
}
else ignoringErr $ report inp Nothing cfg1
where
dropComponent = dropWhile (== '/') . dropWhile (/= '/')
classifyComparison b = Just
( takeWhile (/= '/') b
, dropComponent b
)
------------------------------------------------------------------------------
-- text reports
------------------------------------------------------------------------------
selectBench
:: (SortColumn -> Maybe GroupStyle -> Either String [(String, Double)])
-> [String]
selectBench f =
reverse
$ fmap fst
$ either
(const $ either error (sortOn snd) $ f (ColumnIndex 0) (Just PercentDiff))
(sortOn snd)
$ f (ColumnIndex 1) (Just PercentDiff)
benchShow ::
Options
-> Config
-> (Config -> String -> IO ())
-> String
-> FilePath
-> IO ()
benchShow Options{..} cfg func inp out =
if genGraphs
then func cfg {outputDir = Just out} inp
else ignoringErr $ report inp Nothing cfg
main :: IO ()
main = do
res <- parseOptions
case res of
Nothing -> do
putStrLn "cannot parse options"
return ()
Just opts@Options{fields = fs, benchType = btype} ->
let cfg = defaultConfig
{ presentation = Groups PercentDiff
, selectBenchmarks = selectBench
, selectFields = filter
( flip elem fs
. fmap toLower
)
}
in case btype of
Just (Compare str) ->
showComparisons opts cfg
{ title = Just str }
("charts/" ++ str ++ "/results.csv")
("charts/" ++ str)
Just (Standard str) ->
benchShow opts cfg
{ title = Just str }
(makeGraphs str)
("charts/" ++ str ++ "/results.csv")
("charts/" ++ str)
Nothing ->
error "Please specify a benchmark using --benchmark."