packages feed

deltaq-1.0.0.0: benchmark/Main.hs

{-|
Copyright   : Predictable Network Solutions Ltd., 2003-2024
License     : BSD-3-Clause
Description : Main benchmark of 'DQ'.
-}
module Main (main) where

import Benchmark.Plot
    ( Measurement (..)
    )
import Data.Traversable
    ( for
    )
import DeltaQ.Class
    ( DeltaQ (..)
    , Outcome (..)
    )
import DeltaQ.PiecewisePolynomial
    ( DQ
    , complexity
    )

import qualified Statistics.Types as Stat
import qualified Criterion as C
import qualified Criterion.Main as C
import qualified Criterion.Types as C
import qualified Options.Applicative as O

{-----------------------------------------------------------------------------
    Main
------------------------------------------------------------------------------}
-- | Command line arguments for the basic benchmark.
data BenchmarkArgs = BenchmarkArgs
    { resultFile :: FilePath
    }

benchmarkOptions :: O.Parser BenchmarkArgs
benchmarkOptions =
    BenchmarkArgs
    <$> O.strOption
        (  O.long "output"
        <> O.short 'o'
        <> O.metavar "FILENAME"
        <> O.help "File for benchmark results"
        )

commandLineOptions :: O.ParserInfo BenchmarkArgs
commandLineOptions =
    O.info (benchmarkOptions O.<**> O.helper)
      ( O.fullDesc
     <> O.progDesc "Run a benchmark of the `deltaq` package"
     <> O.header "basic - a benchmark for `deltaq`" )

main :: IO ()
main = do
    args <- O.execParser commandLineOptions
    measurements <- concat <$> sequence
        [ measureOp ".\\/." (.\/.) 20
        , measureOp "./\\." (./\.) 20
        , measureOp ".>>." (.>>.) 9
        ]
    writeFile (resultFile args) $ show measurements

{-----------------------------------------------------------------------------
    Benchmark
    Plumbing
------------------------------------------------------------------------------}
measureOp :: String -> (DQ -> DQ -> DQ) -> Int -> IO [Measurement]
measureOp name op mm = do
    for [0 .. mm] $ \m -> do
        let mkExpression = replicateOp op
        putStrLn $ "Measurement: " <> name <> ", m = " <> show m
        report <- C.benchmarkWith' config $ C.nf mkExpression m
        pure $ Measurement
            { mName = name
            , mTime = getTime report
            , mExpressionSize = m
            , mValueComplexity = complexity (mkExpression m)
            }
  where
    getTime = 
        Stat.estPoint . C.anMean . C.reportAnalysis 
    config = C.defaultConfig { C.timeLimit = 10 / fromIntegral mm }

{-----------------------------------------------------------------------------
    Benchmark
    Expressions
------------------------------------------------------------------------------}
-- | Construct an expression that applies a given binary operation
-- \( m \) times.
replicateOp :: (DQ -> DQ -> DQ) -> Int -> DQ
replicateOp _  0 = uniform 0 1
replicateOp op m =
    uniform x 1 `op` replicateOp op (m-1)
  where
    x = 1 - 1 / fromIntegral m