{-| Module : Crypto.Lol.Utils.PrettyPrint Description : Pretty-printing for benchmark results. Copyright : (c) Eric Crockett, 2011-2017 Chris Peikert, 2011-2017 License : GPL-3 Maintainer : ecrockett0@email.com Stability : experimental Portability : POSIX Pretty-printing for benchmark results. -} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TupleSections #-} module Crypto.Lol.Utils.PrettyPrint ( getTableName , getBenchParams , getBenchLvl , getBenchFunc , getReports , getRuntime , col , testName , OptsInternal(..) , Verb(..) ) where import Control.Monad (foldM, when) import Control.Monad.IO.Class (liftIO) import Criterion.Internal (runAndAnalyseOne) import Criterion.Main.Options (defaultConfig) import Criterion.Measurement (initializeTime, secs) import Criterion.Monad (Criterion, withConfig) import Criterion.Types import qualified Data.Map as Map import Statistics.Types (Estimate (..)) -- | Verbosity of benchmark output. data Verb = Progress -- ^ prints a \'.\' when each benchmark completes | Abridged -- ^ prints a one-line summary for each benchmark | Full -- ^ prints full criterion output for each benchmark deriving (Eq) -- | Options for printing benchmark summary. data OptsInternal = OptsInternal {verb :: Verb, -- ^ Verbosity levels :: [String], -- ^ Which levels of Lol to benchmark benches :: [String], -- ^ Which operations to benchmark params :: [String], -- ^ Which parameters to benchmark redThreshold :: Double, -- ^ How many times larger a benchmark -- must be (compared to the minimum -- benchmark for that parameter, -- across all levels), to be printed in red colWidth :: Int, -- ^ Character width of data columns testNameWidth :: Int} -- ^ Character width of row labels col, testName :: OptsInternal -> String testName OptsInternal{..} = "%-" ++ show testNameWidth ++ "s " col OptsInternal{..} = "%-" ++ show colWidth ++ "s " -- get the ith 'word' where words are separated by '/' wordBy :: Int -> String -> String wordBy 0 = takeWhile (/= '/') wordBy i = wordBy (i-1) . tail . dropWhile (/= '/') getTableName :: String -> String getTableName = wordBy 0 getBenchParams :: String -> String getBenchParams = wordBy 1 getBenchLvl :: String -> String getBenchLvl = wordBy 2 getBenchFunc :: String -> String getBenchFunc = wordBy 3 getReports :: OptsInternal -> Benchmark -> IO [Report] getReports o = withConfig (config o) . summarizeBenchReports o config :: OptsInternal -> Config config OptsInternal{..} = defaultConfig {verbosity = if verb == Full then Normal else Quiet} -- collect reports from all selected benchmarks, printing a summary along the way summarizeBenchReports :: OptsInternal -> Benchmark -> Criterion [Report] summarizeBenchReports OptsInternal{..} b = do liftIO initializeTime -- Workaround for criterion issue #195 snd <$> go (0, []) ("", b) where select name = let param = getBenchParams name lvl = getBenchLvl name func = getBenchFunc name in (lvl `elem` levels) && (func `elem` benches) && (param `elem` params) -- if we find a Benchmark that we want to run (as determined by `select`) go r@(rptIdx, reports) (benchPrefix, Benchmark desc b') | select benchName = do -- get a single report when (verb == Abridged || verb == Full) $ liftIO $ putStr $ "benchmark " ++ benchName when (verb == Full) $ liftIO $ putStrLn "" dr <- runAndAnalyseOne rptIdx benchName b' case dr of Measurement{} -> error "PrettyPrint Measurement" -- for Wmissing-monadfail-instances (Analysed rpt) -> do when (verb == Progress) $ liftIO $ putStr "." when (verb == Abridged) $ liftIO $ putStrLn $ "..." ++ secs (getRuntime rpt) -- return the report return (rptIdx, rpt:reports) | otherwise = do -- if we don't want to run this benchmark, print the name anyway. liftIO $ putStrLn benchName -- and return the input return r where benchName = addPrefix benchPrefix desc go r (benchPrefix, BenchGroup desc bs) = let lvlName = addPrefix benchPrefix desc -- append the description to the prefix bs' = map (lvlName,) bs in foldM go r bs' -- | The report runtime, in seconds. getRuntime :: Report -> Double getRuntime Report{..} = let SampleAnalysis{..} = reportAnalysis Regression{..} = head anRegress Estimate{..} = regCoeffs Map.! "iters" in estPoint