fibon-0.2.0: tools/fibon-analyse/Fibon/Analyse/Analysis.hs
module Fibon.Analyse.Analysis (
Analysis(..)
, runAnalysis
, computeRows
, Normalize(..)
, NormMethod
)
where
import Data.List
import Data.Maybe
import qualified Data.Map as M
import Control.Monad.Error
import Fibon.Result
import Fibon.Analyse.AnalysisRoutines
import Fibon.Analyse.Parse
import Fibon.Analyse.Result
import Fibon.Analyse.Metrics
import Fibon.Analyse.Statistics
import Fibon.Analyse.Tables
import qualified Data.Vector.Unboxed as V
runAnalysis :: Analysis a -> FilePath -> IO (Maybe [ResultColumn a])
runAnalysis analysis file = do
fibonResults <- parse file
case fibonResults of
Nothing -> return Nothing
Just rs -> do x <- createResultColumns analysis rs
return (Just x)
where
parse f | ".SHOW" `isSuffixOf` f = parseShowFibonResults f
| otherwise = parseBinaryFibonResults f
createResultColumns :: Analysis a
-> M.Map ResultLabel [FibonResult]
-> IO [ResultColumn a]
createResultColumns analysis fibonResults =
mapM (analyseResults analysis) (M.toList fibonResults)
analyseResults :: Analysis a
-> (ResultLabel, [FibonResult])
-> IO (ResultColumn a)
analyseResults analysis (resultName, fibonResults) = do
ars <- mapM (analyseResult analysis) fibonResults
return $ ResultColumn resultName (resMap ars)
where
resMap ars = foldr create M.empty (zip ars fibonResults)
create (ar,fr) m = M.insert (benchNameOnly fr) ar m
benchNameOnly fr = takeWhile (/= '-') (benchName fr)
analyseResult ::
Analysis a
-> FibonResult
-> IO (AnalyseResult a) -- ^ final result
analyseResult analysis fibonR = do
fibonS <- (fibonAnalysis analysis) fibonR
extraS <- case mbExtras of
Nothing -> return Nothing
Just [] -> return Nothing
Just es -> return . Just =<< (extraAnalysis analysis) es
return (AnalyseResult fibonS extraS)
where
mbExtras = sequence $ map (extraP.runStats) (details.runData $ fibonR)
extraP = extraParser analysis
-- | Functions for normalizing and computing summaries of results
--
--
type RowData = (RowName, [PerfData])
type RowName = String
type TableError = String
type PerfMonad = Either TableError
type NormMethod a = ResultColumn a -> Normalize a
data Normalize a =
NormPercent (ResultColumn a)
| NormRatio (ResultColumn a)
| NormNone (ResultColumn a) -- ^ For uniform normalization use
computeRows :: [(Normalize a, ResultColumn a)]
-> [BenchName]
-> TableSpec a
-> Either TableError ([RowData], [RowData])
computeRows resultColumns benchs colSpecs = do
rows <- mapM (computeOneRow resultColumns colSpecs) benchs
let colData = transpose $ map snd rows
doSumm how = mapM (summarize how) colData
minRow <- doSumm Min
meanRow <- doSumm GeoMean
arithRow <- doSumm ArithMean
maxRow <- doSumm Max
let sumRows = [
("min", minRow)
, ("geomean", meanRow)
, ("arithmean", arithRow)
, ("max", maxRow)
]
return (rows, sumRows)
computeOneRow :: [(Normalize a, ResultColumn a)]
-> [ColSpec a]
-> BenchName
-> PerfMonad RowData
computeOneRow resultColumns colSpecs bench = do
row <- mapM (\spec ->
mapM (computeOneColumn bench spec) resultColumns
) colSpecs
return (bench, concat row)
computeOneColumn :: BenchName
-> ColSpec a
-> (Normalize a, ResultColumn a)
-> PerfMonad PerfData
computeOneColumn bench (ColSpec _ metric) (normType, resultColumn) =
maybe (return NoResult) doNormalize (getRawPerf resultColumn)
where
doNormalize peak =
case normType of
NormPercent base -> normToBase base normalizePercent
NormRatio base -> normToBase base normalizeRatio
NormNone _ -> return (mkRaw peak)
where
mkRaw = Basic . Raw
mkNorm = Basic . Norm
normToBase base normFun = maybe (return NoResult)
(\b -> mkNorm `liftM` normFun b peak)
(getRawPerf base)
getRawPerf rc = perf $ fmap metric ((M.lookup bench . results) rc)
type NormFun a =(a -> Double) -> a -> a -> NormPerf
normalizePercent :: RawPerf -> RawPerf -> PerfMonad NormPerf
normalizePercent = normalize normP
normalizeRatio :: RawPerf -> RawPerf -> PerfMonad NormPerf
normalizeRatio = normalize normR
normalize :: NormFun RawPerf
-> RawPerf
-> RawPerf
-> PerfMonad NormPerf
normalize n base@(RawTime _) peak@(RawTime _) =
return(n rawPerfToDouble base peak)
normalize n base@(RawSize _) peak@(RawSize _) =
return(n rawPerfToDouble base peak)
normalize _ _ _ = throwError "Can not normalize a size by time"
normP :: NormFun a
normP = norm Percent (\base peak -> (peak / base) * 100)
normR :: NormFun a
normR = norm Ratio (\base peak -> (base / peak))
-- TODO: use the intervals to compute the resulting interval
norm :: (Estimate Double -> NormPerf) -- ^ NormPerf constructor
-> (Double -> Double -> Double) -- ^ Normalizing function
-> (a -> Double) -- ^ Conversion to double
-> a -> a -- ^ Values to normalize
-> NormPerf
norm c f toDouble base peak =
c (mkPointEstimate mkStddev (f (toDouble base) (toDouble peak)))
where mkStddev = fromIntegral :: Int -> Double
summarize :: Summary -> [PerfData] -> PerfMonad PerfData
summarize how perfData =
case (normData, rawData) of
([], []) -> return NoResult
(nd, []) -> summarizeNorm how nd
([], rd) -> summarizeRaw how rd
_ -> throwError "Mixed raw and norm results in column"
where
normData = getData (\p -> case p of Basic (Norm n) -> Just n ; _ -> Nothing)
rawData = getData (\p -> case p of Basic (Raw r) -> Just r ; _ -> Nothing)
getData f = (catMaybes . map f) perfData
summarizeRaw :: Summary -> [RawPerf] -> PerfMonad PerfData
summarizeRaw how rawPerfs =
case (isTime rawPerfs, isSize rawPerfs) of
(True, _) -> summarizeRaw' how ExecTime RawTime rawPerfs
(_, True) -> summarizeRaw' how (MemSize . round) RawSize rawPerfs
_ -> throwError "Can only summarize column with time or size"
where
isTime = all (\r -> case r of RawTime _ -> True; RawSize _ -> False)
isSize = all (\r -> case r of RawSize _ -> True; RawTime _ -> False)
summarizeRaw' :: Summary -- ^ what kind of summary
-> (Double -> a) -- ^ rounding function
-> (Estimate a -> RawPerf) -- ^ RawPerf constructor
-> [RawPerf] -- ^ Performance numbers to summary
-> PerfMonad PerfData
summarizeRaw' how roundFun makeRaw rawPerfs =
return $ Summary how (Raw (makeRaw (fmap roundFun (computeSummary how vec))))
where
vec = V.fromList (map rawPerfToDouble rawPerfs)
summarizeNorm :: Summary -> [NormPerf] -> PerfMonad PerfData
summarizeNorm how normPerfs =
case (isPercent normPerfs, isRatio normPerfs) of
(True, _) -> summarizeNorm' how Percent normPerfs
(_, True) -> summarizeNorm' how Ratio normPerfs
_ -> throwError "Can only summarize column with percent or ratio"
where
isPercent = all (\r -> case r of Percent _ -> True; Ratio _ -> False)
isRatio = all (\r -> case r of Percent _ -> False; Ratio _ -> True)
summarizeNorm' :: Summary
-> (Estimate Double -> NormPerf)
-> [NormPerf]
-> PerfMonad PerfData
summarizeNorm' how makeNorm normPerfs =
return $ Summary how (Norm (makeNorm (computeSummary how vec)))
where
vec = V.fromList (map normPerfToDouble normPerfs)