Plutus VM Benchmark

Cross-language UPLC virtual-machine comparison – AMD EPYC-Milan Processor · 16 cores · 61Gi · 3 parallel lanes · 2026-10-23 · harness 0529b3ac1

Contribute

This benchmark compares the speed of Plutus virtual machines (UPLC evaluators), the programs that run Cardano smart contracts. Each VM decodes and evaluates the same 89 Plutus scripts. A correctness gate checks every result and its execution units (ExUnits) against plutus-core, the Haskell CEK machine of the Cardano node. This run of 2026-10-23 has 9 evaluators, written in Go, Scala, Rust, Haskell, TypeScript and Scala.js. The method and the source are open.

Ranked on
decode + eval
Each timed iteration flat-decodes the script and evaluates it, as a node does with a script it has not seen. Eval only (decoded before timing) is shown beside it. A VM that compiles each script before timing has only an eval-only time, so it is not ranked.
Scripts
scripts
Correctness gate
This run

Ranking decode + eval geometric mean, per execution model

Show overall cross-category ranking
Combined leaderboard with the penalty rule (missing/failed scripts get the slowest competitor's time). Useful for a single-number view, but cross-category numbers are not directly comparable. See Execution Model below.

Summary table all VMs, geometric mean over 89 scripts

#VMLanguage / runtimeDecode + evalEval onlyvs fastestCheck
1Plutigo CEKGo283.97 µs182.63 µs1.00×pass
2Scalus CEKScala / JVM399.88 µs291.39 µs1.41×pass
3uplc-turbo AST walkerRust468.58 µs367.94 µs1.65×pass
4plutus-core CEKHaskell / GHC702.57 µs271.69 µs2.47×pass
5Aiken uplc CEKRust → WASM / Node V8901.44 µs901.44 µs3.17×pass
6blaze-plutus CEKTypeScript / Node V81.73 ms1.33 ms6.11×pass
7Scalus CEKScala.js / Node V83.51 ms-12.37×pass
8Aiken uplc CEKRust8.18 ms6.17 ms28.81×pass
–Scalus UPLC→JVM JIT (experimental)Scala / JVM–123.96 µsnot rankedpass

Geometric mean of the time per script over the 89 scripts. Decode + eval is the ranking metric. A VM that fails or skips a script gets the slowest competitor's time for it. Eval only times a program that was decoded before the timed loop. A dash means that the run has no such time for the VM. A VM that compiles a script before it runs it decodes and compiles each script once, before timing. So it has only an eval-only time, and no decode + eval time. The VMs use different execution models, so compare VMs of the same model too (Execution Model). Experimental VMs are shown, but not ranked.

Geometric mean decode + eval, all VMs, fastest first

Per-Script Results

Mean time per script. For each VM: eval only (decode excluded), then decode + eval (the ranking metric). A VM that compiles each script before timing has only the eval only column. Green: the fastest ranked VM on the script, per column type; an experimental VM is never marked. The buttons hide and show VMs.

Execution Model why categories matter

VMs differ in how they execute UPLC. A UPLC-level JIT compiler is doing fundamentally different work than a CEK interpreter, even when both happen to run on a JIT-compiled host runtime like the JVM or V8. Within-category numbers are like-for-like; cross-category numbers are not directly comparable.

Toolchain

VMs in this benchmark ranked fastest first, then experimental, then failed check; versions pinned for this run
Plutigo CEK (Go)Go · native · Interpreter (CEK)
Scalus CEK (Scala / JVM)Scala · GraalVM · Interpreter (CEK)
uplc-turbo AST walker (Rust)Rust · native · Interpreter (AST walker)
plutus-core CEK (Haskell / GHC)Haskell · GHC · Interpreter (CEK)
Aiken uplc CEK (Rust → WASM / Node V8)Rust → WASM · Node (V8) · Interpreter (CEK)decode cached (by the library)
blaze-plutus CEK (TypeScript / Node V8)TypeScript · Node (V8) · Interpreter (CEK)
Scalus CEK (Scala.js / Node V8)Scala.js · Node (V8) · Interpreter (CEK)
Aiken uplc CEK (Rust)Rust · native · Interpreter (CEK)
Scalus UPLC→JVM JIT (Scala / JVM)Scala · GraalVM · JIT compiler (UPLC → JVM bytecode)experimentaldecode and compile excluded

Method

Two timings per script, eval only first. The ranking uses decode + eval: each iteration flat-decodes the script and evaluates it, as a node does with a script it has not seen. Eval only evaluates a program decoded once before timing; decoding is a large share of the time for some VMs (about 66 % for Haskell plutus-core), so the second column shows how fast the evaluator alone is.
Correctness gate: each VM evaluates every script once more and reports the result and the ExUnits. They must equal Haskell plutus-core; a VM that differs is not ranked (“failed check”). A VM whose API cannot take the mainnet cost model is ranked with an “outdated cost model” badge if it equals Haskell with the model the VM ships. A VM that compiles a script before it runs it (“decode and compile excluded”) decodes and compiles each script once, before timing; its timed loop runs the compiled script, so neither step is in its time. So it has only an eval-only time, shown only as eval only, and it is not ranked. Its compile time is shown separately. A VM whose library caches decoded programs (“decode cached (by the library)”) decodes a script only on the first call, in the warm-up; its timed loop runs the cache lookup and the evaluation, so its two columns have the same numbers.
Flat decode + evaluation on Plutus scripts. Each VM uses its native benchmark framework. Each VM runs in its own Docker image, one VM at a time. Geometric means within each category use only the scripts where every VM in that category produced a successful result, so all VMs in a category are compared on the same scripts.

FAQ Plutus VMs and this benchmark

What is a Plutus VM?

A Plutus VM, or UPLC evaluator, runs Untyped Plutus Core (UPLC), the language of Cardano smart contracts. The Cardano node uses the CEK machine of plutus-core (Haskell) to validate scripts. Wallets, SDKs, emulators and other tools use their own evaluators to compute the script result and its execution units (ExUnits) before they submit a transaction.

What does this benchmark measure?

Each VM flat-decodes and evaluates 89 Plutus scripts from the plutus-use-cases test suite (auction, escrow, uniswap, stablecoin and others). The page shows the mean time per script and the geometric mean over all scripts. Each VM runs in its own Docker image on the same server type, one VM at a time. A VM is ranked only if its results and ExUnits equal Haskell plutus-core on every script.

Which Plutus VM is the fastest?

In the run of 2026-10-23, Plutigo CEK (Go) has the lowest decode + eval geometric mean of the ranked VMs: 283.97 µs. plutus-core, the Haskell CEK machine of the Cardano node, takes 702.57 µs (2.47× the fastest time). The VMs use different execution models (CEK interpreter, AST walker, JIT compiler), so compare VMs of the same model too. Experimental VMs are not ranked.

Why does the ranking include the decode time?

A node decodes a script from its flat encoding before it evaluates it. For a script that the node has not seen, both steps take time. For some VMs the decode is a large share of the time (about 66 % for Haskell plutus-core), so the page also shows an eval-only time.

How often is the benchmark updated?

A GitHub Actions workflow runs the full benchmark on the 1st of each month, and the maintainers start extra runs when a VM release changes. The workflow skips a scheduled run when nothing changed. This page shows the run of 2026-10-23.

How can a VM join the benchmark?

A ranked VM has a published release, a downstream user today, and passes the correctness gate. Open a pull request that adds a Docker image for the VM. Follow CONTRIBUTING.md and the image contract.