From ce5acfe71f5fbc1178f6b973178a9f565e8c8579 Mon Sep 17 00:00:00 2001 From: effectfully <effectfully@gmail.com> Date: Wed, 4 May 2022 23:46:30 +0200 Subject: [PATCH 01/10] [Builtins] Optimize 'MakeKnownM' --- .../src/PlutusCore/Builtin/KnownType.hs | 80 +++++++++++++++---- .../src/PlutusCore/Builtin/Meaning.hs | 3 +- .../src/PlutusCore/Builtin/Runtime.hs | 4 +- .../src/PlutusCore/Evaluation/Machine/Ck.hs | 10 +-- .../plutus-core/test/Evaluation/Spec.hs | 4 +- .../Evaluation/Machine/Cek/Internal.hs | 11 +-- 6 files changed, 81 insertions(+), 31 deletions(-) diff --git a/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs b/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs index 604441c0b8d..8d5f3653f76 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs @@ -18,20 +18,18 @@ module PlutusCore.Builtin.KnownType , throwKnownTypeErrorWithCause , KnownBuiltinTypeIn , KnownBuiltinType - , MakeKnownM + , MakeKnownM (..) , ReadKnownM + , liftReadKnownM , readKnownConstant , MakeKnownIn (..) , MakeKnown , ReadKnownIn (..) , ReadKnown - , makeKnownRun , makeKnownOrFail , readKnownSelf ) where -import PlutusPrelude (reoption) - import PlutusCore.Builtin.Emitter import PlutusCore.Builtin.HasConstant import PlutusCore.Builtin.Polymorphism @@ -195,12 +193,68 @@ a ton of instances (and add new ones whenever we need them), wrap and unwrap all -- See Note [MakeKnownM and ReadKnownM being type synonyms]. -- | The monad that 'makeKnown' runs in. -type MakeKnownM = ExceptT KnownTypeError Emitter +data MakeKnownM a + = MakeKnownFailure !(DList Text) !KnownTypeError + | MakeKnownSuccess !a + | MakeKnownSuccessWithLogs !(DList Text) !a + +fmapWithLogs :: DList Text -> (a -> b) -> MakeKnownM a -> MakeKnownM b +fmapWithLogs logs1 f = \case + MakeKnownFailure logs2 err -> MakeKnownFailure (logs1 <> logs2) err + MakeKnownSuccess x -> MakeKnownSuccessWithLogs logs1 (f x) + MakeKnownSuccessWithLogs logs2 x -> MakeKnownSuccessWithLogs (logs1 <> logs2) (f x) +{-# INLINE fmapWithLogs #-} + +withLogs :: DList Text -> MakeKnownM a -> MakeKnownM a +withLogs logs1 = \case + MakeKnownFailure logs2 err -> MakeKnownFailure (logs1 <> logs2) err + MakeKnownSuccess x -> MakeKnownSuccessWithLogs logs1 x + MakeKnownSuccessWithLogs logs2 x -> MakeKnownSuccessWithLogs (logs1 <> logs2) x +{-# INLINE withLogs #-} + +instance Functor MakeKnownM where + fmap _ (MakeKnownFailure logs err) = MakeKnownFailure logs err + fmap f (MakeKnownSuccess x) = MakeKnownSuccess (f x) + fmap f (MakeKnownSuccessWithLogs logs x) = MakeKnownSuccessWithLogs logs (f x) + {-# INLINE fmap #-} + + _ <$ MakeKnownFailure logs err = MakeKnownFailure logs err + x <$ MakeKnownSuccess _ = MakeKnownSuccess x + x <$ MakeKnownSuccessWithLogs logs _ = MakeKnownSuccessWithLogs logs x + {-# INLINE (<$) #-} + +instance Applicative MakeKnownM where + pure = MakeKnownSuccess + {-# INLINE pure #-} + + MakeKnownFailure logs err <*> _ = MakeKnownFailure logs err + MakeKnownSuccess f <*> a = fmap f a + MakeKnownSuccessWithLogs logs f <*> a = fmapWithLogs logs f a + {-# INLINE (<*>) #-} + + MakeKnownFailure logs err *> _ = MakeKnownFailure logs err + MakeKnownSuccess _ *> a = a + MakeKnownSuccessWithLogs logs _ *> a = withLogs logs a + {-# INLINE (*>) #-} + +instance Monad MakeKnownM where + MakeKnownFailure logs err >>= _ = MakeKnownFailure logs err + MakeKnownSuccess x >>= f = f x + MakeKnownSuccessWithLogs logs x >>= f = withLogs logs $ f x + {-# INLINE (>>=) #-} + + (>>) = (*>) + {-# INLINE (>>) #-} -- See Note [MakeKnownM and ReadKnownM being type synonyms]. -- | The monad that 'readKnown' runs in. type ReadKnownM = Either KnownTypeError +liftReadKnownM :: ReadKnownM a -> MakeKnownM a +liftReadKnownM (Left err) = MakeKnownFailure mempty err +liftReadKnownM (Right x) = MakeKnownSuccess x +{-# INLINE liftReadKnownM #-} + -- See Note [Unlifting values of built-in types]. -- | Convert a constant embedded into a PLC term to the corresponding Haskell value. readKnownConstant :: forall val a. KnownBuiltinType val a => val -> ReadKnownM a @@ -253,15 +307,12 @@ class uni ~ UniOf val => ReadKnownIn uni val a where type ReadKnown val = ReadKnownIn (UniOf val) val -makeKnownRun - :: MakeKnownIn uni val a - => a -> (ReadKnownM val, DList Text) -makeKnownRun = runEmitter . runExceptT . makeKnown -{-# INLINE makeKnownRun #-} - -- | Same as 'makeKnown', but allows for neither emitting nor storing the cause of a failure. makeKnownOrFail :: MakeKnownIn uni val a => a -> EvaluationResult val -makeKnownOrFail = reoption . fst . makeKnownRun +makeKnownOrFail x = case makeKnown x of + MakeKnownFailure _ _ -> EvaluationFailure + MakeKnownSuccess val -> EvaluationSuccess val + MakeKnownSuccessWithLogs _ val -> EvaluationSuccess val {-# INLINE makeKnownOrFail #-} -- | Same as 'readKnown', but the cause of a potential failure is the provided term itself. @@ -274,7 +325,7 @@ readKnownSelf val = either (throwKnownTypeErrorWithCause val) pure $ readKnown v {-# INLINE readKnownSelf #-} instance MakeKnownIn uni val a => MakeKnownIn uni val (EvaluationResult a) where - makeKnown EvaluationFailure = throwing_ _EvaluationFailure + makeKnown EvaluationFailure = MakeKnownFailure mempty KnownTypeEvaluationFailure makeKnown (EvaluationSuccess x) = makeKnown x {-# INLINE makeKnown #-} @@ -291,7 +342,8 @@ instance readKnown _ = throwing _UnliftingError "Panic: 'TypeError' was bypassed" instance MakeKnownIn uni val a => MakeKnownIn uni val (Emitter a) where - makeKnown = lift >=> makeKnown + makeKnown a = case runEmitter a of + (x, logs) -> withLogs logs $ makeKnown x {-# INLINE makeKnown #-} instance diff --git a/plutus-core/plutus-core/src/PlutusCore/Builtin/Meaning.hs b/plutus-core/plutus-core/src/PlutusCore/Builtin/Meaning.hs index f6ffb5c567d..9cf0f36f11c 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Builtin/Meaning.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Builtin/Meaning.hs @@ -31,7 +31,6 @@ import PlutusCore.Builtin.TypeScheme import PlutusCore.Core import PlutusCore.Name -import Control.Monad.Except import Data.Array import Data.Kind qualified as GHC import Data.Proxy @@ -177,7 +176,7 @@ instance (res ~ res', Typeable res, KnownTypeAst (UniOf val) res, MakeKnown val -- For deferred unlifting we need to lift the 'ReadKnownM' action into 'MakeKnownM', -- hence 'liftEither'. - toDeferredF getRes = liftEither getRes >>= makeKnown + toDeferredF getRes = liftReadKnownM getRes >>= makeKnown {-# INLINE toDeferredF #-} -- | Every term-level argument becomes as 'TypeSchemeArrow'. diff --git a/plutus-core/plutus-core/src/PlutusCore/Builtin/Runtime.hs b/plutus-core/plutus-core/src/PlutusCore/Builtin/Runtime.hs index 3fe36f0a5e4..30f3b741567 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Builtin/Runtime.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Builtin/Runtime.hs @@ -104,8 +104,8 @@ data UnliftingMode -- instantiating '_broToExF' with a cost model to get the costing function for the builtin. data BuiltinRuntimeOptions n val cost = BuiltinRuntimeOptions { _broRuntimeScheme :: RuntimeScheme n - , _broImmediateF :: ToRuntimeDenotationType val n - , _broDeferredF :: ToRuntimeDenotationType val n + , _broImmediateF :: ~(ToRuntimeDenotationType val n) + , _broDeferredF :: ~(ToRuntimeDenotationType val n) , _broToExF :: cost -> ToCostingType n } diff --git a/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/Ck.hs b/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/Ck.hs index e80de2a9696..237f759f721 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/Ck.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Evaluation/Machine/Ck.hs @@ -67,12 +67,10 @@ evalBuiltinApp -> BuiltinRuntime (CkValue uni fun) -> CkM uni fun s (CkValue uni fun) evalBuiltinApp term runtime@(BuiltinRuntime sch getX _) = case sch of - RuntimeSchemeResult -> do - let (errOrRes, logs) = runEmitter $ runExceptT getX - emitCkM logs - case errOrRes of - Left err -> throwKnownTypeErrorWithCause term err - Right res -> pure res + RuntimeSchemeResult -> case getX of + MakeKnownFailure logs err -> emitCkM logs *> throwKnownTypeErrorWithCause term err + MakeKnownSuccess x -> pure x + MakeKnownSuccessWithLogs logs x -> emitCkM logs $> x _ -> pure $ VBuiltin term runtime ckValueToTerm :: CkValue uni fun -> Term TyName Name uni fun () diff --git a/plutus-core/plutus-core/test/Evaluation/Spec.hs b/plutus-core/plutus-core/test/Evaluation/Spec.hs index 34785a73375..45d40171751 100644 --- a/plutus-core/plutus-core/test/Evaluation/Spec.hs +++ b/plutus-core/plutus-core/test/Evaluation/Spec.hs @@ -55,7 +55,7 @@ prop_builtinsDon'tThrow bn = property $ do mbErr <- liftIO $ catch - (($> Nothing) . evaluate . runEmitter . runExceptT $ eval args) + (($> Nothing) . evaluate $ eval args) (pure . pure) whenJust mbErr $ \(e :: SomeException) -> do annotate "Builtin function evaluation failed" @@ -79,7 +79,7 @@ prop_builtinsDon'tThrow bn = property $ do MakeKnownM Term go sch f args = case (sch, args) of (RuntimeSchemeArrow sch', a : as) -> do - res <- liftEither (f a) + res <- liftReadKnownM (f a) go sch' res as (RuntimeSchemeResult, []) -> f (RuntimeSchemeAll sch', _) -> go sch' f args diff --git a/plutus-core/untyped-plutus-core/src/UntypedPlutusCore/Evaluation/Machine/Cek/Internal.hs b/plutus-core/untyped-plutus-core/src/UntypedPlutusCore/Evaluation/Machine/Cek/Internal.hs index 3a780ab10ee..1054988a313 100644 --- a/plutus-core/untyped-plutus-core/src/UntypedPlutusCore/Evaluation/Machine/Cek/Internal.hs +++ b/plutus-core/untyped-plutus-core/src/UntypedPlutusCore/Evaluation/Machine/Cek/Internal.hs @@ -565,11 +565,12 @@ evalBuiltinApp evalBuiltinApp fun term env runtime@(BuiltinRuntime sch getX cost) = case sch of RuntimeSchemeResult -> do spendBudgetCek (BBuiltinApp fun) cost - let !(errOrRes, logs) = runEmitter $ runExceptT getX - ?cekEmitter logs - case errOrRes of - Left err -> throwKnownTypeErrorWithCause term err - Right res -> pure res + case getX of + MakeKnownFailure logs err -> do + ?cekEmitter logs + throwKnownTypeErrorWithCause term err + MakeKnownSuccess x -> pure x + MakeKnownSuccessWithLogs logs x -> ?cekEmitter logs $> x _ -> pure $ VBuiltin fun term env runtime {-# INLINE evalBuiltinApp #-} From ade58d8f3fcb463bbdc1917e2c1d0a5840101ae5 Mon Sep 17 00:00:00 2001 From: effectfully <effectfully@gmail.com> Date: Mon, 9 May 2022 23:00:37 +0200 Subject: [PATCH 02/10] Docs --- .../src/PlutusCore/Builtin/KnownType.hs | 32 ++++++++++++------- .../src/PlutusCore/Builtin/Runtime.hs | 4 +++ 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs b/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs index 8d5f3653f76..168fa25820f 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs @@ -182,22 +182,22 @@ typeMismatchError uniExp uniAct = fromString $ concat -- failure message and evaluation is about to be shut anyway. {-# NOINLINE typeMismatchError #-} -{- Note [MakeKnownM and ReadKnownM being type synonyms] -Normally it's a good idea for an exported abstraction not to be a type synonym, since a @newtype@ -is cheap, looks good in error messages and clearly emphasize an abstraction barrier. However we -make 'MakeKnownM' and 'ReadKnownM' type synonyms for convenience: that way we don't need to derive -a ton of instances (and add new ones whenever we need them), wrap and unwrap all the time -(including in user code), which can be non-trivial for such performance-sensitive code (see e.g. -'coerceVia' and 'coerceArg') and there is no abstraction barrier anyway. --} - --- See Note [MakeKnownM and ReadKnownM being type synonyms]. -- | The monad that 'makeKnown' runs in. +-- Equivalent to @ExceptT KnownTypeError Emitter@, except optimized in two ways: +-- +-- 1. everything is strict +-- 2. has the 'MakeKnownSuccess' constructor that is used for returning a value with no logs +-- attached, which is the most common case for us, so it helps a lot not to construct and +-- deconstruct a redundant tuple +-- +-- Moving from @ExceptT KnownTypeError Emitter@ to this data type gave us a speedup of 8% of total +-- evaluation time. data MakeKnownM a = MakeKnownFailure !(DList Text) !KnownTypeError | MakeKnownSuccess !a | MakeKnownSuccessWithLogs !(DList Text) !a +-- | Prepend logs to a 'MakeKnownM' computation and 'fmap' it. fmapWithLogs :: DList Text -> (a -> b) -> MakeKnownM a -> MakeKnownM b fmapWithLogs logs1 f = \case MakeKnownFailure logs2 err -> MakeKnownFailure (logs1 <> logs2) err @@ -205,6 +205,7 @@ fmapWithLogs logs1 f = \case MakeKnownSuccessWithLogs logs2 x -> MakeKnownSuccessWithLogs (logs1 <> logs2) (f x) {-# INLINE fmapWithLogs #-} +-- | Prepend logs to a 'MakeKnownM' computation. withLogs :: DList Text -> MakeKnownM a -> MakeKnownM a withLogs logs1 = \case MakeKnownFailure logs2 err -> MakeKnownFailure (logs1 <> logs2) err @@ -213,11 +214,14 @@ withLogs logs1 = \case {-# INLINE withLogs #-} instance Functor MakeKnownM where + -- Written out explicitly, because for some inexplicable reason GHC fails to inline + -- @fmapWithLogs mempty@ despite the pragma. fmap _ (MakeKnownFailure logs err) = MakeKnownFailure logs err fmap f (MakeKnownSuccess x) = MakeKnownSuccess (f x) fmap f (MakeKnownSuccessWithLogs logs x) = MakeKnownSuccessWithLogs logs (f x) {-# INLINE fmap #-} + -- Written out explicitly just in case (see @fmap@ above for what the case might be). _ <$ MakeKnownFailure logs err = MakeKnownFailure logs err x <$ MakeKnownSuccess _ = MakeKnownSuccess x x <$ MakeKnownSuccessWithLogs logs _ = MakeKnownSuccessWithLogs logs x @@ -246,10 +250,16 @@ instance Monad MakeKnownM where (>>) = (*>) {-# INLINE (>>) #-} --- See Note [MakeKnownM and ReadKnownM being type synonyms]. +-- Normally it's a good idea for an exported abstraction not to be a type synonym, since a @newtype@ +-- is cheap, looks good in error messages and clearly emphasize an abstraction barrier. However we +-- make 'ReadKnownM' type synonyms for convenience: that way we don't need to derive all the +-- instances (and add new ones whenever we need them), wrap and unwrap all the time +-- (including in user code), which can be non-trivial for such performance-sensitive code (see e.g. +-- 'coerceVia' and 'coerceArg') and there is no abstraction barrier anyway. -- | The monad that 'readKnown' runs in. type ReadKnownM = Either KnownTypeError +-- | Lift a 'ReadKnownM' computation into 'MakeKnownM'. liftReadKnownM :: ReadKnownM a -> MakeKnownM a liftReadKnownM (Left err) = MakeKnownFailure mempty err liftReadKnownM (Right x) = MakeKnownSuccess x diff --git a/plutus-core/plutus-core/src/PlutusCore/Builtin/Runtime.hs b/plutus-core/plutus-core/src/PlutusCore/Builtin/Runtime.hs index 30f3b741567..ed8b2ce63e7 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Builtin/Runtime.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Builtin/Runtime.hs @@ -102,6 +102,10 @@ data UnliftingMode -- former by choosing the runtime denotation of the builtin (either '_broImmediateF' for immediate -- unlifting or '_broDeferredF' for deferred unlifting, see 'UnliftingMode' for details) and by -- instantiating '_broToExF' with a cost model to get the costing function for the builtin. +-- +-- The runtime denotations are lazy, so that we don't need to worry about a builtin being bottom +-- (happens in tests). The production path is not affected by that, since 'BuiltinRuntimeOptions' +-- doesn't survive optimization. data BuiltinRuntimeOptions n val cost = BuiltinRuntimeOptions { _broRuntimeScheme :: RuntimeScheme n , _broImmediateF :: ~(ToRuntimeDenotationType val n) From 54665b928463523953352a3135e319f522dcc67a Mon Sep 17 00:00:00 2001 From: effectfully <effectfully@gmail.com> Date: Tue, 10 May 2022 14:14:03 +0200 Subject: [PATCH 03/10] Drop 'fmapWithLogs' --- .../plutus-core/src/PlutusCore/Builtin/KnownType.hs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs b/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs index 168fa25820f..f2573c93e87 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs @@ -197,14 +197,6 @@ data MakeKnownM a | MakeKnownSuccess !a | MakeKnownSuccessWithLogs !(DList Text) !a --- | Prepend logs to a 'MakeKnownM' computation and 'fmap' it. -fmapWithLogs :: DList Text -> (a -> b) -> MakeKnownM a -> MakeKnownM b -fmapWithLogs logs1 f = \case - MakeKnownFailure logs2 err -> MakeKnownFailure (logs1 <> logs2) err - MakeKnownSuccess x -> MakeKnownSuccessWithLogs logs1 (f x) - MakeKnownSuccessWithLogs logs2 x -> MakeKnownSuccessWithLogs (logs1 <> logs2) (f x) -{-# INLINE fmapWithLogs #-} - -- | Prepend logs to a 'MakeKnownM' computation. withLogs :: DList Text -> MakeKnownM a -> MakeKnownM a withLogs logs1 = \case @@ -233,7 +225,7 @@ instance Applicative MakeKnownM where MakeKnownFailure logs err <*> _ = MakeKnownFailure logs err MakeKnownSuccess f <*> a = fmap f a - MakeKnownSuccessWithLogs logs f <*> a = fmapWithLogs logs f a + MakeKnownSuccessWithLogs logs f <*> a = withLogs logs $ fmap f a {-# INLINE (<*>) #-} MakeKnownFailure logs err *> _ = MakeKnownFailure logs err From 6377aa69f59f506787390413706b83542ff00c1c Mon Sep 17 00:00:00 2001 From: effectfully <effectfully@gmail.com> Date: Tue, 10 May 2022 15:01:47 +0200 Subject: [PATCH 04/10] Add 'StrictData' --- .../src/PlutusCore/Builtin/KnownType.hs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs b/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs index f2573c93e87..53cae255e76 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs @@ -13,6 +13,8 @@ {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE StrictData #-} + module PlutusCore.Builtin.KnownType ( KnownTypeError , throwKnownTypeErrorWithCause @@ -193,9 +195,9 @@ typeMismatchError uniExp uniAct = fromString $ concat -- Moving from @ExceptT KnownTypeError Emitter@ to this data type gave us a speedup of 8% of total -- evaluation time. data MakeKnownM a - = MakeKnownFailure !(DList Text) !KnownTypeError - | MakeKnownSuccess !a - | MakeKnownSuccessWithLogs !(DList Text) !a + = MakeKnownFailure (DList Text) KnownTypeError + | MakeKnownSuccess a + | MakeKnownSuccessWithLogs (DList Text) a -- | Prepend logs to a 'MakeKnownM' computation. withLogs :: DList Text -> MakeKnownM a -> MakeKnownM a @@ -288,10 +290,11 @@ class uni ~ UniOf val => MakeKnownIn uni val a where -- The inverse of 'readKnown'. makeKnown :: a -> MakeKnownM val default makeKnown :: KnownBuiltinType val a => a -> MakeKnownM val - -- Forcing the value to avoid space leaks. Note that the value is only forced to WHNF, - -- so care must be taken to ensure that every value of a type from the universe gets forced - -- to NF whenever it's forced to WHNF. - makeKnown x = pure . fromConstant . someValue $! x + -- Everything on evaluation path has to be strict, hence we don't do any extra forcing here to + -- avoid space leaks. Note that the value is only forced to WHNF, so care must be taken to + -- ensure that every value of a type from the universe gets forced to NF whenever it's forced to + -- WHNF. + makeKnown = pure . fromConstant . someValue {-# INLINE makeKnown #-} type MakeKnown val = MakeKnownIn (UniOf val) val From c27e088cad4c6bbdc6d596b21ebf33bde13a7fa1 Mon Sep 17 00:00:00 2001 From: effectfully <effectfully@gmail.com> Date: Tue, 10 May 2022 16:06:18 +0200 Subject: [PATCH 05/10] Drop 'StrictData' --- plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs | 2 -- 1 file changed, 2 deletions(-) diff --git a/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs b/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs index 53cae255e76..9479f78d189 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs @@ -13,8 +13,6 @@ {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} -{-# LANGUAGE StrictData #-} - module PlutusCore.Builtin.KnownType ( KnownTypeError , throwKnownTypeErrorWithCause From f6845efe811ab0082fc58273aeda2ab9aacfb936 Mon Sep 17 00:00:00 2001 From: effectfully <effectfully@gmail.com> Date: Tue, 10 May 2022 16:20:14 +0200 Subject: [PATCH 06/10] A bit more comments --- plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs b/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs index 9479f78d189..a89e77b176c 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs @@ -192,6 +192,10 @@ typeMismatchError uniExp uniAct = fromString $ concat -- -- Moving from @ExceptT KnownTypeError Emitter@ to this data type gave us a speedup of 8% of total -- evaluation time. +-- +-- Logs are represented as a 'DList', because we don't particularly care about the efficiency of +-- logging, since there's no logging on the chain and builtins don't emit much anyway. Otherwise +-- we'd have to use @text-builder@ or @text-builder-linear@ or something of this sort. data MakeKnownM a = MakeKnownFailure (DList Text) KnownTypeError | MakeKnownSuccess a @@ -228,6 +232,8 @@ instance Applicative MakeKnownM where MakeKnownSuccessWithLogs logs f <*> a = withLogs logs $ fmap f a {-# INLINE (<*>) #-} + -- Better than the default implementation, because the value in the 'MakeKnownSuccess' case + -- doesn't need to be retained. MakeKnownFailure logs err *> _ = MakeKnownFailure logs err MakeKnownSuccess _ *> a = a MakeKnownSuccessWithLogs logs _ *> a = withLogs logs a From 20f4e15e2ed54433bf5117f9ca64fef94241fe95 Mon Sep 17 00:00:00 2001 From: effectfully <effectfully@gmail.com> Date: Tue, 10 May 2022 17:04:06 +0200 Subject: [PATCH 07/10] And now do the forcing properly --- plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs b/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs index a89e77b176c..74f88d06c00 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs @@ -298,7 +298,7 @@ class uni ~ UniOf val => MakeKnownIn uni val a where -- avoid space leaks. Note that the value is only forced to WHNF, so care must be taken to -- ensure that every value of a type from the universe gets forced to NF whenever it's forced to -- WHNF. - makeKnown = pure . fromConstant . someValue + makeKnown x = pure . fromConstant . someValue $! x {-# INLINE makeKnown #-} type MakeKnown val = MakeKnownIn (UniOf val) val From d06d3af8dbf1f8155024127b336681b0900a5731 Mon Sep 17 00:00:00 2001 From: effectfully <effectfully@gmail.com> Date: Tue, 10 May 2022 17:46:50 +0200 Subject: [PATCH 08/10] Revert "Drop 'StrictData'" This reverts commit c27e088cad4c6bbdc6d596b21ebf33bde13a7fa1. --- plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs b/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs index 74f88d06c00..21eab085c97 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs @@ -13,6 +13,8 @@ {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} +{-# LANGUAGE StrictData #-} + module PlutusCore.Builtin.KnownType ( KnownTypeError , throwKnownTypeErrorWithCause From f93d9df525195b22eedd8020a103a3494643c53e Mon Sep 17 00:00:00 2001 From: effectfully <effectfully@gmail.com> Date: Tue, 10 May 2022 17:47:45 +0200 Subject: [PATCH 09/10] Fixups --- plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs b/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs index 21eab085c97..15fa17103d4 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs @@ -212,8 +212,6 @@ withLogs logs1 = \case {-# INLINE withLogs #-} instance Functor MakeKnownM where - -- Written out explicitly, because for some inexplicable reason GHC fails to inline - -- @fmapWithLogs mempty@ despite the pragma. fmap _ (MakeKnownFailure logs err) = MakeKnownFailure logs err fmap f (MakeKnownSuccess x) = MakeKnownSuccess (f x) fmap f (MakeKnownSuccessWithLogs logs x) = MakeKnownSuccessWithLogs logs (f x) @@ -300,7 +298,7 @@ class uni ~ UniOf val => MakeKnownIn uni val a where -- avoid space leaks. Note that the value is only forced to WHNF, so care must be taken to -- ensure that every value of a type from the universe gets forced to NF whenever it's forced to -- WHNF. - makeKnown x = pure . fromConstant . someValue $! x + makeKnown = pure . fromConstant . someValue {-# INLINE makeKnown #-} type MakeKnown val = MakeKnownIn (UniOf val) val From bd8071f910ab0b53f23eacd9dc6ccdd29e1f2d5a Mon Sep 17 00:00:00 2001 From: effectfully <effectfully@gmail.com> Date: Wed, 11 May 2022 01:09:11 +0200 Subject: [PATCH 10/10] Back to the bang --- .../src/PlutusCore/Builtin/KnownType.hs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs b/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs index 15fa17103d4..1a8489d1cb8 100644 --- a/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs +++ b/plutus-core/plutus-core/src/PlutusCore/Builtin/KnownType.hs @@ -294,11 +294,15 @@ class uni ~ UniOf val => MakeKnownIn uni val a where -- The inverse of 'readKnown'. makeKnown :: a -> MakeKnownM val default makeKnown :: KnownBuiltinType val a => a -> MakeKnownM val - -- Everything on evaluation path has to be strict, hence we don't do any extra forcing here to - -- avoid space leaks. Note that the value is only forced to WHNF, so care must be taken to - -- ensure that every value of a type from the universe gets forced to NF whenever it's forced to - -- WHNF. - makeKnown = pure . fromConstant . someValue + -- Everything on evaluation path has to be strict in production, so in theory we don't need to + -- force anything here. In practice however all kinds of weird things happen in tests and @val@ + -- can be non-strict enough to cause trouble here, so we're forcing the argument. Looking at the + -- generated Core, the forcing amounts to pulling a @case@ out of the 'fromConstant' call, + -- which doesn't affect the overall cost and benchmarking results suggest the same. + -- + -- Note that the value is only forced to WHNF, so care must be taken to ensure that every value + -- of a type from the universe gets forced to NF whenever it's forced to WHNF. + makeKnown x = pure . fromConstant . someValue $! x {-# INLINE makeKnown #-} type MakeKnown val = MakeKnownIn (UniOf val) val