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.
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
| # | VM | Language / runtime | Decode + eval | Eval only | vs fastest | Check |
| 1 | Plutigo CEK | Go | 283.97 µs | 182.63 µs | 1.00× | pass |
| 2 | Scalus CEK | Scala / JVM | 399.88 µs | 291.39 µs | 1.41× | pass |
| 3 | uplc-turbo AST walker | Rust | 468.58 µs | 367.94 µs | 1.65× | pass |
| 4 | plutus-core CEK | Haskell / GHC | 702.57 µs | 271.69 µs | 2.47× | pass |
| 5 | Aiken uplc CEK | Rust → WASM / Node V8 | 901.44 µs | 901.44 µs | 3.17× | pass |
| 6 | blaze-plutus CEK | TypeScript / Node V8 | 1.73 ms | 1.33 ms | 6.11× | pass |
| 7 | Scalus CEK | Scala.js / Node V8 | 3.51 ms | - | 12.37× | pass |
| 8 | Aiken uplc CEK | Rust | 8.18 ms | 6.17 ms | 28.81× | pass |
| – | Scalus UPLC→JVM JIT (experimental) | Scala / JVM | – | 123.96 µs | not ranked | pass |
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.
Failed check not ranked
The result or the ExUnits of these VMs differ from Haskell plutus-core for at least one script, so their timings are not published (correctness gate, check/README.md).
Geometric mean decode + eval, all VMs, fastest first
History speed relative to Haskell plutus-core, same run – higher is faster
Each point is Haskell's geometric mean time divided by the VM's, from the same run: 2× faster means the VM needs half of Haskell's time. The solid grey line is Haskell (1×); a dashed line is an experimental VM. The ratio uses the scripts where both the VM and Haskell have a timing. A line marked “eval only” is a VM that compiles each script before timing: its ratio is its eval-only time divided by Haskell's eval-only time. Comparing ratios, not absolute times, removes most of the hardware and run-to-run drift. Servers of the same type differ: ratios to Haskell move up to about 5 % between two runs on different servers, so a change under 5 % is shown as no change. The end label and the tooltip give the change against the VM's previous run. Log scale.
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.
Experimental not ranked
These VMs are run, gated and shown with their numbers, but they are not ranked: they are not in the rankings above, not a competitor in the penalty rule, and not the 1.00× reference in the geometric-mean chart (where they are drawn hatched); the history chart draws them dashed, and never the fastest in the per-script table. The geometric mean uses the penalty rule against the ranked VMs. A VM that compiles each script before timing shows its eval-only geometric mean (no penalty rule) and is not in the geometric-mean chart.
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.
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)
version v1.1.24source aiken-lang/aikenhome aiken-lang.orgAiken v1.1.24 copies the CEK environment on every lambda application (quadratic in recursion depth). aiken main fixes it (PRs
#1451,
#1452: coop-6 92 ms -> 1.2 ms), but no release contains the fix yet.
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.