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

[Builtins] Inline 'mkMachineParameters' in its full glory #4481

Merged
merged 1 commit into from
Mar 21, 2022
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
5 changes: 4 additions & 1 deletion plutus-core/plutus-core/src/PlutusCore/Builtin/Runtime.hs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import Control.Lens (ix, (^?))
import Control.Monad.Except
import Data.Array
import Data.Kind qualified as GHC (Type)
import GHC.Exts (inline)
import PlutusCore.Builtin.KnownType

-- | Same as 'TypeScheme' except this one doesn't contain any evaluation-irrelevant types stuff.
Expand Down Expand Up @@ -90,13 +91,15 @@ toBuiltinRuntime :: cost -> BuiltinMeaning val cost -> BuiltinRuntime val
toBuiltinRuntime cost (BuiltinMeaning sch f exF) =
BuiltinRuntime (typeSchemeToRuntimeScheme sch) f (exF cost)

-- See Note [Inlining meanings of builtins].
-- | Calculate runtime info for all built-in functions given denotations of builtins
-- and a cost model.
toBuiltinsRuntime
:: (cost ~ CostingPart uni fun, HasConstantIn uni val, ToBuiltinMeaning uni fun)
=> cost -> BuiltinsRuntime fun val
toBuiltinsRuntime cost =
BuiltinsRuntime . tabulateArray $ toBuiltinRuntime cost . toBuiltinMeaning
BuiltinsRuntime . tabulateArray $ toBuiltinRuntime cost . inline toBuiltinMeaning
{-# INLINE toBuiltinsRuntime #-}

-- | Look up the runtime info of a built-in function during evaluation.
lookupBuiltin
Expand Down
2 changes: 2 additions & 0 deletions plutus-core/plutus-core/src/PlutusCore/Default/Builtins.hs
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,8 @@ instance uni ~ DefaultUni => ToBuiltinMeaning uni DefaultFun where
makeBuiltinMeaning
(\() -> [] @(Data,Data))
(runCostingFunOneArgument . paramMkNilPairData)
-- See Note [Inlining meanings of builtins].
{-# INLINE toBuiltinMeaning #-}

-- It's set deliberately to give us "extra room" in the binary format to add things without running
-- out of space for tags (expanding the space would change the binary format for people who're
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import PlutusCore.Builtin
import PlutusCore.Evaluation.Machine.ExBudget ()

import Control.DeepSeq
import GHC.Exts (inline)
import GHC.Generics
import GHC.Types (Type)

Expand Down Expand Up @@ -42,6 +43,7 @@ data MachineParameters machinecosts term (uni :: Type -> Type) (fun :: Type) =
deriving stock Generic
deriving anyclass NFData

-- See Note [Inlining meanings of builtins].
{-| This just uses 'toBuiltinsRuntime' function to convert a BuiltinCostModel to a BuiltinsRuntime. -}
mkMachineParameters ::
( -- In Cek.Internal we have `type instance UniOf (CekValue uni fun) = uni`, but we don't know that here.
Expand All @@ -52,4 +54,5 @@ mkMachineParameters ::
=> CostModel machinecosts builtincosts
-> MachineParameters machinecosts val uni fun
mkMachineParameters (CostModel mchnCosts builtinCosts) =
MachineParameters mchnCosts (toBuiltinsRuntime builtinCosts)
MachineParameters mchnCosts (inline toBuiltinsRuntime builtinCosts)
{-# INLINE mkMachineParameters #-}
26 changes: 25 additions & 1 deletion plutus-ledger-api/src/Plutus/V1/Ledger/EvaluationContext.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,44 @@ import Data.Map as Map
import Data.Maybe
import Data.Set as Set
import Data.Text qualified as Text
import GHC.Exts (inline)

-- | An opaque type that contains all the static parameters that the evaluator needs to evaluate a script.
-- This is so that they can be computed once and cached, rather than recomputed on every evaluation.
newtype EvaluationContext = EvaluationContext
{ toMachineParameters :: Plutus.MachineParameters CekMachineCosts CekValue DefaultUni DefaultFun }
deriving newtype NFData

{- Note [Inlining meanings of builtins]
It's vitally important to inline the 'toBuiltinMeaning' method of a set of built-in functions as
that allows GHC to look under lambdas and completely optimize multiple abstractions away.

There are two ways of doing that: by relying on 'INLINE' pragmas all the way up from the
'ToBuiltinMeaning' instance for the default set of builtins or by ensuring that 'toBuiltinsRuntime'
is compiled efficient by turning it into a one-method class (see
https://github.com/input-output-hk/plutus/pull/4419 for how that looks like). We chose the former,
because it's simpler. Although it's also less reliable: machine parameters are computed in
multiple places and we need to make sure that benchmarking, cost model calculations and the actual
production path have builtins compiled in the same way, 'cause otherwise performance analysis and
cost predictions can be wrong by a big margin without us knowing. Because of this danger in addition
to putting @INLINE@ pragmas on every relevant definition, we also stick a call to 'inline' at the
call site. We also do not attempt to only compile things efficiently where we need that and instead
inline the meanins of builtins everywhere. Just to be sure.

Note that a combination of @INLINABLE@ + 'inline' does not result in proper inlining for whatever
reason. It has to be @INLINE@ (and we add 'inline' on top of that for some additional reliability
as we did have cases where sticking 'inline' on something that already had @INLINE@ would fix
inlining).
-}

-- See Note [Inlining meanings of builtins].
-- | Build the 'EvaluationContext'.
--
-- The input is a `Map` of strings to cost integer values (aka `Plutus.CostModelParams`, `Alonzo.CostModel`)
mkEvaluationContext :: Plutus.CostModelParams
-> Maybe EvaluationContext
mkEvaluationContext newCMP =
EvaluationContext . Plutus.mkMachineParameters <$> Plutus.applyCostModelParams Plutus.defaultCekCostModel newCMP
EvaluationContext . inline Plutus.mkMachineParameters <$> Plutus.applyCostModelParams Plutus.defaultCekCostModel newCMP

-- | Comparably expensive to `mkEvaluationContext`, so it should only be used sparingly.
isCostModelParamsWellFormed :: Plutus.CostModelParams -> Bool
Expand Down