Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kwxm/uplc-criterion #5755

Merged
merged 9 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 7 additions & 13 deletions plutus-core/executables/plc/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@ import PlutusCore.MkPlc (mkConstant)
import PlutusCore.Pretty qualified as PP
import PlutusPrelude

import Control.DeepSeq (rnf)
import Data.ByteString.Lazy qualified as BSL (readFile)
import Data.Text.IO qualified as T
import Flat (unflat)
import Options.Applicative
import System.Exit (exitSuccess)
import System.Exit (exitFailure, exitSuccess)

plcHelpText :: String
plcHelpText = helpText "Typed Plutus Core"
Expand All @@ -35,7 +34,6 @@ data EvalOptions =
Input
Format
PrintMode
TimingMode
(BuiltinSemanticsVariant PLC.DefaultFun)
data EraseOptions = EraseOptions Input Format Output Format PrintMode

Expand Down Expand Up @@ -63,7 +61,7 @@ eraseOpts = EraseOptions <$> input <*> inputformat <*> output <*> outputformat <

evalOpts :: Parser EvalOptions
evalOpts =
EvalOptions <$> input <*> inputformat <*> printmode <*> timingmode <*> builtinSemanticsVariant
EvalOptions <$> input <*> inputformat <*> printmode <*> builtinSemanticsVariant

plutus ::
-- | The @helpText@
Expand Down Expand Up @@ -186,21 +184,17 @@ runOptimisations (OptimiseOptions inp ifmt outp ofmt mode) = do
---------------- Evaluation ----------------

runEval :: EvalOptions -> IO ()
runEval (EvalOptions inp ifmt printMode timingMode semvar) = do
runEval (EvalOptions inp ifmt printMode semvar) = do
prog <- readProgram ifmt inp
let evaluate = Ck.evaluateCkNoEmit (PLC.defaultBuiltinsRuntimeForSemanticsVariant semvar)
term = void $ prog ^. PLC.progTerm
!_ = rnf term
-- Force evaluation of body to ensure that we're not timing parsing/deserialisation.
-- The parser apparently returns a fully-evaluated AST, but let's be on the safe side.
case timingMode of
NoTiming -> evaluate term & handleEResult printMode
Timing n -> timeEval n evaluate term >>= handleTimingResults term
case evaluate term of
Right v -> print (getPrintMethod printMode v) >> exitSuccess
Left err -> print err *> exitFailure

----------------- Print examples -----------------------

runPlcPrintExample ::
ExampleOptions -> IO ()
runPlcPrintExample :: ExampleOptions -> IO ()
runPlcPrintExample = runPrintExample getPlcExamples

---------------- Erasure ----------------
Expand Down
70 changes: 10 additions & 60 deletions plutus-core/executables/src/PlutusCore/Executable/Common.hs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ module PlutusCore.Executable.Common
, getPlcExamples
, getPrintMethod
, getUplcExamples
, handleEResult
, handleTimingResults
, helpText
, loadASTfromFlat
, parseInput
Expand All @@ -29,7 +27,6 @@ module PlutusCore.Executable.Common
, runPrint
, runPrintBuiltinSignatures
, runPrintExample
, timeEval
, topSrcSpan
, writeFlat
, writePrettyToFileOrStd
Expand Down Expand Up @@ -71,14 +68,13 @@ import PlutusIR.Check.Uniques as PIR (checkProgram)
import PlutusIR.Core.Instance.Pretty ()
import PlutusIR.Parser qualified as PIR (parse, program)

import Control.DeepSeq (rnf)
import Control.Monad.Except
import Data.Aeson qualified as Aeson
import Data.ByteString.Lazy qualified as BSL
import Data.Foldable (traverse_)
import Data.HashMap.Monoidal qualified as H
import Data.Kind (Type)
import Data.List (intercalate, nub)
import Data.List (intercalate)
import Data.List qualified as List
import Data.Maybe (fromJust)
import Data.Proxy (Proxy (..))
Expand All @@ -89,9 +85,6 @@ import Flat (Flat)
import GHC.TypeLits (symbolVal)
import Prettyprinter ((<+>))

import System.CPUTime (getCPUTime)
import System.Exit (exitFailure, exitSuccess)
import System.Mem (performGC)
import Text.Megaparsec (errorBundlePretty)
import Text.Printf (printf)

Expand Down Expand Up @@ -143,6 +136,15 @@ instance ProgramLike UplcProg where

---------------- Printing budgets and costs ----------------

-- Convert a time in picoseconds into a readable format with appropriate units
formatTimePicoseconds :: Double -> String
formatTimePicoseconds t
| t >= 1e12 = printf "%.3f s" (t / 1e12)
| t >= 1e9 = printf "%.3f ms" (t / 1e9)
| t >= 1e6 = printf "%.3f μs" (t / 1e6)
| t >= 1e3 = printf "%.3f ns" (t / 1e3)
| otherwise = printf "%f ps" t

printBudgetStateBudget :: CekModel -> ExBudget -> IO ()
printBudgetStateBudget model b =
case model of
Expand Down Expand Up @@ -464,58 +466,6 @@ getUplcExamples =
-- is requested and at each lookup of a particular example. I.e. each time we generate distinct
-- terms. But types of those terms must not change across requests, so we're safe.

---------------- Timing ----------------

-- Convert a time in picoseconds into a readable format with appropriate units
formatTimePicoseconds :: Double -> String
formatTimePicoseconds t
| t >= 1e12 = printf "%.3f s" (t / 1e12)
| t >= 1e9 = printf "%.3f ms" (t / 1e9)
| t >= 1e6 = printf "%.3f μs" (t / 1e6)
| t >= 1e3 = printf "%.3f ns" (t / 1e3)
| otherwise = printf "%f ps" t

{- | Apply an evaluator to a program a number of times and report the mean execution
time. The first measurement is often significantly larger than the rest
(perhaps due to warm-up effects), and this can distort the mean. To avoid this
we measure the evaluation time (n+1) times and discard the first result.
-}
timeEval :: NFData a => Integer -> (t -> a) -> t -> IO [a]
timeEval n evaluate prog
| n <= 0 = error "Error: the number of repetitions should be at least 1"
| otherwise = do
(results, times) <-
unzip . tail <$> for (replicate (fromIntegral (n + 1)) prog) (timeOnce evaluate)
let mean = fromIntegral (sum times) / fromIntegral n :: Double
runs :: String = if n == 1 then "run" else "runs"
printf "Mean evaluation time (%d %s): %s\n" n runs (formatTimePicoseconds mean)
pure results
where
timeOnce eval prg = do
start <- performGC >> getCPUTime
let result = eval prg
!_ = rnf result
end <- getCPUTime
pure (result, end - start)

------------ Aux functions for @runEval@ ------------------

handleEResult ::
(PP.PrettyBy PP.PrettyConfigPlc a1, Show a2) =>
PrintMode ->
Either a2 a1 ->
IO b
handleEResult printMode result =
case result of
Right v -> print (getPrintMethod printMode v) >> exitSuccess
Left err -> print err *> exitFailure
handleTimingResults :: (Eq a1, Eq b, Show a1) => p -> [Either a1 b] -> IO a2
handleTimingResults _ results =
case nub results of
[Right _] -> exitSuccess -- We don't want to see the result here
[Left err] -> print err >> exitFailure
-- Should never happen
_ -> error "Timing evaluations returned inconsistent results"

----------------- Print examples -----------------------

Expand Down
22 changes: 0 additions & 22 deletions plutus-core/executables/src/PlutusCore/Executable/Parsers.hs
Original file line number Diff line number Diff line change
Expand Up @@ -82,28 +82,6 @@ outputformat = option (maybeReader formatReader)
<> showDefault
<> help ("Output format: " ++ formatHelp))

-- -x -> run 100 times and print the mean time
timing1 :: Parser TimingMode
timing1 = flag NoTiming (Timing 100)
( short 'x'
<> help "Report mean execution time of program over 100 repetitions"
)

-- -X N -> run N times and print the mean time
timing2 :: Parser TimingMode
timing2 = Timing <$> option auto
( long "time-execution"
<> short 'X'
<> metavar "N"
<> help ("Report mean execution time of program over N repetitions. "
<> " Use a large value of N if possible to get accurate results.")
)

-- We really do need two separate parsers here.
-- See https://github.com/pcapriotti/optparse-applicative/issues/194#issuecomment-205103230
timingmode :: Parser TimingMode
timingmode = timing1 <|> timing2

tracemode :: Parser TraceMode
tracemode = option auto
( long "trace-mode"
Expand Down
Loading