Skip to content

Commit 67aa404

Browse files
committed
Add cost model key to the schema and use it for caching
1 parent f57da3c commit 67aa404

File tree

8 files changed

+119
-39
lines changed

8 files changed

+119
-39
lines changed

database/db.dbm

+5-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ CAUTION: Do not modify this file unless you know what you are doing.
44
Unexpected results may occur if the code is changed deliberately.
55
-->
66
<dbmodel pgmodeler-ver="1.1.5" use-changelog="false" max-obj-count="18"
7-
last-position="257,118" last-zoom="1" scene-rect="0,0,1570.8,1076.8"
7+
last-position="0,14" last-zoom="1" scene-rect="0,0,1570.8,1076.8"
88
default-schema="public" default-owner="postgres"
99
layers="Default layer"
1010
active-layers="0"
@@ -217,18 +217,20 @@ ORDER BY
217217
<role name="&quot;plutus-admin&quot;"/>
218218
<position x="1060" y="520"/>
219219
<definition> <![CDATA[SELECT
220+
SEE.PK,
220221
SEE.SLOT,
221222
SEE.BLOCK,
222223
SEE.MAJOR_PROTOCOL_VERSION,
223224
SEE.EVALUATED_SUCCESSFULLY,
224225
SEE.EXEC_BUDGET_CPU,
225226
SEE.EXEC_BUDGET_MEM,
227+
CMP.PK AS COST_MODEL_KEY,
226228
CMP.PARAM_VALUES AS COST_MODEL_PARAM_VALUES,
229+
SS.SERIALISED AS SCRIPT_SERIALISED,
227230
SEE.DATUM,
228231
SEE.REDEEMER,
229232
SEE.SCRIPT_CONTEXT,
230-
SS.LEDGER_LANGUAGE,
231-
SS.SERIALISED
233+
SS.LEDGER_LANGUAGE
232234
FROM
233235
SCRIPT_EVALUATION_EVENTS AS SEE
234236
JOIN SERIALISED_SCRIPTS SS ON SEE.SCRIPT_HASH = SS.HASH

database/db.png

216 Bytes
Loading

database/db.sql

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
-- Database generated with pgModeler (PostgreSQL Database Modeler).
22
-- pgModeler version: 1.1.5
3-
-- PostgreSQL version: 16.0
3+
-- PostgreSQL version: 17.0
44
-- Project Site: pgmodeler.io
55
-- Model Author: ---
66
-- object: "plutus-admin" | type: ROLE --
@@ -193,18 +193,20 @@ ALTER MATERIALIZED VIEW public.builtin_version_num_scripts OWNER TO "plutus-inde
193193
CREATE VIEW public.script_evaluations
194194
AS
195195
SELECT
196+
SEE.PK,
196197
SEE.SLOT,
197198
SEE.BLOCK,
198199
SEE.MAJOR_PROTOCOL_VERSION,
199200
SEE.EVALUATED_SUCCESSFULLY,
200201
SEE.EXEC_BUDGET_CPU,
201202
SEE.EXEC_BUDGET_MEM,
203+
CMP.PK AS COST_MODEL_KEY,
202204
CMP.PARAM_VALUES AS COST_MODEL_PARAM_VALUES,
205+
SS.SERIALISED AS SCRIPT_SERIALISED,
203206
SEE.DATUM,
204207
SEE.REDEEMER,
205208
SEE.SCRIPT_CONTEXT,
206-
SS.LEDGER_LANGUAGE,
207-
SS.SERIALISED
209+
SS.LEDGER_LANGUAGE
208210
FROM
209211
SCRIPT_EVALUATION_EVENTS AS SEE
210212
JOIN SERIALISED_SCRIPTS SS ON SEE.SCRIPT_HASH = SS.HASH

plutus-script-evaluation/evaluate-scripts/Main.hs

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
{- | This module contains the main entry point
2+
into the program which CEK-evaluates scripts using
3+
the information recorded in the database in a
4+
streaming fashion.
5+
-}
16
module Main where
27

38
import Control.Exception (bracket, catch)

plutus-script-evaluation/lib/Database/Query.hs

+2-3
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,8 @@ withScriptEvaluationEvents
106106
withScriptEvaluationEvents conn blockNo a f = do
107107
let startBlock = pgFromInteger (fromIntegral (unBlockNo blockNo))
108108
select = orderBy (asc seBlockNo) do
109-
res@(MkScriptEvaluationRecord' _slot block _ _ _ _ _ _ _ _ _ _) <-
110-
selectTable scriptEvaluations
111-
where_ (block .>= startBlock)
109+
res <- selectTable scriptEvaluations
110+
where_ (seBlockNo res .>= startBlock)
112111
pure res
113112
withRunInIO \runInIO ->
114113
runSelectFold conn select a \accum record ->

plutus-script-evaluation/lib/Database/Schema.hs

+7-2
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ data
193193
datum
194194
redeemer
195195
scriptContext
196+
costModelKey
196197
costModel
197198
= MkScriptEvaluationRecord'
198199
{ sePk :: pk
@@ -207,6 +208,7 @@ data
207208
, seDatum :: datum
208209
, seRedeemer :: redeemer
209210
, seScriptContext :: scriptContext
211+
, seCostModelKey :: costModelKey
210212
, seCostModelParams :: costModel
211213
}
212214
deriving (Show, Eq)
@@ -225,6 +227,7 @@ type ScriptEvaluationRecord =
225227
(Maybe ByteString) -- datum
226228
(Maybe ByteString) -- redeemer
227229
ByteString -- script_context
230+
Hash64 -- cost_model_key
228231
[Int64] -- cost_model_params
229232

230233
type ScriptEvaluationRecordFields =
@@ -237,10 +240,11 @@ type ScriptEvaluationRecordFields =
237240
(Field SqlBool) -- evaluated_successfully
238241
(Field SqlInt4) -- exec_budget_cpu
239242
(Field SqlInt4) -- exec_budget_mem
240-
(Field SqlBytea) -- script_hash
243+
(Field SqlBytea) -- script
241244
(FieldNullable SqlBytea) -- datum
242245
(FieldNullable SqlBytea) -- redeemer
243246
(Field SqlBytea) -- script_context
247+
(Field SqlInt8) -- cost_model_params
244248
(Field (SqlArray SqlInt8)) -- cost_model_params
245249

246250
--------------------------------------------------------------------------------
@@ -292,10 +296,11 @@ scriptEvaluations =
292296
, seEvaluatedSuccessfully = tableField "evaluated_successfully"
293297
, seExecBudgetCpu = tableField "exec_budget_cpu"
294298
, seExecBudgetMem = tableField "exec_budget_mem"
295-
, seScript = tableField "serialised"
299+
, seScript = tableField "script_serialised"
296300
, seDatum = tableField "datum"
297301
, seRedeemer = tableField "redeemer"
298302
, seScriptContext = tableField "script_context"
303+
, seCostModelKey = tableField "cost_model_key"
299304
, seCostModelParams = tableField "cost_model_param_values"
300305
}
301306

plutus-script-evaluation/lib/Deserialise.hs

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
module Deserialise where
22

3+
import Codec.CBOR.Decoding qualified as CBOR
34
import Codec.CBOR.Read qualified as CBOR
45
import Codec.Extras.SerialiseViaFlat (decodeViaFlatWith, readDeserialiseFailureInfo)
56
import Control.Exception (throwIO)
@@ -10,6 +11,7 @@ import Data.Aeson qualified as Json
1011
import Data.Base64.Types qualified as Base64
1112
import Data.ByteString qualified as BS
1213
import Data.ByteString.Base64 qualified as Base64
14+
import Data.Coerce (coerce)
1315
import Data.Function ((&))
1416
import Data.Some (withSome)
1517
import Data.String.Interpolate (i)
@@ -18,6 +20,7 @@ import Database qualified as DB
1820
import Database.PostgreSQL.Simple (Connection)
1921
import Numeric.Natural (Natural)
2022
import PlutusCore (DefaultUni (..), ValueOf (..))
23+
import PlutusCore.DeBruijn.Internal (FakeNamedDeBruijn (..))
2124
import PlutusCore.Default (noMoreTypeFunctions)
2225
import PlutusCore.Default qualified as U
2326
import PlutusCore.Pretty (pretty, render)
@@ -59,16 +62,20 @@ deserialiseScript
5962
deserialiseScript
6063
(DB.MkSerialisedScriptRecord hash _ledgerLang serialised) = do
6164
let builtinPredicate _fun = Nothing -- Don't check builtins compatibility
65+
decoder
66+
:: CBOR.Decoder
67+
s
68+
(U.Program U.FakeNamedDeBruijn DefaultUni U.DefaultFun ())
6269
decoder = decodeViaFlatWith (U.decodeProgram builtinPredicate)
63-
uplc <-
70+
uplc :: U.Program U.NamedDeBruijn DefaultUni U.DefaultFun () <-
6471
case CBOR.deserialiseFromBytes decoder (BS.fromStrict serialised) of
6572
Left err ->
6673
throwError $ CBORDeserialiseError $ readDeserialiseFailureInfo err
6774
Right (remainder, _uplc)
6875
| remainder /= mempty ->
6976
throwError $ RemainderError remainder
7077
Right (_rest, uplc) ->
71-
pure uplc
78+
pure $ coerce uplc
7279
pure . DB.MkDeserialisedScriptRecord hash . termToJson $ U._progTerm uplc
7380

7481
termToJson :: U.Term U.NamedDeBruijn U.DefaultUni U.DefaultFun () -> Json.Value

plutus-script-evaluation/lib/Evaluate.hs

+86-26
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,14 @@ import Control.Monad.Trans.Except (runExceptT)
99
import Control.Monad.Trans.Writer (WriterT (runWriterT))
1010
import Data.ByteString qualified as BSL
1111
import Data.ByteString.Short qualified as BSS
12+
import Data.Digest.Murmur64 (Hash64)
1213
import Data.Either (isRight)
14+
import Data.Map (Map)
15+
import Data.Map qualified as Map
1316
import Data.Text qualified as Text
1417
import Data.Text.IO qualified as TIO
15-
import Data.Time.Clock (getCurrentTime)
18+
import Data.Time.Clock (NominalDiffTime, diffUTCTime, getCurrentTime, nominalDiffTimeToSeconds)
19+
import Data.Word (Word32)
1620
import Database qualified as Db
1721
import Database.PostgreSQL.Simple qualified as Postgres
1822
import Database.Schema (ScriptEvaluationRecord' (..))
@@ -33,19 +37,20 @@ import PlutusLedgerApi.Common (
3337
import PlutusLedgerApi.V1 qualified as V1
3438
import PlutusLedgerApi.V2 qualified as V2
3539
import PlutusLedgerApi.V3 qualified as V3
40+
import System.Exit (ExitCode (..), exitWith)
3641
import Text.PrettyBy qualified as Pretty
37-
import UnliftIO (IORef, atomicModifyIORef', liftIO, newIORef, readIORef)
42+
import UnliftIO (IORef, MonadIO, atomicModifyIORef', liftIO, newIORef, readIORef, writeIORef)
3843
import UnliftIO.Concurrent (forkFinally, threadDelay)
3944

4045
data ScriptEvaluationInput = MkScriptEvaluationInput
41-
{ seiPlutusLedgerLanguage :: PlutusLedgerLanguage
42-
, seiMajorProtocolVersion :: MajorProtocolVersion
43-
, seiEvaluationContext :: EvaluationContext
46+
{ seiPlutusLedgerLanguage :: !PlutusLedgerLanguage
47+
, seiMajorProtocolVersion :: !MajorProtocolVersion
48+
, seiEvaluationContext :: !EvaluationContext
4449
, seiData :: [Data]
45-
, seiScript :: ScriptForEvaluation
46-
, seiExBudget :: ExBudget
47-
, seiEvaluationSuccess :: Bool
48-
, seiBlock :: BlockNo
50+
, seiScript :: !ScriptForEvaluation
51+
, seiExBudget :: !ExBudget
52+
, seiEvaluationSuccess :: !Bool
53+
, seiBlock :: !BlockNo
4954
}
5055

5156
renderScriptEvaluationInput :: ScriptEvaluationInput -> String
@@ -79,9 +84,10 @@ accumulateScripts
7984
-> (ScriptEvaluationInput -> a -> m a)
8085
-- ^ Accumulation function
8186
-> m a
82-
accumulateScripts conn startBlock initialAccum accumulate =
87+
accumulateScripts conn startBlock initialAccum accumulate = do
88+
evaluationContexts <- newIORef Map.empty
8389
Db.withScriptEvaluationEvents conn startBlock initialAccum \accum record -> do
84-
scriptInput <- inputFromRecord record
90+
scriptInput <- inputFromRecord evaluationContexts record
8591
accumulate scriptInput accum
8692

8793
evaluateScripts
@@ -96,42 +102,96 @@ evaluateScripts
96102
-> m ()
97103
evaluateScripts conn startBlock callback = do
98104
maxThreads <- liftIO getNumCapabilities
99-
threadCounter <- newIORef 0
105+
st <-
106+
newIORef
107+
( 0 -- current number of threads
108+
, 0 -- number of evaluated scripts
109+
, 0 -- average processing time (millis)
110+
, 0 -- average evaluation time (millis)
111+
)
112+
evalContexts <- newIORef Map.empty -- cashed evaluation contexts
100113
Db.withScriptEvaluationEvents conn startBlock () \_unit record -> do
101-
waitForAFreeThread maxThreads threadCounter
102-
atomicModifyIORef' threadCounter \n -> (n + 1, ())
103-
_threadId <- forkFinally (callback =<< inputFromRecord record) \_ ->
104-
atomicModifyIORef' threadCounter \n -> (n - 1, ())
114+
startProcessing <- liftIO getCurrentTime
115+
waitForAFreeThread maxThreads st
116+
atomicModifyIORef' st \(threads, n, a, s) ->
117+
((threads + 1, n, a, s), ())
118+
let work = do
119+
input <- inputFromRecord evalContexts record
120+
startEvaluation <- liftIO getCurrentTime
121+
callback input
122+
end <- liftIO getCurrentTime
123+
pure
124+
( nominalDiffTimeToMillis (end `diffUTCTime` startProcessing)
125+
, nominalDiffTimeToMillis (end `diffUTCTime` startEvaluation)
126+
)
127+
_threadId <- forkFinally work \case
128+
Left err -> liftIO do
129+
putStrLn $ "Failed to evaluate script: " <> show err
130+
exitWith (ExitFailure 1)
131+
Right (!dtp, !dte) -> do
132+
atomicModifyIORef' st \(threads, n, pt, et) ->
133+
let pt' =
134+
if pt == 0
135+
then dtp
136+
else
137+
round @Double @Word32 $
138+
fromIntegral (pt * (n - 1) + dtp) / fromIntegral n
139+
et' =
140+
if et == 0
141+
then dte
142+
else
143+
round @Double @Word32 $
144+
fromIntegral (et * (n - 1) + dte) / fromIntegral n
145+
in ((threads - 1, n + 1, pt', et'), ())
105146
pure ()
106147
where
107-
waitForAFreeThread :: Int -> IORef Int -> m ()
148+
{-
149+
(_, n, pt, et) <- readIORef st
150+
when (n `mod` 100 == 0) $ liftIO do
151+
putStrLn $ "Average time: processing " <> show pt <> "ms, "
152+
<> "evaluation " <> show et <> "ms"
153+
-}
154+
155+
waitForAFreeThread :: Int -> IORef (Int, Word32, Word32, Word32) -> m ()
108156
waitForAFreeThread maxThreads counter = do
109-
threadCount <- readIORef counter
157+
(threadCount, _, _, _) <- readIORef counter
110158
when (threadCount >= maxThreads) do
111159
threadDelay 1_000 -- wait for 1ms
112160
waitForAFreeThread maxThreads counter
113161

162+
nominalDiffTimeToMillis :: NominalDiffTime -> Word32
163+
nominalDiffTimeToMillis dt = round (1000 * nominalDiffTimeToSeconds dt)
164+
114165
inputFromRecord
115-
:: (MonadFail m)
116-
=> Db.ScriptEvaluationRecord
166+
:: (MonadFail m, MonadIO m)
167+
=> IORef (Map Hash64 EvaluationContext)
168+
-> Db.ScriptEvaluationRecord
117169
-> m ScriptEvaluationInput
118-
inputFromRecord MkScriptEvaluationRecord'{..} = do
170+
inputFromRecord evalCtxRef MkScriptEvaluationRecord'{..} = do
119171
let mkEvalCtx f =
120172
runExceptT (runWriterT f) >>= \case
121173
Left e -> fail $ "Failed to create evaluation context: " <> show e
122174
Right (ctx, _warnings) -> pure ctx
123-
seiEvaluationContext <-
124-
mkEvalCtx case seLedgerLanguage of
125-
PlutusV1 -> V1.mkEvaluationContext seCostModelParams
126-
PlutusV2 -> V2.mkEvaluationContext seCostModelParams
127-
PlutusV3 -> V3.mkEvaluationContext seCostModelParams
175+
seiEvaluationContext <- do
176+
keyedEvalCtxs <- liftIO $ readIORef evalCtxRef
177+
case Map.lookup seCostModelKey keyedEvalCtxs of
178+
Just ctx -> pure ctx
179+
Nothing -> do
180+
ctx <- mkEvalCtx case seLedgerLanguage of
181+
PlutusV1 -> V1.mkEvaluationContext seCostModelParams
182+
PlutusV2 -> V2.mkEvaluationContext seCostModelParams
183+
PlutusV3 -> V3.mkEvaluationContext seCostModelParams
184+
let keyedEvalCtxs' = Map.insert seCostModelKey ctx keyedEvalCtxs
185+
liftIO $ writeIORef evalCtxRef keyedEvalCtxs'
186+
pure ctx
128187
seiScript <-
129188
case deserialiseScript
130189
seLedgerLanguage
131190
seMajorProtocolVersion
132191
(BSS.toShort seScript) of
133192
Left err -> fail $ "Failed to deserialise script: " <> show err
134193
Right script -> pure script
194+
135195
pure
136196
MkScriptEvaluationInput
137197
{ seiPlutusLedgerLanguage = seLedgerLanguage

0 commit comments

Comments
 (0)