Skip to content

Commit 6a2e6f2

Browse files
michaelpjbrainrake
authored andcommitted
PLT-901: Implement some basic benchmarks for script context usage (IntersectMBO#4941)
* PLT-901: Implement some basic benchmarks for script context usage Two cases: 1. Decode the script context and do some comparable work 2. Decode the script context then ignore it and do something cheap Both run with a couple of different sizes of script context. This should show some significant changes with the sums and products work. * Comment
1 parent 0756f60 commit 6a2e6f2

File tree

8 files changed

+186
-0
lines changed

8 files changed

+186
-0
lines changed

.stylish-haskell.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ language_extensions:
2525
- GeneralizedNewtypeDeriving
2626
- MultiParamTypeClasses
2727
- NamedFieldPuns
28+
- ImportQualifiedPost
2829
- PackageImports
2930
- QuasiQuotes
3031
- ScopedTypeVariables

plutus-benchmark/plutus-benchmark.cabal

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,3 +357,41 @@ executable ed25519-throughput
357357
, plutus-core ^>=1.1
358358
, plutus-tx ^>=1.1
359359
, plutus-tx-plugin ^>=1.1
360+
361+
---------------- script contexts ----------------
362+
363+
library script-contexts-internal
364+
import: lang
365+
366+
if impl(ghc <9.0)
367+
buildable: False
368+
369+
-- Something weird causes this to sometimes report
370+
-- the plugin package as unused...
371+
ghc-options: -Wno-unused-packages
372+
hs-source-dirs: script-contexts/src
373+
exposed-modules: PlutusBenchmark.ScriptContexts
374+
build-depends:
375+
, base >=4.9 && <5
376+
, plutus-ledger-api ^>=1.1
377+
, plutus-tx ^>=1.1
378+
, plutus-tx-plugin ^>=1.1
379+
380+
test-suite plutus-benchmark-script-contexts-tests
381+
import: lang
382+
383+
if impl(ghc <9.0)
384+
buildable: False
385+
386+
type: exitcode-stdio-1.0
387+
main-is: Spec.hs
388+
hs-source-dirs: script-contexts/test
389+
other-modules:
390+
build-depends:
391+
, base >=4.9 && <5
392+
, plutus-benchmark-common
393+
, plutus-core:{plutus-core, plutus-core-testlib} ^>=1.1
394+
, plutus-tx:{plutus-tx, plutus-tx-testlib} ^>=1.1
395+
, script-contexts-internal
396+
, tasty
397+
, tasty-hunit
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
{-# LANGUAGE BangPatterns #-}
2+
{-# LANGUAGE DataKinds #-}
3+
{-# LANGUAGE OverloadedStrings #-}
4+
{-# LANGUAGE TemplateHaskell #-}
5+
6+
module PlutusBenchmark.ScriptContexts where
7+
8+
import PlutusLedgerApi.V1
9+
import PlutusLedgerApi.V1.Address
10+
import PlutusLedgerApi.V1.Value
11+
import PlutusTx qualified as PlutusTx
12+
import PlutusTx.Builtins qualified as PlutusTx
13+
import PlutusTx.Prelude qualified as PlutusTx
14+
15+
-- | A very crude deterministic generator for 'ScriptContext's with size
16+
-- approximately proportional to the input integer.
17+
mkScriptContext :: Int -> ScriptContext
18+
mkScriptContext i = ScriptContext (mkTxInfo i) (Spending (TxOutRef (TxId "") 0))
19+
20+
mkTxInfo :: Int -> TxInfo
21+
mkTxInfo i = TxInfo {
22+
txInfoInputs=mempty,
23+
txInfoOutputs=fmap mkTxOut [1..i],
24+
txInfoFee=mempty,
25+
txInfoMint=mempty,
26+
txInfoDCert=mempty,
27+
txInfoWdrl=mempty,
28+
txInfoValidRange=always,
29+
txInfoSignatories=mempty,
30+
txInfoData=mempty,
31+
txInfoId=TxId ""
32+
}
33+
34+
mkTxOut :: Int -> TxOut
35+
mkTxOut i = TxOut {
36+
txOutAddress=pubKeyHashAddress (PubKeyHash ""),
37+
txOutValue=mkValue i,
38+
txOutDatumHash=Nothing
39+
}
40+
41+
mkValue :: Int -> Value
42+
mkValue i = assetClassValue (assetClass adaSymbol adaToken) (fromIntegral i)
43+
44+
-- This example decodes the script context (which is O(size-of-context) work), and then also
45+
-- does some work that's roughly proportional to the size of the script context (counting the
46+
-- outputs). This should be a somewhat realistic example where a reasonable chunk of work is
47+
-- done in addition to decoding.
48+
{-# INLINABLE checkScriptContext1 #-}
49+
checkScriptContext1 :: PlutusTx.BuiltinData -> ()
50+
checkScriptContext1 d =
51+
-- Bang pattern to ensure this is forced, probably not necesssary
52+
-- since we do use it later
53+
let !sc = PlutusTx.unsafeFromBuiltinData d
54+
(ScriptContext txi _) = sc
55+
in
56+
if PlutusTx.length (txInfoOutputs txi) `PlutusTx.modInteger` 2 PlutusTx.== 0
57+
then ()
58+
else PlutusTx.traceError "Odd number of outputs"
59+
60+
mkCheckScriptContext1Code :: ScriptContext -> PlutusTx.CompiledCode ()
61+
mkCheckScriptContext1Code sc =
62+
let d = PlutusTx.toBuiltinData sc
63+
in $$(PlutusTx.compile [|| checkScriptContext1 ||]) `PlutusTx.applyCode` PlutusTx.liftCode d
64+
65+
-- This example aims to *force* the decoding of the script context and then ignore it entirely.
66+
-- This corresponds to the unfortunate case where the decoding "wrapper" around a script forces
67+
-- all the decoding work to be done even if it isn't used.
68+
{-# INLINABLE checkScriptContext2 #-}
69+
checkScriptContext2 :: PlutusTx.BuiltinData -> ()
70+
checkScriptContext2 d =
71+
let (sc :: ScriptContext) = PlutusTx.unsafeFromBuiltinData d
72+
-- Just using a bang pattern was not enough to stop GHC from getting
73+
-- rid of the dead binding before we even hit the plugin, this works
74+
-- for now!
75+
in case sc of
76+
!_ ->
77+
if 48*9900 PlutusTx.== (475200 :: Integer)
78+
then ()
79+
else PlutusTx.traceError "Got my sums wrong"
80+
81+
mkCheckScriptContext2Code :: ScriptContext -> PlutusTx.CompiledCode ()
82+
mkCheckScriptContext2Code sc =
83+
let d = PlutusTx.toBuiltinData sc
84+
in $$(PlutusTx.compile [|| checkScriptContext2 ||]) `PlutusTx.applyCode` PlutusTx.liftCode d
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
module Main (main) where
2+
3+
import Test.Tasty
4+
import Test.Tasty.Extras (TestNested, runTestNestedIn)
5+
import Test.Tasty.HUnit
6+
7+
import PlutusBenchmark.Common (compiledCodeToTerm, runTermCek)
8+
9+
import PlutusBenchmark.ScriptContexts
10+
11+
import PlutusCore.Evaluation.Result
12+
import PlutusTx.Test qualified as Tx
13+
14+
runTestNested :: TestNested -> TestTree
15+
runTestNested = runTestNestedIn ["script-contexts", "test"]
16+
17+
testCheckSc1 :: TestTree
18+
testCheckSc1 = testGroup "checkScriptContext1"
19+
[ testCase "succeed on 4" $ assertBool "evaluation failed" $ isEvaluationSuccess $
20+
runTermCek $ compiledCodeToTerm $ mkCheckScriptContext1Code (mkScriptContext 4)
21+
, testCase "fails on 5" $ assertBool "evaluation succeeded" $ isEvaluationFailure $
22+
runTermCek $ compiledCodeToTerm $ mkCheckScriptContext1Code (mkScriptContext 5)
23+
, Tx.fitsInto "checkScriptContext1 (size)" (mkCheckScriptContext1Code (mkScriptContext 1)) 2500
24+
, runTestNested $ Tx.goldenBudget "checkScriptContext1-4" $
25+
mkCheckScriptContext1Code (mkScriptContext 4)
26+
, runTestNested $ Tx.goldenBudget "checkScriptContext1-20" $
27+
mkCheckScriptContext1Code (mkScriptContext 20)
28+
]
29+
30+
testCheckSc2 :: TestTree
31+
testCheckSc2 = testGroup "checkScriptContext2"
32+
[ testCase "succeed on 4" $ assertBool "evaluation failed" $ isEvaluationSuccess $
33+
runTermCek $ compiledCodeToTerm $ mkCheckScriptContext2Code (mkScriptContext 4)
34+
, testCase "succeed on 5" $ assertBool "evaluation failed" $ isEvaluationSuccess $
35+
runTermCek $ compiledCodeToTerm $ mkCheckScriptContext2Code (mkScriptContext 5)
36+
, Tx.fitsInto "checkScriptContext2 (size)" (mkCheckScriptContext2Code (mkScriptContext 1)) 2400
37+
, runTestNested $ Tx.goldenBudget "checkScriptContext2-4" $
38+
mkCheckScriptContext2Code (mkScriptContext 4)
39+
, runTestNested $ Tx.goldenBudget "checkScriptContext2-20" $
40+
mkCheckScriptContext2Code (mkScriptContext 20)
41+
]
42+
43+
allTests :: TestTree
44+
allTests =
45+
testGroup "plutus-benchmark script-contexts tests"
46+
[ testCheckSc1
47+
, testCheckSc2
48+
]
49+
50+
main :: IO ()
51+
main = defaultMain allTests
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
({ cpu: 492446997
2+
| mem: 1613269
3+
})
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
({ cpu: 141826245
2+
| mem: 466293
3+
})
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
({ cpu: 438942997
2+
| mem: 1400528
3+
})
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
({ cpu: 127689877
2+
| mem: 410384
3+
})

0 commit comments

Comments
 (0)