Benchmark datasheet

Plutus VM Benchmark

How fast Cardano Plutus (UPLC) virtual machines run the same scripts, and whether their results are correct.

Revision (run date)
Correctness gate
Scripts
scripts
Harness commit
256b6aafc
Test machine
AMD EPYC-Milan Processor · 16 cores · 61Gi · 3 parallel lanes · 2026-10-27 · harness 256b6aafc
Test conditions

RankingRanked on decode + eval. The same scripts for every VM.

Ranking

Timing
Timing

    Decode + eval is the ranking metric: each timed loop decodes the flat script, then evaluates it, as a node does with a script it has not seen. Eval only times a script decoded before the loop. A VM that compiles each script before timing has only an eval-only time, so it is not ranked. The scripts’ Plutus version and cost model are in Test conditions.

    Select a row for its details. Select a script name in the per-script table to rank that script here.

    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-27 has 15 evaluators, written in Scala, Go, Rust, Haskell, Scala.js, TypeScript, C#, Java, Zig and C++. The method and the source are open.

    Summary of all VMs – all VMs, geometric mean over 89 scripts

    #VMLanguage / runtimeDecode + evalEval onlyvs fastestCheck
    1Scalus CEKScala / JVM268.19 µs170.83 µs1.00×pass
    2PlutigoGo306.48 µs204.65 µs1.14×pass
    3amaru-uplcRust467.01 µs365.22 µs1.74×pass
    4plutus-coreHaskell / GHC710.63 µs276.06 µs2.65×pass
    5Scalus.js CEKScala.js / Node V81.46 ms980.88 µs5.46×pass
    6Aiken uplcRust8.22 ms6.21 ms30.66×pass
    –Aiken uplc WASMRust → WASM / Node V8–884.88 µseval onlypass
    –Scalus JIT (experimental: no downstream user)Scala / JVM–94.41 µsnot rankedpass
    –blaze-plutus (experimental: no downstream user)TypeScript / Node V81.29 ms1.06 msnot rankedpass
    –Chrysalis (experimental: pre-release only)C# / .NET JIT536.58 µs401.63 µsnot rankedpass
    –Chrysalis AOT (experimental: pre-release only)C# / .NET AOT586.35 µs447.74 µsnot rankedpass
    –Julc Java CEK (experimental: pre-release only)Java / JVM487.78 µs392.40 µsnot rankedpass
    –Plutuz (experimental: no release)Zig517.88 µs335.45 µsnot rankedpass
    –llvm-uplc JIT (experimental: outdated)C++ / LLVM–111.08 µsnot rankedoutdated cost model
    –amaru-uplc BC (fork) (experimental: no release)Rust340.86 µs220.89 µ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. Aiken WASM caches decoded programs inside the library, so its time is shown as eval only.

    Per-script resultsSort by any column. A script name loads it into the ranking.

    Mean time per script

    Each VM has two columns: 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 bold marks the fastest ranked VM on a script, per column type. An experimental VM is never marked. Select a header to sort. Select a script name to rank that script in the ranking. The buttons hide and show VMs.

    HistoryEach run is a revision. One colour per VM, as in the ranking.

    Revision history

    The benchmark runs again when a VM or the harness changes. Each run is one revision of this datasheet. The history chart plots the same runs.

    RevisionRun dateVMs timedFastest vs Haskell

    Test conditionsThe conditions of this revision.

    All VMs run on the same server, in the same run, with the same harness. These are the conditions of this revision.

    Scripts Plutus scripts, the same for every VM.
    ReferenceHaskell plutus-core, the CEK machine of the Cardano node.Every VM must return the same result and the same ExUnits on every script.
    IsolationEach VM runs in its own Docker image, with its native benchmark framework.
    VM versionsPinned for this run. See VMs and toolchains.
    HarnessCommit 256b6aafc
    Reference driftA small drift means the server stayed stable during the run.
    Noise band±5 %Servers of the same type differ by up to this much between runs. A smaller change counts as no change.

    Full method – how each loop is timed and how the geometric means are taken.

    VMs and toolchainsRanked fastest first, then experimental, then failed check. Versions pinned for this run.

    VMPinned versionLinksUsed byStatus and notes
    Scalus CEK (Scala / JVM)Scala, GraalVM, Interpreter (CEK)v1.4.1Hydrozoa L2 proofBifrost bridge proofCardano Client Lib SDK proofYaci Store indexer proofYaci DevKit dev tool proof
    Plutigo (Go)Go, native, Interpreter (CEK)v0.1.10Dingo node proof
    amaru-uplc (Rust)Rust, native, Interpreter (AST walker)amaru-uplc-v0.4.0Amaru node proof
    plutus-core (Haskell / GHC)Haskell, GHC, Interpreter (CEK)1.70.0.0cardano-node node proof
    Scalus.js CEK (Scala.js / Node V8)Scala.js, Node (V8), Interpreter (CEK)scalus 1.4.1 (npm)Lucid Evolution SDK proofMeshJS SDK proofEvolution SDK SDK proof
    Aiken uplc (Rust)Rust, native, Interpreter (CEK)v1.1.24Aiken compiler proofBlaze SDK SDK proof
    Aiken 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.
    Aiken uplc WASM (Rust → WASM / Node V8)Rust → WASM, Node (V8), Interpreter (CEK)@lucid-evolution/uplc 0.4.0Lucid Evolution SDK proofdecode cached (by the library)
    Built from colll78/aiken (aiken main plus fixes), which includes the CEK fixes that v1.1.24 lacks.
    Scalus JIT (Scala / JVM)Scala, GraalVM, JIT compiler (UPLC → JVM bytecode)v1.4.1none yetexperimental: no downstream userdecode and compile excluded
    blaze-plutus (TypeScript / Node V8)TypeScript, Node (V8), Interpreter (CEK)@blaze-cardano/plutus 0.1.1none yetexperimental: no downstream user
    Chrysalis (C# / .NET JIT)C#, .NET 10 (JIT), Interpreter (CEK)Chrysalis.Plutus 1.7.14-alpha (NuGet)none yetexperimental: pre-release only
    Chrysalis AOT (C# / .NET AOT)C#, .NET 10 (NativeAOT), Interpreter (CEK)Chrysalis.Plutus 1.7.14-alpha (NuGet)none yetexperimental: pre-release only
    Julc Java CEK (Java / JVM)Java, GraalVM, Interpreter (CEK)v0.1.0-pre19none yetexperimental: pre-release only
    Plutuz (Zig)Zig, native, Interpreter (CEK)main 33b812dnone yetexperimental: no release
    llvm-uplc JIT (C++ / LLVM)C / C++, native (LLVM 22 LLJIT), JIT compiler (UPLC → native code)main 6bcef67none yetoutdated cost modelexperimental: outdateddecode and compile excluded
    amaru-uplc BC (fork) (Rust)Rust, native, Bytecode VM (UPLC → bytecode)fork 069cef5none yetexperimental: no release

    Execution modelWhy the ranking has categories.

    VMs run UPLC in different ways. A UPLC-level JIT compiler does different work from a CEK interpreter, even when both run on a JIT-compiled host such as the JVM or V8. Numbers in one category compare like for like. Numbers across categories do not compare directly.

    Method

    Each script has two timings. Eval only evaluates a program that was decoded once, before timing. Decode + eval decodes the flat script and evaluates it in every iteration, as a node does with a script it has not seen. The ranking uses decode + eval. Decoding is a large share of the time for some VMs (about 66 % for Haskell plutus-core), so eval only 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. It has only an eval-only time and 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 and evaluation on Plutus scripts. Each VM uses its native benchmark framework and runs in its own Docker image. Geometric means in a category use only the scripts where every VM in that category succeeded, so all VMs in a category are compared on the same scripts.

    FAQPlutus 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-27, Scalus CEK (Scala / JVM) has the lowest decode + eval geometric mean of the ranked VMs: 268.19 µs. plutus-core, the Haskell CEK machine of the Cardano node, takes 710.63 µs (2.65× 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-27.

    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.