Skip to Content
Scalus Club is now open! Join us to get an early access to new features 🎉

Measuring Performance

Always measure before and after optimization.

Measuring script execution directly

For quick comparisons of script variants, you can evaluate the UPLC directly and get CPU steps, memory, and flat-encoded size:

import scalus.*, scalus.compiler.{compile, Options} import scalus.uplc.*, scalus.uplc.eval.* import scalus.cardano.ledger.{ExUnitPrices, ExUnits, NonNegativeInterval} given Options = Options.release given PlutusVM = PlutusVM.makePlutusV3VM() val sir = compile { /* your validator */ ??? } val program = sir.toUplcOptimized().plutusV3 // Apply arguments and evaluate val applied = program // $ datum $ redeemer $ ctxData val result = applied.deBruijnedProgram.evaluateDebug result match case Result.Success(_, budget, _, _) => val flatSize = applied.flatEncoded.length println(s"Flat size: $flatSize bytes") println(s"CPU steps: ${budget.steps}") println(s"Memory: ${budget.memory}") // Compute execution fee with mainnet prices val exPrices = ExUnitPrices( priceMemory = NonNegativeInterval(0.0577, precision = 15), priceSteps = NonNegativeInterval(0.0000721, precision = 15) ) val execFee = ExUnits(budget.memory, budget.steps).fee(exPrices) println(s"Exec fee: ${execFee.value} lovelace") // Approximate total: exec fee + size fee (44 lovelace/byte) println(s"Size fee: ${flatSize * 44} lovelace") case Result.Failure(err, _, _, logs) => println(s"Failed: ${err.getMessage}")

However, measuring script execution alone can be misleading. The actual transaction fee includes a size component (44 lovelace per byte of transaction), so a script that saves CPU but grows in size may cost more overall. If you have a working smart contract, the most accurate way to measure is to build a complete transaction via the Emulator and check the final fee.

Measuring via the Emulator

Build a real transaction with TxBuilder and the Emulator, then inspect the fee and execution units:

import scalus.cardano.ledger.* import scalus.cardano.node.Emulator import scalus.cardano.txbuilder.TxBuilder import scalus.uplc.PlutusV3 import scalus.compiler.Options import scalus.utils.await given CardanoInfo = CardanoInfo.mainnet given Options = Options.release val compiled = PlutusV3.compile(MyValidator.validate) val emulator = Emulator(initialUtxos) // Build and complete the transaction val tx = TxBuilder(summon[CardanoInfo]) .spend(scriptUtxo, redeemer, compiled) .payTo(recipientAddress, outputValue) .complete(emulator, changeAddress) .await() .sign(signer) .transaction // The transaction fee includes both size and execution costs val fee: Coin = tx.body.value.fee println(s"Transaction fee: ${fee.value} lovelace") // Inspect execution units per redeemer val redeemers = tx.witnessSet.redeemers.toSeq.flatMap(_.value.toSeq) redeemers.foreach { r => println(s" ${r.tag}: CPU=${r.exUnits.steps}, mem=${r.exUnits.memory}") }

See the Emulator page for full setup details.

What’s Next?

Last updated on