Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,17 @@ profilesPlutuscall =
. P.analysisStandard
. P.desc "Small dataset, honest 15 epochs duration"

loop = plutusCall & V.plutusTypeLoop . V.plutusDoubleSaturation . P.analysisSizeModerate . P.analysisEpoch3Plus
ecdsa = plutusCall & V.plutusTypeECDSA . V.plutusDoubleSaturation . P.analysisSizeModerate . P.analysisEpoch3Plus
schnorr = plutusCall & V.plutusTypeSchnorr . V.plutusDoubleSaturation . P.analysisSizeModerate . P.analysisEpoch3Plus
loop = plutusCall & V.plutusTypeLoop . V.plutusDoubleSaturation . P.analysisSizeModerate . P.analysisEpoch3Plus
ecdsa = plutusCall & V.plutusTypeECDSA . V.plutusDoubleSaturation . P.analysisSizeModerate . P.analysisEpoch3Plus
schnorr = plutusCall & V.plutusTypeSchnorr . V.plutusDoubleSaturation . P.analysisSizeModerate . P.analysisEpoch3Plus
schnorrV3 = plutusCall & V.plutusTypeSchnorrV3 . V.plutusDoubleSaturation . P.analysisSizeSmall . P.analysisEpoch3Plus
mscalmul = plutusCall & V.plutusTypeMultScalarMultG1 . V.plutusDoubleSaturation . P.analysisSizeSmall . P.analysisEpoch3Plus
ripemdVolt4tx = plutusCall & V.plutusTypeRIPEMD_4tx . V.plutusDoubleSaturation . P.analysisSizeSmall . P.analysisEpoch3Plus
expmod = plutusCall & V.plutusTypeExpMod . V.plutusDoubleSaturation . P.analysisSizeSmall . P.analysisEpoch3Plus

loopVolt = plutusCall & V.plutusTypeLoop . V.plutusDoublePlusSaturation . P.analysisSizeSmall
blstVolt = plutusCall & V.plutusTypeBLST . V.plutusDoublePlusSaturation . P.analysisSizeModerate2
ripemdVolt = plutusCall & V.plutusTypeRIPEMD . V.plutusDoublePlusSaturation . P.analysisSizeSmall
loopVolt = plutusCall & V.plutusTypeLoop . V.plutusDoublePlusSaturation . P.analysisSizeSmall
blstVolt = plutusCall & V.plutusTypeBLST . V.plutusDoublePlusSaturation . P.analysisSizeModerate2
ripemdVolt = plutusCall & V.plutusTypeRIPEMD . V.plutusDoublePlusSaturation . P.analysisSizeSmall

postPlomin = V.genesisVariantVoltaire
in [
Expand All @@ -65,5 +69,9 @@ profilesPlutuscall =
, blstVolt & P.name "plutuscall-volt-blst" . postPlomin
, ripemdVolt & P.name "plutuscall-volt-ripemd" . postPlomin

, ripemdVolt & P.name "plutuscall-voltv11-ripemd" . postPlomin . P.v11Preview
-- PlutusV3 benchmarks, targeting PV11
, ripemdVolt4tx & P.name "plutuscall-voltv11-ripemd" . postPlomin . P.v11Preview
, schnorrV3 & P.name "plutuscall-voltv11-schnorrv3" . postPlomin . P.v11Preview
, mscalmul & P.name "plutuscall-voltv11-mscalmul" . postPlomin . P.v11Preview
, expmod & P.name "plutuscall-voltv11-expmod" . postPlomin . P.v11Preview
]
Original file line number Diff line number Diff line change
Expand Up @@ -894,15 +894,19 @@ analysisFiltersAppend str = analysis
else a {Types.filters = Types.filters a ++ [str]}
)

-- for 88k blocks: from 2.6k to 8.8k
analysisSizeSmall :: HasCallStack => Types.Profile -> Types.Profile
analysisSizeSmall = analysisFiltersAppend "size-small"

-- for 88k blocks: from 7k to 9.8k
analysisSizeModerate :: HasCallStack => Types.Profile -> Types.Profile
analysisSizeModerate = analysisFiltersAppend "size-moderate"

-- for 88k blocks: from 6.7k to 9.2k
analysisSizeModerate2 :: HasCallStack => Types.Profile -> Types.Profile
analysisSizeModerate2 = analysisFiltersAppend "size-moderate-2"

-- for 88k blocks: larger than 79.2k
analysisSizeFull :: HasCallStack => Types.Profile -> Types.Profile
analysisSizeFull = analysisFiltersAppend "size-full"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ module Cardano.Benchmarking.Profile.Vocabulary (
, plutusSaturation, plutusDoubleSaturation, plutusDoublePlusSaturation

, plutusTypeLoop, plutusTypeLoopV3, plutusTypeLoop2024, plutusTypeECDSA, plutusTypeSchnorr
, plutusTypeBLST, plutusTypeRIPEMD
, plutusTypeBLST, plutusTypeRIPEMD, plutusTypeRIPEMD_4tx, plutusTypeMultScalarMultG1
, plutusTypeSchnorrV3
, plutusTypeExpMod
) where

--------------------------------------------------------------------------------
Expand Down Expand Up @@ -250,3 +252,66 @@ plutusTypeRIPEMD =
, KeyMap.fromList [("bytes", Aeson.String "5a56da88e6fd8419181dec4d3dd6997bab953d2f")]
]
. P.txFee 940000

-- the bytes content is arbitrary, but should be of the same size as RIPEMD-160 output, i.e. 160 bits
plutusTypeRIPEMD_4tx :: Types.Profile -> Types.Profile
plutusTypeRIPEMD_4tx =
P.plutusType "LimitTxPerBlock_4" . P.plutusScript "Ripemd160"
. P.redeemerFields [
KeyMap.fromList [("int", Aeson.Number 1000000.0)]
, KeyMap.fromList [("bytes", Aeson.String "5a56da88e6fd8419181dec4d3dd6997bab953d2f")]
]
. P.txFee 915500

-- there must always be 1 scalar value less than there are points.
-- for the rationale about the redeemer structure, see note in
-- the script's source: bench/plutus-scripts-bench/src/Cardano/Benchmarking/PlutusScripts/MultiScalarMulG1.hs
plutusTypeMultScalarMultG1 :: Types.Profile -> Types.Profile
plutusTypeMultScalarMultG1 =
P.plutusType "LimitTxPerBlock_4" . P.plutusScript "MultiScalarMulG1"
. P.redeemerFields [
KeyMap.fromList [("int", Aeson.Number 1000000.0)]
, KeyMap.fromList [("list", Aeson.Array $ Vector.fromList [
Aeson.Object $ KeyMap.fromList [("int", Aeson.Number 871345174532.0)]
, Aeson.Object $ KeyMap.fromList [("int", Aeson.Number 987654321.0)]
])
]
, KeyMap.fromList [("list", Aeson.Array $ Vector.fromList [
Aeson.Object $ KeyMap.fromList [("bytes", Aeson.String "ab5d1d67b495361c3297c721cf3c9dc510fc5055bdf92fefc1e67d91a00a765520150428eb4c2fb01902d41d5ca62d85")]
, Aeson.Object $ KeyMap.fromList [("bytes", Aeson.String "88c7e388ee58f1db9a24d7098b01d13634298bebf2d159254975bd450cb0d287fcc622eb71edde8b469a8513551baf1f")]
, Aeson.Object $ KeyMap.fromList [("bytes", Aeson.String "95e4262f370607b2765312297461a04a3fb1fbb83e998a8e7d0f237017df86c5fb01f85101177c3dd41586df05b038cb")]
])
]
]
. P.txFee 572000

plutusTypeSchnorrV3 :: Types.Profile -> Types.Profile
plutusTypeSchnorrV3 =
P.plutusType "LimitTxPerBlock_4" . P.plutusScript "SchnorrSecp256k1LoopV3"
. P.redeemerFields [
KeyMap.fromList [("int", Aeson.Number 1000000.0)]
, KeyMap.fromList [("bytes", Aeson.String "599de3e582e2a3779208a210dfeae8f330b9af00a47a7fb22e9bb8ef596f301b")]
, KeyMap.fromList [("bytes", Aeson.String "30303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030")]
, KeyMap.fromList [("bytes", Aeson.String "5a56da88e6fd8419181dec4d3dd6997bab953d2fc71ab65e23cfc9e7e3d1a310613454a60f6703819a39fdac2a410a094442afd1fc083354443e8d8bb4461a9b")]
]
. P.txFee 586000

-- redeemer is a flat `(n, a, m)`: `n` (the loop counter) must stay the first
-- numeric value in the encoding, cf. bench/plutus-scripts-bench/src/Cardano/Benchmarking/PlutusScripts/ExpModInteger.hs
-- `n`'s placeholder here must NOT be 1000000: the mandatory `scriptDataModifyNumber
-- (const 1_000_000)` reset step (Cardano.TxGenerator.PlutusContext) detects "already
-- found a number to change" by comparing before/after values, so if this placeholder
-- already equals 1000000 the reset is a false-negative no-op and falls through to
-- corrupt `a` instead. Keep it at 0.
-- txFee is rounded up from the unscaled "txperblock_4" baseline fee (1262499)
-- reported by `calibrate-script run ExpModInteger txperblock_4`,
-- cf. bench/tx-generator/summaries_ExpModInteger.json
plutusTypeExpMod :: Types.Profile -> Types.Profile
plutusTypeExpMod =
P.plutusType "LimitTxPerBlock_4" . P.plutusScript "ExpModInteger"
. P.redeemerFields [
KeyMap.fromList [("int", Aeson.Number 0.0)]
, KeyMap.fromList [("int", Aeson.Number 3.0)]
, KeyMap.fromList [("int", Aeson.Number 2305843009213693951.0)]
]
. P.txFee 1275000
4 changes: 3 additions & 1 deletion bench/plutus-scripts-bench/plutus-scripts-bench.cabal
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cabal-version: 3.0
name: plutus-scripts-bench
version: 1.0.4.3
version: 1.0.5.0
synopsis: Plutus scripts used for benchmarking
description: Plutus scripts used for benchmarking.
category: Cardano,
Expand Down Expand Up @@ -72,9 +72,11 @@ library
Cardano.Benchmarking.PlutusScripts.CustomCallV3
Cardano.Benchmarking.PlutusScripts.EcdsaSecp256k1Loop
Cardano.Benchmarking.PlutusScripts.EcdsaSecp256k1LoopV3
Cardano.Benchmarking.PlutusScripts.ExpModInteger
Cardano.Benchmarking.PlutusScripts.HashOntoG2AndAdd
Cardano.Benchmarking.PlutusScripts.Loop2024
Cardano.Benchmarking.PlutusScripts.LoopV3
Cardano.Benchmarking.PlutusScripts.MultiScalarMulG1
Cardano.Benchmarking.PlutusScripts.Ripemd160
Cardano.Benchmarking.PlutusScripts.SchnorrSecp256k1Loop
Cardano.Benchmarking.PlutusScripts.SchnorrSecp256k1LoopV3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ import qualified Cardano.Benchmarking.PlutusScripts.CustomCall as CustomCall
import qualified Cardano.Benchmarking.PlutusScripts.CustomCallV3 as CustomCallV3
import qualified Cardano.Benchmarking.PlutusScripts.EcdsaSecp256k1Loop as ECDSA
import qualified Cardano.Benchmarking.PlutusScripts.EcdsaSecp256k1LoopV3 as ECDSAV3
import qualified Cardano.Benchmarking.PlutusScripts.ExpModInteger as ExpModInteger
import qualified Cardano.Benchmarking.PlutusScripts.HashOntoG2AndAdd as HashG2Add
import qualified Cardano.Benchmarking.PlutusScripts.Loop2024 as Loop2024
import qualified Cardano.Benchmarking.PlutusScripts.LoopV3 as LoopV3
import qualified Cardano.Benchmarking.PlutusScripts.MultiScalarMulG1 as MultiScalarMulG1
import qualified Cardano.Benchmarking.PlutusScripts.Ripemd160 as Ripemd160
import qualified Cardano.Benchmarking.PlutusScripts.SchnorrSecp256k1Loop as Schnorr
import qualified Cardano.Benchmarking.PlutusScripts.SchnorrSecp256k1LoopV3 as SchnorrV3
Expand All @@ -42,9 +44,11 @@ getAllScripts =
, CustomCallV3.script
, ECDSA.script
, ECDSAV3.script
, ExpModInteger.script
, HashG2Add.script
, Loop2024.script
, LoopV3.script
, MultiScalarMulG1.script
, Ripemd160.script
, Schnorr.script
, SchnorrV3.script
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE TemplateHaskell #-}

-- | Fixed-iteration benchmark loop exercising the 'expModInteger' builtin,
-- ported from plutus-scripts-e2e's PlutusScripts.Batch6.ExpModInteger.Common.
module Cardano.Benchmarking.PlutusScripts.ExpModInteger
( script
) where

import Cardano.Api (PlutusScriptVersion (PlutusScriptV3))
import Cardano.Benchmarking.ScriptAPI (PlutusBenchScript, mkPlutusBenchScript)
import Language.Haskell.TH.Syntax (Exp (LitE), Lit (StringL), Loc (loc_module), qLocation)
import PlutusLedgerApi.Common (serialiseCompiledCode)
import qualified PlutusLedgerApi.V3 as PlutusV3
import qualified PlutusTx
import qualified PlutusTx.Builtins as BI (expModInteger)
import qualified PlutusTx.Builtins.Internal as BI (BuiltinList, head, snd, tail,
unitval, unsafeDataAsConstr)
import PlutusTx.Prelude as P hiding (Semigroup (..), (.), (<$>))
import Prelude as Haskell ((.), (<$>))


-- while n > 1000000, force-evaluate expModInteger a e m (discarding the
-- result) and loop with n := n - 1, e := e + 1
{-# INLINEABLE mkSimpleExpModIntegerPolicyBench #-}
mkSimpleExpModIntegerPolicyBench :: P.BuiltinData -> P.BuiltinUnit
mkSimpleExpModIntegerPolicyBench ctx =
if red_n P.< 1000000 -- large number ensures same bitsize for all counter values
then P.traceError "redeemer is < 1000000"
else go e0 red_n
where
-- lazily decode script context up to redeemer, which is less expensive and results in much smaller tx size
constrArgs :: P.BuiltinData -> BI.BuiltinList P.BuiltinData
constrArgs = BI.snd . BI.unsafeDataAsConstr

redeemerFollowedByScriptInfo :: BI.BuiltinList P.BuiltinData
redeemerFollowedByScriptInfo = BI.tail (constrArgs ctx)

redeemer :: P.BuiltinData
redeemer = BI.head redeemerFollowedByScriptInfo

-- `n` must come first in the redeemer's (flat) Data encoding: tx-generator's
-- auto-budget calibration (Cardano.TxGenerator.PlutusContext.scriptDataModifyNumber)
-- bumps whichever ScriptDataNumber it encounters first in a depth-first walk.
red_n :: Integer
red_a :: Integer
red_m :: Integer
(red_n, red_a, red_m) = PlutusV3.unsafeFromBuiltinData redeemer

-- e₀ = m - 2: a realistic, non-arbitrary exponent size, tracking `m`'s
-- own bit-length (not tied to `m` being prime -- there's no correctness
-- check against an expected result here, just a representative-sized
-- exponent to feed `expModInteger`).
--
-- `e` only ever increments by 1 per iteration (never doubles), so its
-- bit-length -- and thus `expModInteger`'s per-call cost -- stays essentially
-- constant across the whole benchmark run, however many iterations it runs for.
-- This also keeps every iteration's arguments genuinely distinct (dependent on
-- the loop counter), so no compiler optimisation (e.g. full laziness) can
-- legally hoist/common up the `expModInteger` call across iterations.
e0 :: Integer
e0 = red_m - 2

go :: Integer -> Integer -> P.BuiltinUnit
go e n =
if n P.== 1000000
then BI.unitval
else let !_ = BI.expModInteger red_a e red_m in go (e P.+ 1) (P.pred n)

script :: PlutusBenchScript
script = mkPlutusBenchScript
$(LitE . StringL . loc_module <$> qLocation)
PlutusScriptV3
(serialiseCompiledCode $$(PlutusTx.compile [|| mkSimpleExpModIntegerPolicyBench ||]))
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE TemplateHaskell #-}

module Cardano.Benchmarking.PlutusScripts.MultiScalarMulG1 (script) where

import Cardano.Api (PlutusScriptVersion (PlutusScriptV3))
import Cardano.Benchmarking.ScriptAPI (PlutusBenchScript, mkPlutusBenchScript)
import Language.Haskell.TH.Syntax (Exp (LitE), Lit (StringL), Loc (loc_module), qLocation)
import PlutusLedgerApi.Common (serialiseCompiledCode)
import qualified PlutusLedgerApi.V3 as PlutusV3
import qualified PlutusTx (compile)
import qualified PlutusTx.Builtins.Internal as BI (BuiltinList, head, snd, tail, unitval,
unsafeDataAsConstr)
import PlutusTx.Builtins as BI (bls12_381_G1_multiScalarMul)
import PlutusTx.Prelude as Tx hiding (Semigroup (..), (.), (<$>))
import Prelude as Haskell ((.), (<$>))


script :: PlutusBenchScript
script = mkPlutusBenchScript
$(LitE . StringL . loc_module <$> qLocation)
PlutusScriptV3
(serialiseCompiledCode $$(PlutusTx.compile [|| mkValidator ||]))

{-# INLINABLE mkValidator #-}
mkValidator :: BuiltinData -> BuiltinUnit
mkValidator arg =
if red_n < 1000000 -- large number ensures same bitsize for all counter values
then traceError "redeemer is < 1000000"
else loop (fmap Tx.bls12_381_G1_uncompress red_bss) red_is red_n
where
-- lazily decode script context up to redeemer, which is less expensive and results in much smaller tx size
constrArgs :: BuiltinData -> BI.BuiltinList BuiltinData
constrArgs = BI.snd . BI.unsafeDataAsConstr

redeemerFollowedByScriptInfo :: BI.BuiltinList BuiltinData
redeemerFollowedByScriptInfo = BI.tail (constrArgs arg)

redeemer :: BuiltinData
redeemer = BI.head redeemerFollowedByScriptInfo

red_n :: Integer
red_is :: [Integer]
red_bss :: [BuiltinByteString]
(red_n, red_is, red_bss) = PlutusV3.unsafeFromBuiltinData redeemer

-- see Note[1]
loop points scalars n
| n == 1000000 = BI.unitval
| otherwise = let !_ = BI.bls12_381_G1_multiScalarMul (n : scalars) points in loop points scalars (pred n)

{-
Note[1]:

The benchmarking loop's counter will always be used as a nonce, prepended to the list of scalars.
Hence, make sure that in the redeemer args,
>> THE LIST OF SCALARS IS ALWAYS 1 ELEMENT SHORTER THAN THE LIST OF POINTS <<

== Reason for Nonce-as-Head ('n : scalars'):
1. Defeats Pippenger Bucket-Caching: Mutating a single scalar
head element breaks the windowed bit-partitioning configuration. This forces
the 'blst' library to perform full, un-cached linear combination logic from
scratch rather than reusing pre-computed bucket structures.
2. Minmize execution units to achieve 1.: Prepending a head nonce element
guarantees a predictable O(1) overhead, focusing execution cost purely on the underlying
curve arithmetic. Also, this guarantees a stable memory footprint.
-}
22 changes: 11 additions & 11 deletions bench/tx-generator/app/calibrate-script.hs
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,10 @@ runScaling ::
-> IO [(PlutusBudgetSummary, ScriptRedeemer)]
runScaling script budgetType basePParams baseline baseBudget (scaleSanitize -> scalesArg)
| null scalesArg = go1 scaleBaseLine scaleAutoFactors
| otherwise = mapM go0 scalesArg
| otherwise = catMaybes <$> mapM go0 scalesArg
where
-- specific scaling requested
go0 :: Scale -> IO (PlutusBudgetSummary, ScriptRedeemer)
go0 :: Scale -> IO (Maybe (PlutusBudgetSummary, ScriptRedeemer))
go0 scale =
let
fields@[txm, txs, bm, bs] = scaleFields scale
Expand All @@ -218,22 +218,22 @@ runScaling script budgetType basePParams baseline baseBudget (scaleSanitize -> s
budget = withHint (maximum fields) baseBudget
in do
putStrLn $ "--> run: " ++ show scope
evaluate $
summaryAndRedeermerOrDie scope $
plutusAutoScaleBlockfit pparams (scriptNameExt, scope) script budget strategy 1
summaryAndRedeermerSafe scope $
plutusAutoScaleBlockfit pparams (scriptNameExt, scope) script budget strategy 1

-- auto-scale until conditions are met (last list entry), but include intermediate results
go1 :: Scale -> [Double] -> IO [(PlutusBudgetSummary, ScriptRedeemer)]
go1 _ [] = pure []
go1 scale_ factors@(factor:fs) =
case bumpLimit strategy budgetType factor scale_ of
Nothing -> go1 scaleBaseLine fs -- no further bump possible: give up and auto-scale for next factor
Just scale -> do
run@(summary, _) <- go0 scale
(run :) <$>
if all (\cond -> cond summary baseline) happilyCalibrated
then go1 scaleBaseLine fs -- auto-scale next factor
else go1 scale factors -- apply next bump to current scaling, re-run
Just scale -> go0 scale >>= \case
Nothing -> pure [] -- if there was an error, skip all remaining scaling runs
Just run@(summary, _) ->
(run :) <$>
if all (\cond -> cond summary baseline) happilyCalibrated
then go1 scaleBaseLine fs -- auto-scale next factor
else go1 scale factors -- apply next bump to current scaling, re-run

-- conditions for a succesful calibration: same limiting factors, same txn count per block
-- NEXT RELEASE: does that need to be strategy dependent with TargetBlockExpenditure?
Expand Down
14 changes: 14 additions & 0 deletions bench/tx-generator/data/ExpModInteger.redeemer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"constructor": 0,
"fields": [
{
"int": 0
},
{
"int": 3
},
{
"int": 2305843009213693951
}
]
}
Loading
Loading