module Gauge.Internal
(
runAndAnalyse
, runAndAnalyseOne
, runFixedIters
) where
import Control.DeepSeq (rnf)
import Control.Exception (evaluate)
import Control.Monad (foldM, forM_, void, when)
import Foundation.Monad
import Foundation.Monad.Reader (ask)
import Data.Int (Int64)
import Gauge.Analysis (analyseSample, noteOutliers)
import Gauge.IO.Printf (note, printError, prolix, rewindClearLine)
import Gauge.Measurement (runBenchmark, runBenchmarkable_, secs)
import Gauge.Monad (Gauge)
import Gauge.Monad.ExceptT
import Gauge.Types hiding (measure)
import qualified Data.Map as Map
import qualified Data.Vector as V
import Statistics.Types (Estimate(..),ConfInt(..),confidenceInterval,cl95,confidenceLevel)
import System.IO (hSetBuffering, BufferMode(..), stdout)
import Text.Printf (printf)
runOne :: Int -> String -> Benchmarkable -> Gauge DataRecord
runOne i desc bm = do
Config{..} <- ask
(meas,timeTaken) <- liftIO $ runBenchmark bm timeLimit
when (timeTaken > timeLimit * 1.25) .
void $ prolix "measurement took %s\n" (secs timeTaken)
return (Measurement i desc meas)
analyseOne :: Int -> String -> V.Vector Measured -> Gauge DataRecord
analyseOne i desc meas = do
Config{..} <- ask
_ <- prolix "analysing with %d resamples\n" resamples
erp <- runExceptT $ analyseSample i desc meas
case erp of
Left err -> printError "*** Error: %s\n" err
Right rpt@Report{..} -> do
let SampleAnalysis{..} = reportAnalysis
OutlierVariance{..} = anOutlierVar
wibble = printOverallEffect ovEffect
(builtin, others) = splitAt 1 anRegress
case displayMode of
StatsTable -> do
_ <- note "%sbenchmarked %s\n" rewindClearLine desc
let r2 n = printf "%.3f R\178" n
forM_ builtin $ \Regression{..} ->
case Map.lookup "iters" regCoeffs of
Nothing -> return ()
Just t -> bs secs "time" t >> bs r2 "" regRSquare
bs secs "mean" anMean
bs secs "std dev" anStdDev
forM_ others $ \Regression{..} -> do
_ <- bs r2 (regResponder ++ ":") regRSquare
forM_ (Map.toList regCoeffs) $ \(prd,val) ->
bs (printf "%.3g") (" " ++ prd) val
--writeCsv
when (verbosity == Verbose || (ovEffect > Slight && verbosity > Quiet)) $ do
when (verbosity == Verbose) $ noteOutliers reportOutliers
_ <- note "variance introduced by outliers: %d%% (%s)\n"
(round (ovFraction * 100) :: Int) wibble
return ()
_ <- note "\n"
pure ()
Condensed -> do
_ <- note "%s%-40s " rewindClearLine desc
bsSmall secs "mean" anMean
bsSmall secs "( +-" anStdDev
_ <- note ")\n"
pure ()
return (Analysed rpt)
where bs :: (Double -> String) -> String -> Estimate ConfInt Double -> Gauge ()
bs f metric e@Estimate{..} =
note "%-20s %-10s (%s .. %s%s)\n" metric
(f estPoint) (f $ fst $ confidenceInterval e) (f $ snd $ confidenceInterval e)
(let cl = confIntCL estError
str | cl == cl95 = ""
| otherwise = printf ", ci %.3f" (confidenceLevel cl)
in str
)
bsSmall :: (Double -> String) -> String -> Estimate ConfInt Double -> Gauge ()
bsSmall f metric Estimate{..} =
note "%s %-10s" metric (f estPoint)
printOverallEffect :: OutlierEffect -> String
printOverallEffect Unaffected = "unaffected"
printOverallEffect Slight = "slightly inflated"
printOverallEffect Moderate = "moderately inflated"
printOverallEffect Severe = "severely inflated"
runAndAnalyseOne :: Int -> String -> Benchmarkable -> Gauge DataRecord
runAndAnalyseOne i desc bm = do
Measurement _ _ meas <- runOne i desc bm
analyseOne i desc meas
runAndAnalyse :: (String -> Bool)
-> Benchmark
-> Gauge ()
runAndAnalyse select bs = do
liftIO $ hSetBuffering stdout NoBuffering
for select bs $ \idx desc bm -> do
_ <- note "benchmarking %s" desc
Analysed _ <- runAndAnalyseOne idx desc bm
return ()
return ()
runFixedIters :: Int64
-> (String -> Bool)
-> Benchmark
-> Gauge ()
runFixedIters iters select bs =
for select bs $ \_idx desc bm -> do
_ <- note "benchmarking %s\r" desc
liftIO $ runBenchmarkable_ bm iters
for :: (String -> Bool)
-> Benchmark
-> (Int -> String -> Benchmarkable -> Gauge ())
-> Gauge ()
for select bs0 handle = go (0::Int) ("", bs0) >> return ()
where
go !idx (pfx, Environment mkenv cleanenv mkbench)
| shouldRun pfx mkbench = do
e <- liftIO $ do
ee <- mkenv
evaluate (rnf ee)
return ee
go idx (pfx, mkbench e) `finally` liftIO (cleanenv e)
| otherwise = return idx
go idx (pfx, Benchmark desc b)
| select desc' = do handle idx desc' b; return $! idx + 1
| otherwise = return idx
where desc' = addPrefix pfx desc
go idx (pfx, BenchGroup desc bs) =
foldM go idx [(addPrefix pfx desc, b) | b <- bs]
shouldRun pfx mkbench =
any (select . addPrefix pfx) . benchNames . mkbench $
error "Gauge.env could not determine the list of your benchmarks since they force the environment (see the documentation for details)"