tools: simplebench is now quickbench
This commit is contained in:
parent
bdf8f5ca88
commit
843c417079
21
Makefile
21
Makefile
@ -13,6 +13,7 @@
|
|||||||
#
|
#
|
||||||
# - stack, installs dependencies and drives cabal & ghc
|
# - stack, installs dependencies and drives cabal & ghc
|
||||||
# - shelltestrunner (latest version from hackage or possibly git), runs functional tests
|
# - shelltestrunner (latest version from hackage or possibly git), runs functional tests
|
||||||
|
# - quickbench (from git), runs benchmarks
|
||||||
# - hasktags, generates tag files for code navigation
|
# - hasktags, generates tag files for code navigation
|
||||||
# - profiteur, renders profiles as interactive html
|
# - profiteur, renders profiles as interactive html
|
||||||
# - hpack, generates cabal files from package.yaml files
|
# - hpack, generates cabal files from package.yaml files
|
||||||
@ -58,9 +59,6 @@ PROFRTSFLAGS=-P
|
|||||||
# COVCMD=test
|
# COVCMD=test
|
||||||
# COVCMD=-f test-wf.csv print
|
# COVCMD=-f test-wf.csv print
|
||||||
|
|
||||||
# executables to run during "make quickbench"
|
|
||||||
BENCHEXES=hledger-0.27 hledger-journalupdate hledger-parsedjournal hledger
|
|
||||||
|
|
||||||
# misc. system tools
|
# misc. system tools
|
||||||
BROWSE=open
|
BROWSE=open
|
||||||
# VIEWHTML=$(BROWSE)
|
# VIEWHTML=$(BROWSE)
|
||||||
@ -504,10 +502,6 @@ dev-heap-upload:
|
|||||||
# )
|
# )
|
||||||
# $(GHC) tools/doctest.hs
|
# $(GHC) tools/doctest.hs
|
||||||
|
|
||||||
tools/simplebench: tools/simplebench.hs \
|
|
||||||
$(call def-help,tools/simplebench, build the standalone generic benchmark runner. Requires libs installed by stack build --bench. )
|
|
||||||
$(STACK) exec -- $(GHC) tools/simplebench.hs
|
|
||||||
|
|
||||||
# tools/criterionbench: tools/criterionbench.hs \
|
# tools/criterionbench: tools/criterionbench.hs \
|
||||||
# $(call def-help,tools/criterionbench,\
|
# $(call def-help,tools/criterionbench,\
|
||||||
# build the criterion-based benchmark runner. Requires criterion.\
|
# build the criterion-based benchmark runner. Requires criterion.\
|
||||||
@ -678,13 +672,14 @@ cabalfiletest: \
|
|||||||
# && echo $@ PASSED) || echo $@ FAILED
|
# && echo $@ PASSED) || echo $@ FAILED
|
||||||
# # && cabal upload dist/$$p-$(VERSION).tar.gz --check -v3 \
|
# # && cabal upload dist/$$p-$(VERSION).tar.gz --check -v3 \
|
||||||
|
|
||||||
quickbench: samplejournals bench.tests tools/simplebench \
|
BENCHEXES=hledger-0.27,hledger
|
||||||
|
|
||||||
|
quickbench: samplejournals bench.sh \
|
||||||
$(call def-help,quickbench,\
|
$(call def-help,quickbench,\
|
||||||
run simple performance benchmarks without saving results\
|
run simple performance benchmarks without saving results\
|
||||||
Requires some commands defined in bench.tests and some BENCHEXES defined above.\
|
Requires some commands defined in bench.sh\
|
||||||
)
|
)
|
||||||
tools/simplebench -v -fbench.tests $(BENCHEXES)
|
quickbench -v -w $(BENCHEXES)
|
||||||
@rm -f benchresults.*
|
|
||||||
|
|
||||||
# bench: samplejournals tests/bench.tests tools/simplebench \
|
# bench: samplejournals tests/bench.tests tools/simplebench \
|
||||||
# $(call def-help,bench,\
|
# $(call def-help,bench,\
|
||||||
@ -1169,9 +1164,6 @@ $(call def-help-subsection,RELEASING:)
|
|||||||
# #
|
# #
|
||||||
# # - The .version file must be updated manually before a release.
|
# # - The .version file must be updated manually before a release.
|
||||||
# #
|
# #
|
||||||
# # - "make simplebench" depends on version numbers in BENCHEXES, these also
|
|
||||||
# # must be updated manually.
|
|
||||||
# #
|
|
||||||
# # - "make" updates the version in most other places, and defines PATCHES.
|
# # - "make" updates the version in most other places, and defines PATCHES.
|
||||||
# # Note "cabal build" should also do this but doesn't yet.
|
# # Note "cabal build" should also do this but doesn't yet.
|
||||||
# #
|
# #
|
||||||
@ -1303,7 +1295,6 @@ tagrelease: \
|
|||||||
# $(call def-help,showreleasestats stats,\
|
# $(call def-help,showreleasestats stats,\
|
||||||
# show project stats useful for release notes\
|
# show project stats useful for release notes\
|
||||||
# )
|
# )
|
||||||
# # simplebench
|
|
||||||
# # showerrors
|
# # showerrors
|
||||||
|
|
||||||
# FROMTAG=.
|
# FROMTAG=.
|
||||||
|
|||||||
@ -1,178 +0,0 @@
|
|||||||
-- a quick librarification of tools/simplebench.hs for cabal benchmarking
|
|
||||||
|
|
||||||
-- #!/usr/bin/env runhaskell
|
|
||||||
{-
|
|
||||||
bench.hs - simple benchmarking of command-line programs.
|
|
||||||
Requires html and tabular.
|
|
||||||
Simon Michael 2008-2015
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
$ simplebench.hs --help
|
|
||||||
...
|
|
||||||
$ cat - >bench.tests
|
|
||||||
-f sample.ledger -s balance
|
|
||||||
-f ~/.ledger -s balance
|
|
||||||
$ simplebench.hs -v hledger "ledger --no-cache" ledger
|
|
||||||
Using bench.tests
|
|
||||||
Running 2 tests 2 times in . with 3 executables at 2008-11-26 18:52:15.776357 UTC:
|
|
||||||
1: hledger -f sample.ledger -s balance [0.02s]
|
|
||||||
2: hledger -f sample.ledger -s balance [0.01s]
|
|
||||||
1: ledger --no-cache -f sample.ledger -s balance [0.02s]
|
|
||||||
2: ledger --no-cache -f sample.ledger -s balance [0.02s]
|
|
||||||
1: ledger -f sample.ledger -s balance [0.02s]
|
|
||||||
2: ledger -f sample.ledger -s balance [0.02s]
|
|
||||||
1: hledger -f ~/.ledger -s balance [3.56s]
|
|
||||||
2: hledger -f ~/.ledger -s balance [3.56s]
|
|
||||||
1: ledger --no-cache -f ~/.ledger -s balance [0.10s]
|
|
||||||
2: ledger --no-cache -f ~/.ledger -s balance [0.10s]
|
|
||||||
1: ledger -f ~/.ledger -s balance [0.10s]
|
|
||||||
2: ledger -f ~/.ledger -s balance [0.10s]
|
|
||||||
|
|
||||||
Summary (best iteration):
|
|
||||||
|
|
||||||
|| hledger | ledger --no-cache | ledger
|
|
||||||
============================++=========+===================+=======
|
|
||||||
-f sample.ledger -s balance || 0.01 | 0.02 | 0.02
|
|
||||||
-f ~/.ledger -s balance || 3.56 | 0.10 | 0.10
|
|
||||||
|
|
||||||
-}
|
|
||||||
|
|
||||||
module SimpleBench
|
|
||||||
where
|
|
||||||
|
|
||||||
import Data.List
|
|
||||||
import System.Environment
|
|
||||||
-- import System.FilePath
|
|
||||||
import System.Process
|
|
||||||
import System.IO
|
|
||||||
import Text.Tabular
|
|
||||||
import qualified Text.Tabular.AsciiArt as TA
|
|
||||||
-- import qualified Text.Tabular.Html as TH
|
|
||||||
-- import Text.Html ((+++), renderHtml, stringToHtml)
|
|
||||||
import System.Exit
|
|
||||||
import Text.Printf
|
|
||||||
import Data.Time.Clock
|
|
||||||
import Data.Time.Format ()
|
|
||||||
import Control.Monad
|
|
||||||
import System.Console.GetOpt
|
|
||||||
|
|
||||||
usagehdr = "bench [-f testsfile] [-n iterations] [-p precision] executable1 [executable2 ...]\n" ++
|
|
||||||
"\n" ++
|
|
||||||
"Run some functional tests with each of the specified executables,\n" ++
|
|
||||||
"where a test is \"zero or more arguments supported by all executables\",\n" ++
|
|
||||||
"and report the best execution times.\n"
|
|
||||||
|
|
||||||
options = [
|
|
||||||
Option "f" ["testsfile"] (ReqArg File "testsfile") "file containing tests, one per line, default: bench.tests"
|
|
||||||
,Option "n" ["iterations"] (ReqArg Num "iterations") "number of test iterations to run, default: 1"
|
|
||||||
,Option "p" ["precision"] (ReqArg Prec "precision") "show times with this precision, default: 2"
|
|
||||||
,Option "v" ["verbose"] (NoArg Verbose) "show intermediate results"
|
|
||||||
,Option "h" ["help"] (NoArg Help) "show this help"
|
|
||||||
]
|
|
||||||
|
|
||||||
usageftr = "\n" ++
|
|
||||||
"Tips:\n" ++
|
|
||||||
"- executables may have arguments if enclosed in quotes\n" ++
|
|
||||||
"- tests can be commented out with #\n" ++
|
|
||||||
"- results are saved in benchresults.{html,txt}\n"
|
|
||||||
|
|
||||||
usage = usageInfo usagehdr options ++ usageftr
|
|
||||||
|
|
||||||
-- an option value
|
|
||||||
data Opt = File {value::String}
|
|
||||||
| Num {value::String}
|
|
||||||
| Prec {value::String}
|
|
||||||
-- I don't know how optValuesForConstructor etc. can have that
|
|
||||||
-- type signature with these, but it works..
|
|
||||||
-- | Some Int
|
|
||||||
| Verbose
|
|
||||||
| Help
|
|
||||||
deriving (Eq,Show)
|
|
||||||
|
|
||||||
-- option value getters.
|
|
||||||
fileopt :: [Opt] -> String
|
|
||||||
fileopt = optValueWithDefault File "bench.tests"
|
|
||||||
|
|
||||||
precisionopt :: [Opt] -> Int
|
|
||||||
precisionopt = read . optValueWithDefault Prec "2"
|
|
||||||
|
|
||||||
numopt :: [Opt] -> Int
|
|
||||||
numopt = read . optValueWithDefault Num "1"
|
|
||||||
|
|
||||||
verboseopt :: [Opt] -> Bool
|
|
||||||
verboseopt = (Verbose `elem`)
|
|
||||||
|
|
||||||
-- options utilities
|
|
||||||
parseargs :: [String] -> ([Opt],[String])
|
|
||||||
parseargs as =
|
|
||||||
case (getOpt Permute options as) of
|
|
||||||
(opts,args,[]) -> (opts,args)
|
|
||||||
(_,_,errs) -> error (concat errs ++ usage)
|
|
||||||
|
|
||||||
optValueWithDefault :: (String -> Opt) -> String -> [Opt] -> String
|
|
||||||
optValueWithDefault optcons def opts =
|
|
||||||
last $ def : optValuesForConstructor optcons opts
|
|
||||||
|
|
||||||
optValuesForConstructor :: (String -> Opt) -> [Opt] -> [String]
|
|
||||||
optValuesForConstructor optcons opts = concatMap get opts
|
|
||||||
where get o = [v | optcons v == o] where v = value o
|
|
||||||
|
|
||||||
defaultMain = do
|
|
||||||
args <- getArgs
|
|
||||||
let (opts,exes) = parseargs args
|
|
||||||
when (null exes) $ error $ "at least one executable needed\n" ++ usage
|
|
||||||
let (file, num) = (fileopt opts, numopt opts)
|
|
||||||
tests <- liftM (filter istest . lines) (readFile file)
|
|
||||||
now <- getCurrentTime
|
|
||||||
putStrLn $ printf "Using %s" file
|
|
||||||
putStrLn $ printf "Running %d tests %d times with %d executables at %s:"
|
|
||||||
(length tests) num (length exes) (show now)
|
|
||||||
let doexe t e = mapM (doiteration opts t e) [1..num]
|
|
||||||
let dotest t = mapM (doexe t) exes
|
|
||||||
hSetBuffering stdout NoBuffering
|
|
||||||
results <- mapM dotest tests
|
|
||||||
summarise opts tests exes results
|
|
||||||
|
|
||||||
istest s = not (null s' || ("#" `isPrefixOf` s')) where s' = clean s
|
|
||||||
clean = unwords . words
|
|
||||||
|
|
||||||
doiteration :: [Opt] -> String -> String -> Int -> IO Float
|
|
||||||
doiteration opts test exe iteration = do
|
|
||||||
let cmd = unwords [exe,clean test]
|
|
||||||
when (verboseopt opts) $ putStr $ show iteration ++ ": " ++ cmd
|
|
||||||
hFlush stdout
|
|
||||||
t <- time cmd
|
|
||||||
when (verboseopt opts) $ printf "\t[%ss]\n" (showtime opts t)
|
|
||||||
return t
|
|
||||||
|
|
||||||
time :: String -> IO Float
|
|
||||||
time cmd = do
|
|
||||||
t1 <- getCurrentTime
|
|
||||||
ret <- system $ cmd ++ " >/dev/null 2>&1"
|
|
||||||
case ret of
|
|
||||||
ExitSuccess -> return ()
|
|
||||||
ExitFailure f -> putStr $ printf " (error %d)" f
|
|
||||||
t2 <- getCurrentTime
|
|
||||||
return $ realToFrac $ diffUTCTime t2 t1
|
|
||||||
|
|
||||||
summarise :: [Opt] -> [String] -> [String] -> [[[Float]]] -> IO ()
|
|
||||||
summarise opts tests exes results = do
|
|
||||||
putStrLn "\nSummary (best iteration):\n"
|
|
||||||
let t = maketable opts tests exes results
|
|
||||||
putStrLn $ TA.render id id id t
|
|
||||||
-- let outname = "benchresults"
|
|
||||||
-- writeFile (outname <.> "txt") $ TA.render id id id t
|
|
||||||
-- writeFile (outname <.> "html") $ renderHtml $ TH.css TH.defaultCss +++ TH.render stringToHtml stringToHtml stringToHtml t
|
|
||||||
|
|
||||||
maketable :: [Opt] -> [String] -> [String] -> [[[Float]]] -> Table String String String
|
|
||||||
maketable opts rownames colnames results = Table rowhdrs colhdrs rows
|
|
||||||
where
|
|
||||||
rowhdrs = Group NoLine $ map Header $ padright rownames
|
|
||||||
colhdrs = Group SingleLine $ map Header colnames
|
|
||||||
rows = map (map (showtime opts . minimum)) results
|
|
||||||
padright ss = map (printf (printf "%%-%ds" w)) ss
|
|
||||||
where w = maximum $ map length ss
|
|
||||||
|
|
||||||
showtime :: [Opt] -> (Float -> String)
|
|
||||||
showtime opts = printf $ "%." ++ show (precisionopt opts) ++ "f"
|
|
||||||
@ -1,10 +1,10 @@
|
|||||||
-- bench
|
-- bench
|
||||||
-- By default, show approximate times for some standard hledger operations on a sample journal.
|
-- By default, show approximate times for some standard hledger operations on a sample journal.
|
||||||
-- With --criterion, show accurate times (slow).
|
-- With --criterion, show accurate times (slow).
|
||||||
-- With --simplebench, show approximate times for the commands in default.bench, using the first hledger executable on $PATH.
|
-- TODO With --quickbench, show approximate times for the commands in default.bench, using the first hledger executable on $PATH.
|
||||||
|
|
||||||
import Criterion.Main (defaultMainWith, defaultConfig, bench, nfIO)
|
import Criterion.Main (defaultMainWith, defaultConfig, bench, nfIO)
|
||||||
import SimpleBench (defaultMain)
|
-- import QuickBench (defaultMain)
|
||||||
import System.Directory (getCurrentDirectory)
|
import System.Directory (getCurrentDirectory)
|
||||||
import System.Environment (getArgs, withArgs)
|
import System.Environment (getArgs, withArgs)
|
||||||
import System.Info (os)
|
import System.Info (os)
|
||||||
@ -20,13 +20,13 @@ outputfile = "/dev/null" -- hide output of benchmarked commands (XXX unixism)
|
|||||||
-- outputfile = "-" -- show output of benchmarked commands
|
-- outputfile = "-" -- show output of benchmarked commands
|
||||||
|
|
||||||
main = do
|
main = do
|
||||||
-- withArgs ["--simplebench"] $ do
|
-- withArgs ["--quickbench"] $ do
|
||||||
-- withArgs ["--criterion"] $ do
|
-- withArgs ["--criterion"] $ do
|
||||||
args <- getArgs
|
args <- getArgs
|
||||||
if "--criterion" `elem` args
|
if "--criterion" `elem` args
|
||||||
then withArgs [] benchWithCriterion
|
then withArgs [] benchWithCriterion
|
||||||
else if "--simplebench" `elem` args
|
else if "--quickbench" `elem` args
|
||||||
then benchWithSimplebench
|
then benchWithQuickbench
|
||||||
else benchWithTimeit
|
else benchWithTimeit
|
||||||
|
|
||||||
benchWithTimeit = do
|
benchWithTimeit = do
|
||||||
@ -57,12 +57,12 @@ benchWithCriterion = do
|
|||||||
bench ("stats") $ nfIO $ stats opts j
|
bench ("stats") $ nfIO $ stats opts j
|
||||||
]
|
]
|
||||||
|
|
||||||
benchWithSimplebench = do
|
benchWithQuickbench = do
|
||||||
let whichcmd = if os == "mingw32" then "where" else "which"
|
let whichcmd = if os == "mingw32" then "where" else "which"
|
||||||
exe <- init <$> readProcess whichcmd ["hledger"] ""
|
exe <- init <$> readProcess whichcmd ["hledger"] ""
|
||||||
pwd <- getCurrentDirectory
|
pwd <- getCurrentDirectory
|
||||||
printf "Benchmarking %s in %s with simplebench and shell\n" exe pwd
|
printf "Benchmarking %s in %s with quickbench and shell\n" exe pwd
|
||||||
flip withArgs SimpleBench.defaultMain [
|
flip withArgs QuickBench.defaultMain [
|
||||||
"-fbench/default.bench"
|
"-fbench/default.bench"
|
||||||
,"-v"
|
,"-v"
|
||||||
,"hledger"
|
,"hledger"
|
||||||
|
|||||||
@ -283,8 +283,6 @@ benchmark bench
|
|||||||
hs-source-dirs:
|
hs-source-dirs:
|
||||||
bench
|
bench
|
||||||
main-is: bench.hs
|
main-is: bench.hs
|
||||||
other-modules:
|
|
||||||
SimpleBench
|
|
||||||
ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures -fno-warn-type-defaults -fno-warn-orphans
|
ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures -fno-warn-type-defaults -fno-warn-orphans
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
build-depends:
|
build-depends:
|
||||||
|
|||||||
@ -1,173 +0,0 @@
|
|||||||
#!/usr/bin/env runhaskell
|
|
||||||
{-
|
|
||||||
bench.hs - simple benchmarking of command-line programs.
|
|
||||||
Requires html and tabular.
|
|
||||||
Simon Michael 2008-2013
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
$ simplebench.hs --help
|
|
||||||
...
|
|
||||||
$ cat - >bench.tests
|
|
||||||
-f sample.ledger -s balance
|
|
||||||
-f ~/.ledger -s balance
|
|
||||||
$ simplebench.hs -v hledger "ledger --no-cache" ledger
|
|
||||||
Using bench.tests
|
|
||||||
Running 2 tests 2 times in . with 3 executables at 2008-11-26 18:52:15.776357 UTC:
|
|
||||||
1: hledger -f sample.ledger -s balance [0.02s]
|
|
||||||
2: hledger -f sample.ledger -s balance [0.01s]
|
|
||||||
1: ledger --no-cache -f sample.ledger -s balance [0.02s]
|
|
||||||
2: ledger --no-cache -f sample.ledger -s balance [0.02s]
|
|
||||||
1: ledger -f sample.ledger -s balance [0.02s]
|
|
||||||
2: ledger -f sample.ledger -s balance [0.02s]
|
|
||||||
1: hledger -f ~/.ledger -s balance [3.56s]
|
|
||||||
2: hledger -f ~/.ledger -s balance [3.56s]
|
|
||||||
1: ledger --no-cache -f ~/.ledger -s balance [0.10s]
|
|
||||||
2: ledger --no-cache -f ~/.ledger -s balance [0.10s]
|
|
||||||
1: ledger -f ~/.ledger -s balance [0.10s]
|
|
||||||
2: ledger -f ~/.ledger -s balance [0.10s]
|
|
||||||
|
|
||||||
Summary (best iteration):
|
|
||||||
|
|
||||||
|| hledger | ledger --no-cache | ledger
|
|
||||||
============================++=========+===================+=======
|
|
||||||
-f sample.ledger -s balance || 0.01 | 0.02 | 0.02
|
|
||||||
-f ~/.ledger -s balance || 3.56 | 0.10 | 0.10
|
|
||||||
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Data.List
|
|
||||||
import System.Environment
|
|
||||||
import System.FilePath
|
|
||||||
import System.Process
|
|
||||||
import System.IO
|
|
||||||
import Text.Tabular
|
|
||||||
import qualified Text.Tabular.AsciiArt as TA
|
|
||||||
import qualified Text.Tabular.Html as TH
|
|
||||||
import Text.Html ((+++), renderHtml, stringToHtml)
|
|
||||||
import System.Exit
|
|
||||||
import Text.Printf
|
|
||||||
import Data.Time.Clock
|
|
||||||
import Data.Time.Format ()
|
|
||||||
import Control.Monad
|
|
||||||
import System.Console.GetOpt
|
|
||||||
|
|
||||||
usagehdr = "bench [-f testsfile] [-n iterations] [-p precision] executable1 [executable2 ...]\n" ++
|
|
||||||
"\n" ++
|
|
||||||
"Run some functional tests with each of the specified executables,\n" ++
|
|
||||||
"where a test is \"zero or more arguments supported by all executables\",\n" ++
|
|
||||||
"and report the best execution times.\n"
|
|
||||||
|
|
||||||
options = [
|
|
||||||
Option "f" ["testsfile"] (ReqArg File "testsfile") "file containing tests, one per line, default: bench.tests"
|
|
||||||
,Option "n" ["iterations"] (ReqArg Num "iterations") "number of test iterations to run, default: 2"
|
|
||||||
,Option "p" ["precision"] (ReqArg Prec "precision") "show times with this precision, default: 2"
|
|
||||||
,Option "v" ["verbose"] (NoArg Verbose) "show intermediate results"
|
|
||||||
,Option "h" ["help"] (NoArg Help) "show this help"
|
|
||||||
]
|
|
||||||
|
|
||||||
usageftr = "\n" ++
|
|
||||||
"Tips:\n" ++
|
|
||||||
"- executables may have arguments if enclosed in quotes\n" ++
|
|
||||||
"- tests can be commented out with #\n" ++
|
|
||||||
"- results are saved in benchresults.{html,txt}\n"
|
|
||||||
|
|
||||||
usage = usageInfo usagehdr options ++ usageftr
|
|
||||||
|
|
||||||
-- an option value
|
|
||||||
data Opt = File {value::String}
|
|
||||||
| Num {value::String}
|
|
||||||
| Prec {value::String}
|
|
||||||
-- I don't know how optValuesForConstructor etc. can have that
|
|
||||||
-- type signature with these, but it works..
|
|
||||||
-- | Some Int
|
|
||||||
| Verbose
|
|
||||||
| Help
|
|
||||||
deriving (Eq,Show)
|
|
||||||
|
|
||||||
-- option value getters.
|
|
||||||
fileopt :: [Opt] -> String
|
|
||||||
fileopt = optValueWithDefault File "bench.tests"
|
|
||||||
|
|
||||||
precisionopt :: [Opt] -> Int
|
|
||||||
precisionopt = read . optValueWithDefault Prec "2"
|
|
||||||
|
|
||||||
numopt :: [Opt] -> Int
|
|
||||||
numopt = read . optValueWithDefault Num "2"
|
|
||||||
|
|
||||||
verboseopt :: [Opt] -> Bool
|
|
||||||
verboseopt = (Verbose `elem`)
|
|
||||||
|
|
||||||
-- options utilities
|
|
||||||
parseargs :: [String] -> ([Opt],[String])
|
|
||||||
parseargs as =
|
|
||||||
case (getOpt Permute options as) of
|
|
||||||
(opts,args,[]) -> (opts,args)
|
|
||||||
(_,_,errs) -> error (concat errs ++ usage)
|
|
||||||
|
|
||||||
optValueWithDefault :: (String -> Opt) -> String -> [Opt] -> String
|
|
||||||
optValueWithDefault optcons def opts =
|
|
||||||
last $ def : optValuesForConstructor optcons opts
|
|
||||||
|
|
||||||
optValuesForConstructor :: (String -> Opt) -> [Opt] -> [String]
|
|
||||||
optValuesForConstructor optcons opts = concatMap get opts
|
|
||||||
where get o = [v | optcons v == o] where v = value o
|
|
||||||
|
|
||||||
main = do
|
|
||||||
args <- getArgs
|
|
||||||
let (opts,exes) = parseargs args
|
|
||||||
when (null exes) $ error $ "at least one executable needed\n" ++ usage
|
|
||||||
let (file, num) = (fileopt opts, numopt opts)
|
|
||||||
tests <- liftM (filter istest . lines) (readFile file)
|
|
||||||
now <- getCurrentTime
|
|
||||||
putStrLn $ printf "Using %s" file
|
|
||||||
putStrLn $ printf "Running %d tests %d times with %d executables at %s:"
|
|
||||||
(length tests) num (length exes) (show now)
|
|
||||||
let doexe t e = mapM (doiteration opts t e) [1..num]
|
|
||||||
let dotest t = mapM (doexe t) exes
|
|
||||||
hSetBuffering stdout NoBuffering
|
|
||||||
results <- mapM dotest tests
|
|
||||||
summarise opts tests exes results
|
|
||||||
|
|
||||||
istest s = not (null s' || ("#" `isPrefixOf` s')) where s' = clean s
|
|
||||||
clean = unwords . words
|
|
||||||
|
|
||||||
doiteration :: [Opt] -> String -> String -> Int -> IO Float
|
|
||||||
doiteration opts test exe iteration = do
|
|
||||||
let cmd = unwords [exe,clean test]
|
|
||||||
when (verboseopt opts) $ putStr $ show iteration ++ ": " ++ cmd
|
|
||||||
hFlush stdout
|
|
||||||
t <- time cmd
|
|
||||||
when (verboseopt opts) $ printf "\t[%ss]\n" (showtime opts t)
|
|
||||||
return t
|
|
||||||
|
|
||||||
time :: String -> IO Float
|
|
||||||
time cmd = do
|
|
||||||
t1 <- getCurrentTime
|
|
||||||
ret <- system $ cmd ++ " >/dev/null 2>&1"
|
|
||||||
case ret of
|
|
||||||
ExitSuccess -> return ()
|
|
||||||
ExitFailure f -> putStr $ printf " (error %d)" f
|
|
||||||
t2 <- getCurrentTime
|
|
||||||
return $ realToFrac $ diffUTCTime t2 t1
|
|
||||||
|
|
||||||
summarise :: [Opt] -> [String] -> [String] -> [[[Float]]] -> IO ()
|
|
||||||
summarise opts tests exes results = do
|
|
||||||
putStrLn "\nSummary (best iteration):\n"
|
|
||||||
let t = maketable opts tests exes results
|
|
||||||
putStrLn $ TA.render id id id t
|
|
||||||
let outname = "benchresults"
|
|
||||||
writeFile (outname <.> "txt") $ TA.render id id id t
|
|
||||||
writeFile (outname <.> "html") $ renderHtml $ TH.css TH.defaultCss +++ TH.render stringToHtml stringToHtml stringToHtml t
|
|
||||||
|
|
||||||
maketable :: [Opt] -> [String] -> [String] -> [[[Float]]] -> Table String String String
|
|
||||||
maketable opts rownames colnames results = Table rowhdrs colhdrs rows
|
|
||||||
where
|
|
||||||
rowhdrs = Group NoLine $ map Header $ padright rownames
|
|
||||||
colhdrs = Group SingleLine $ map Header colnames
|
|
||||||
rows = map (map (showtime opts . minimum)) results
|
|
||||||
padright ss = map (printf (printf "%%-%ds" w)) ss
|
|
||||||
where w = maximum $ map length ss
|
|
||||||
|
|
||||||
showtime :: [Opt] -> (Float -> String)
|
|
||||||
showtime opts = printf $ "%." ++ show (precisionopt opts) ++ "f"
|
|
||||||
Loading…
Reference in New Issue
Block a user