hsbencher-fusion-0.3.2: CriterionUploader/Main.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE NamedFieldPuns #-}
-- |
-- Seeded from code by:
-- Copyright : [2014] Trevor L. McDonell
module Main where
-- Friends:
import HSBencher
import HSBencher.Internal.Config (augmentResultWithConfig, getConfig)
import HSBencher.Backend.Fusion
import Criterion.Types ( Report(..), SampleAnalysis(..), Regression(..) )
import Criterion.IO ( readReports )
import Statistics.Resampling.Bootstrap ( Estimate(..) )
-- Standard:
import Control.Monad.Reader
import Data.List as L
import System.Console.GetOpt (getOpt, getOpt', ArgOrder(Permute), OptDescr(Option), ArgDescr(..), usageInfo)
import System.Environment (getArgs)
import System.Exit
import qualified Data.Map as Map
----------------------------------------------------------------------------------------------------
data ExtraFlag = TableName String
| PrintHelp
deriving (Eq,Ord,Show,Read)
extra_cli_options :: [OptDescr ExtraFlag]
extra_cli_options = [ Option ['h'] ["help"] (NoArg PrintHelp)
"Show this help message and exit."
, Option [] ["name"] (ReqArg TableName "NAME")
"Name for the fusion table to which we upload (discovered or created)." ]
plug :: FusionPlug
plug = defaultFusionPlugin
main :: IO ()
main = do
cli_args <- getArgs
let (help,fusion_cli_options) = plugCmdOpts plug
let (opts1,plainargs,unrec,errs1) = getOpt' Permute extra_cli_options cli_args
let (opts2,_,errs2) = getOpt Permute fusion_cli_options unrec
let errs = errs1 ++ errs2
when (L.elem PrintHelp opts1 || not (null errs)) $ do
putStrLn $
"USAGE: fusion-upload-criterion [options] REPORTFILE\n\n"++
"Upload a pre-existing Criterion report benchmarked on the CURRENT machine.\n"++
"This restriction is due to the Report not containing system information. Rather,\n"++
"'fusion-upload-criterion' gathers information about the platform at the time of upload.\n"++
"\n"++
(usageInfo "Options:" extra_cli_options)++"\n"++
(usageInfo help fusion_cli_options)
if null errs then exitSuccess else exitFailure
let name = case [ n | TableName n <- opts1 ] of
[] -> error "Must supply a table name!"
[n] -> n
ls -> error $ "Multiple table names supplied!: "++show ls
-- This bit could be abstracted nicely by the HSBencher lib:
------------------------------------------------------------
-- Gather info about the benchmark platform:
gconf0 <- getConfig [] []
let gconf1 = gconf0 { benchsetName = Just name }
gconf2 <- plugInitialize plug gconf1
let fconf0 = getMyConf plug gconf2
let fconf1 = foldFlags plug opts2 fconf0
let gconf3 = setMyConf plug fconf1 gconf2
------------------------------------------------------------
case plainargs of
[] -> error "No file given to upload!"
reports -> forM_ reports (doupload gconf3)
doupload :: Config -> FilePath -> IO ()
doupload confs file = do
x <- readReports file
case x of
Left err -> error $ "Failed to read report file: \n"++err
Right reports -> forM_ reports (upreport confs)
upreport :: Config -> Report -> IO ()
upreport gconf report = do
let br = emptyBenchmarkResult
printReport report -- TEMP
br' <- augmentResultWithConfig gconf (addReport report br)
runReaderT (uploadBenchResult br') gconf
printReport :: Report -> IO ()
printReport Report{..} = do
putStrLn ("Found report with keys: "++show reportKeys)
let SampleAnalysis{..} = reportAnalysis
forM_ anRegress $ \Regression{..} -> do
putStrLn$ " Regression: "++ show (regResponder, Map.keys regCoeffs)
addReport :: Report -> BenchmarkResult -> BenchmarkResult
addReport rep@Report{..} BenchmarkResult{..} =
BenchmarkResult
{ _PROGNAME = reportName
, _VARIANT = if null _VARIANT
then "criterion" -- This is just helpful for filtering down the fusion table.
else _VARIANT
, _MEDIANTIME = medtime
, _MINTIME = estLowerBound $ fetch "time" "iters"
, _MAXTIME = estUpperBound $ fetch "time" "iters"
, _MEDIANTIME_PRODUCTIVITY =
do ms <- Map.lookup ("mutatorWallSeconds","iters") ests
gs <- Map.lookup ("gcWallSeconds","iters") ests
return (estPoint ms / (estPoint ms + estPoint gs))
, _MEDIANTIME_ALLOCRATE =
do e <- Map.lookup ("allocated","iters") ests
-- Use time to extrapolate the alloc rate / second:
return (round(estPoint e * (1.0 / medtime)))
, _CUSTOM =
(maybe [] (\ e -> [("BYTES_ALLOC",DoubleResult (estPoint e))])
(Map.lookup ("allocated","iters") ests)) ++
(maybe [] (\ e -> [("BYTES_COPIED",DoubleResult (estPoint e))])
(Map.lookup ("bytesCopied","iters") ests)) ++
(maybe [] (\ e -> [("NUMGC",DoubleResult (estPoint e))])
(Map.lookup ("numGcs","iters") ests)) ++
(maybe [] (\ e -> [("CPUTIME",DoubleResult (estPoint e))])
(Map.lookup ("cpuTime","iters") ests)) ++
(maybe [] (\ e -> [("CYCLES",DoubleResult (estPoint e))])
(Map.lookup ("cycles","iters") ests))
, ..
}
where
medtime = estPoint $ fetch "time" "iters"
SampleAnalysis{..} = reportAnalysis
ests = Map.fromList $
[ ((regResponder,p), e) | Regression{..} <- anRegress
, (p,e) <- Map.toList regCoeffs ]
fetch r p = case Map.lookup (r,p) ests of
Nothing -> error $ "Expected regression with responder/predictor:"++r++"/"++p
Just x -> x