In 2026, parallel EVM execution delivers a measured 5-6x throughput gain over sequential execution in Cryptuon’s Zig-EVM — but only on workloads where transactions don’t contend for the same state. That headline number sits in a crowded field: Monad reports roughly 10,000 TPS from parallel EVM execution at the client level, Solana’s Firedancer has demonstrated around 1M TPS in testing on a non-EVM runtime, and the incumbent Ethereum execution clients reth and geth remain largely sequential. The honest answer to “how does parallel EVM benchmark in 2026” is that the number you get depends almost entirely on your transaction conflict rate — and most published TPS figures quietly assume the best case.
TL;DR
- Parallel EVM speedup is real but conditional. Zig-EVM’s wave-based scheduler achieves a measured 5-6x throughput gain over sequential execution — on low-conflict workloads. High contention erases most of that gain, and no scheduler can fix a workload that is inherently serial.
- TPS is a misleading single number. A benchmark that reports one TPS figure without stating its conflict rate, workload mix, and hardware is not a benchmark — it’s marketing. This article gives the methodology first.
- The comparison is apples-to-oranges by design. Zig-EVM is an embeddable EVM core; Monad, reth, and geth are full clients; Solana’s SVM is a different virtual machine entirely. They optimize different things, and we say so.
- Embedding a fast core beats running a node for simulation, agent decision loops, and pre-flight checks — you want an execution engine, not a peer on the network.
- Throughput headroom matters most for agent-driven and high-frequency workloads, where thousands of speculative simulations precede each real transaction. See how this fits the broader stack on our /agents page.
- Zig-EVM is MIT-licensed, implements 96+ opcodes, and ships FFI bindings for Python, Rust, JavaScript, and C.
- Want to embed a parallel EVM in your own product or benchmark it against your workload? Read /solutions or get in touch.
Why “just report the TPS” is a broken benchmark
Transactions-per-second is the most abused number in blockchain infrastructure. It is trivially gameable because the two dominant factors — what the transactions do and how much they conflict — are rarely disclosed alongside the figure.
Consider two workloads of 10,000 transactions each:
- 10,000 independent token transfers, each touching a distinct pair of accounts that no other transaction in the batch touches.
- 10,000 swaps against a single AMM pool, each reading and writing the same reserve slots.
Workload 1 is embarrassingly parallel: with enough cores, you can execute all 10,000 transactions simultaneously, because none of them read state that another writes. Workload 2 is fundamentally serial: every swap depends on the pool reserves left by the previous swap. No parallel execution engine — not Zig-EVM, not Monad, not anything — can execute workload 2 faster than sequentially, because the data dependency chain is the entire batch.
A vendor who benchmarks workload 1 and reports “50,000 TPS” is not lying about the arithmetic. They are lying by omission about the conditions. This is why every serious parallel-EVM claim must be accompanied by a conflict rate: the fraction of transactions that touch state written by another transaction in the same scheduling window.
For the architecture of how Zig-EVM actually detects those conflicts and schedules waves, see the companion article How We Built a Parallel Execution Engine in Zig. This piece is deliberately not an architecture deep-dive — it’s about how you measure the result and how it compares to the alternatives.
Benchmark methodology
The only way to make parallel-EVM numbers meaningful is to fix and disclose every variable that moves them. Here is the frame we use, and the frame you should demand from any vendor.
The variables that actually move the number
- Conflict rate. The single most important variable. Defined as the fraction of transactions in a batch that read or write a storage slot written by another transaction in the same batch. At 0% conflict, speedup approaches the core count (minus scheduling overhead). At 100% conflict, speedup approaches 1.0x — you are running sequentially with extra bookkeeping.
- Workload mix. Native ETH transfers are cheap (~21,000 gas, minimal state). ERC-20 transfers touch two balances and an allowance. AMM swaps touch pool reserves plus token balances plus fee accounting. A batch of pure transfers and a batch of DEX swaps have completely different compute-to-state ratios, and they parallelize completely differently.
- Hardware. Parallel speedup is bounded by core count. A 5-6x result on an 8-core machine and a 5-6x result on a 64-core machine mean very different things. State that lives in L2 cache behaves differently from state that spills to main memory. Memory bandwidth, not CPU, is often the true ceiling for state-heavy workloads.
- What “throughput” means. We measure execution throughput: wall-clock time to execute a fixed batch of transactions against an in-memory state, excluding networking, consensus, mempool gossip, block propagation, and disk I/O. This is the right measurement for an embeddable engine, and it is a subset of what a full node must do. Comparing an embedded engine’s execution throughput to a full client’s end-to-end TPS is a category error — we call that out explicitly in the comparison table.
- Warm vs cold state. Cold storage reads (first touch, potentially a disk or trie lookup in a real client) dominate cold-workload timing. Our execution benchmarks run against warm in-memory state so that we measure the scheduler, not the storage backend.
The workload matrix we run
We define representative workloads along two axes — compute profile and conflict rate — rather than reporting a single number. A responsible benchmark reports a curve, not a point.
| Workload | Compute profile | Conflict rate | Parallelism ceiling |
|---|---|---|---|
| Independent transfers | Light state, light compute | ~0% | Near core count |
| Mixed dApp traffic | Medium state, medium compute | Low-to-moderate | High, sublinear |
| Hot-pool DEX swaps | Heavy state on shared slots | High | Near 1.0x |
| Adversarial (single contended slot) | Any | ~100% | 1.0x |
Note on Zig-EVM figures: The only performance claims we state as measured are the 5-6x throughput gain over sequential execution and 96+ opcodes. Every other Zig-EVM number in this article — per-workload speedups, illustrative TPS, core-scaling curves — is representative/illustrative to explain the shape of the results, not a benchmarked figure. We are pedantic about this on purpose; the point of the article is benchmarking honesty.
Why the same engine produces a range, not a point
The “5-6x” range is not measurement noise. It reflects the fact that even “low-conflict” real workloads contain some contention. A batch of mixed dApp traffic might be 90% independent and 10% contended; the contended 10% forces short serial segments into an otherwise parallel schedule, and Amdahl’s Law does the rest. The upper end of the range (closer to 6x) corresponds to cleaner, more independent batches; the lower end (closer to 5x) corresponds to realistic mixed traffic. Push the conflict rate high enough and you fall out of the range entirely, toward 1x — which is the honest thing to report, and which most vendors don’t.
Amdahl’s Law is the ceiling nobody advertises
If a fraction s of your workload is inherently serial (because of data dependencies), the maximum speedup from parallelism is bounded by 1 / (s + (1 - s) / N) for N cores. Even a modest 15% serial fraction caps you well under linear scaling regardless of how many cores you throw at it. This is why we lead with conflict rate: it is the serial fraction, and it is the number that determines whether parallelism helps you at all.
How wave-based scheduling gets measured
At a high level — and only at a high level, since the sibling architecture article covers the internals — wave-based execution groups transactions into “waves” of mutually independent transactions, runs each wave in parallel, and commits between waves. The benchmark measures how few waves the scheduler can decompose a batch into, and how efficiently each wave saturates the available cores.
The following pseudocode sketches the shape of what the benchmark exercises. It is illustrative, not the production source:
// Illustrative sketch of the wave-scheduling loop the benchmark exercises.
// Real implementation lives in cryptuon/zig-evm.
fn executeBatch(alloc: Allocator, txs: []Transaction, state: *State) !Metrics {
var pending = txs;
var wave_count: usize = 0;
while (pending.len > 0) {
// Partition the still-pending txs into a maximal set that is
// mutually non-conflicting given the *current* committed state.
const wave = try selectIndependentWave(alloc, pending, state);
// Execute this wave across the thread pool. Each tx runs against
// a snapshot; conflict-driven rollback handles mispredictions.
try runInParallel(alloc, wave, state);
pending = remaining(pending, wave);
wave_count += 1;
}
// Fewer waves + fuller waves == higher measured speedup.
return Metrics{ .waves = wave_count, .parallel_efficiency = /* ... */ };
}
The benchmark’s headline metric is derived from wall_time_sequential / wall_time_parallel. But the diagnostic metrics — wave count and per-wave occupancy — are what tell you why a given workload hit 5.8x or only 1.4x. A workload that decomposes into 3 fat waves parallelizes beautifully; one that decomposes into 400 thin waves is telling you it’s nearly serial.
Conflict detection is what determines the wave count
The scheduler’s job is to know, cheaply, whether two transactions conflict. Conceptually it compares read/write sets:
// Illustrative conflict check between two transactions' access sets.
struct AccessSet {
reads: HashSet<StorageKey>,
writes: HashSet<StorageKey>,
}
fn conflicts(a: &AccessSet, b: &AccessSet) -> bool {
// Write-write, write-read, or read-write on any shared key => conflict.
!a.writes.is_disjoint(&b.writes)
|| !a.writes.is_disjoint(&b.reads)
|| !a.reads.is_disjoint(&b.writes)
// Note: read-read is NOT a conflict — two txs can read the same slot in parallel.
}
The subtlety every benchmark must respect: read/write sets are frequently not known in advance for EVM transactions, because control flow (and therefore which storage slots get touched) depends on runtime values. Zig-EVM handles this with speculative execution and rollback rather than requiring hint annotations — which means the benchmark measures a realistic scenario where the scheduler discovers conflicts dynamically, not an idealized one where every transaction ships a perfect access list. Benchmarks that assume perfect, pre-declared access lists (as some parallel schemes require) are measuring an easier problem than the one real traffic presents.
The benchmark harness config
A reproducible parallel-EVM benchmark pins every variable discussed above. An illustrative harness configuration:
# bench.toml — illustrative harness configuration
[hardware]
cores = 8 # parallel speedup is bounded by this
pin_threads = true # avoid scheduler migration noise
[state]
warm = true # measure the scheduler, not the storage backend
backend = "in-memory"
[[workload]]
name = "independent-transfers"
tx_count = 10_000
kind = "erc20-transfer"
conflict_rate = 0.00 # best case; reports the ceiling
[[workload]]
name = "mixed-dapp"
tx_count = 10_000
kind = "mixed"
conflict_rate = 0.10 # realistic; where the 5-6x range lives
[[workload]]
name = "hot-pool-swaps"
tx_count = 10_000
kind = "amm-swap"
conflict_rate = 0.85 # adversarial; expect speedup near 1x
[report]
metric = "wall_time_sequential / wall_time_parallel"
also_report = ["wave_count", "per_wave_occupancy", "p99_latency"]
runs = 30 # report median + variance, never a single run
If a vendor’s benchmark can’t be described by a config like this — with an explicit conflict_rate per workload and a runs count above one — treat the number with suspicion.
Embedding the engine: getting started
The reason execution throughput (rather than end-to-end node TPS) is the right metric for Zig-EVM is that it’s designed to be embedded — you call it as a library from your own process, not run it as a networked node. That’s what makes it useful for simulation and agent workloads, and it’s also what makes the benchmark measure the thing you actually care about.
A Python caller invoking the embedded engine looks like this:
# Illustrative: invoking the embedded EVM via the Python FFI binding.
import zigevm
vm = zigevm.EVM()
vm.set_code(contract_address, bytecode)
# Simulate a call without touching any network or committing state.
result = vm.call(
caller=agent_address,
to=contract_address,
calldata=encoded_swap,
gas=300_000,
)
print(result.success, result.gas_used, result.return_data)
And from Rust, linking the same native core:
// Illustrative: same engine, Rust FFI binding.
use zigevm::{Evm, CallParams};
let mut vm = Evm::new();
vm.set_code(contract, &bytecode);
let outcome = vm.call(CallParams {
caller: agent,
to: contract,
calldata: &encoded_swap,
gas: 300_000,
});
println!("ok={} gas={}", outcome.success, outcome.gas_used);
Because Python, Rust, JavaScript, and C all bind to the same native library through a zero-overhead C ABI, the execution throughput you benchmark in one language is the throughput you get in all of them — the binding is a thin call shim, not a reimplementation.
Zig-EVM resources: zig-evm.cryptuon.com · Documentation · Source on GitHub
Head-to-head: parallel and sequential EVM implementations in 2026
The following table compares Zig-EVM to the systems it is most often measured against. The critical thing to understand before reading it: these systems are not substitutes for one another. Zig-EVM is an embeddable execution core; Monad, reth, and geth are full execution clients; Solana’s SVM is a different virtual machine. Comparing their TPS numbers directly is the category error we warned about — the table exists to make the differences legible, not to crown a single winner.
| System | Execution model | EVM-compatible? | Embeddable? | Parallelism approach | Maturity |
|---|---|---|---|---|---|
| Zig-EVM (Cryptuon) | Embeddable EVM core | Yes | Yes (FFI: Python, Rust, JS, C) | Wave-based scheduling with speculative execution + conflict-driven rollback; measured 5-6x over sequential on low-conflict workloads | Open-source (MIT), production-focused core, growing |
| Monad | Full L1 client | Yes | No (run as a node) | Optimistic parallel execution at the client level; reported ~10,000 TPS; reportedly ~$355M TVL since its Nov 2025 launch | Mainnet, well-funded, actively maturing |
| reth | Full Ethereum execution client | Yes | Partially (modular crates, but node-oriented) | Largely sequential execution; parallel work concentrated in state/trie and I/O paths | Mature, production, widely deployed |
| geth | Full Ethereum execution client | Yes | No (reference client) | Largely sequential EVM execution | Most mature, reference implementation |
| Solana SVM (incl. Firedancer) | Full L1 runtime | No (SVM, not EVM) | Partially (SVM is extractable) | Parallel by design via pre-declared account access lists; Firedancer reported ~1M TPS in testing | Mainnet; Firedancer maturing |
A few honest observations on the table:
- Monad’s ~10,000 TPS and Firedancer’s ~1M TPS are full-system, end-to-end figures reported by their teams — they include consensus, networking, and the whole pipeline. Zig-EVM’s 5-6x is a relative execution-only speedup versus sequential execution of the same batch on the same hardware. These are not the same kind of measurement and cannot be lined up as “5-6x vs 10,000 vs 1M.” Anyone who does line them up that way is comparing a ratio to two absolutes across three different measurement boundaries.
- Solana’s ~1M TPS is on the SVM, not the EVM. The SVM parallelizes cleanly in large part because Solana transactions must declare the accounts they touch up front. The EVM’s dynamic state access is precisely what makes EVM parallelization hard — and it’s why a dynamic, speculative approach like wave-based scheduling is the interesting engineering problem for the EVM specifically.
- reth and geth being “largely sequential” is not a criticism. They are correctness-first, network-facing clients where deterministic, sequential execution is the conservative and well-understood choice, and where the bottleneck is often state I/O rather than execution compute.
Business outcome: why throughput headroom is worth paying for
If you only ever execute one transaction at a time, in order, waiting for network confirmation between each, then parallel execution throughput is irrelevant to you. The reason it matters — and matters a lot in 2026 — is the rise of workloads that execute far more transactions than they submit.
The agent-economy workload
Autonomous on-chain agents don’t submit one transaction and hope. They simulate. Before an agent commits to a swap, a liquidation, or a rebalance, it runs the candidate transaction — often dozens of variants across different routes, sizes, and slippage tolerances — against current state to pick the best one. Every real transaction is preceded by a burst of speculative simulations, and those simulations don’t need to hit the network at all. They need a fast, embeddable execution core.
This is why parallel EVM is part of the agent-economy stack. An agent framework that can simulate 10,000 candidate transactions in the time a node would take to simulate 2,000 can explore a much larger decision space per block. Throughput headroom translates directly into better decisions. This is the connection between raw execution speed and the agent infrastructure Cryptuon builds, and it pairs naturally with the state-synchronization problem we cover in cross-chain state sync for the agent economy.
Embed a core vs run a node
The ROI framing is starkest here. Running a full node to do simulation is enormously wasteful: you pay for consensus participation, peer networking, mempool gossip, and disk-heavy state you don’t need, just to get at the execution engine buried inside. Embedding a dedicated execution core:
- Eliminates the operational surface of running and monitoring a node fleet just to simulate transactions.
- Removes network round-trips from the hot path — simulations are in-process function calls, not RPC.
- Scales with your CPU budget, not your node budget — you provision cores for throughput, not validators for consensus.
- Keeps proprietary strategy logic in your process, since the agent’s candidate transactions never leave your infrastructure to be simulated by a shared public endpoint.
For teams evaluating this tradeoff for a specific workload, that’s exactly the kind of architecture question our solutions work addresses, and the broader engineering rationale lives in our research.
Limitations
Benchmarking honesty cuts both ways, so here is where parallel EVM — and Zig-EVM specifically — does not help you.
Current limitations
- Speedup collapses under high contention. This is not a bug to be fixed; it is a mathematical property of the workload. A batch where most transactions touch the same hot storage slot (a single popular AMM pool, a heavily-used nonce, a global counter) is serial by nature. Wave-based scheduling will correctly identify this and execute those transactions in sequence, yielding speedup near 1.0x. The 5-6x figure is explicitly a low-conflict result.
- The 5-6x range is workload-dependent, not a constant. It reflects mixed-but-mostly-independent traffic. Your workload will land somewhere on the curve between ~1x (adversarial) and near-linear (fully independent). Benchmark your own traffic; do not assume the headline.
- Speculative execution has overhead. When the scheduler mispredicts and a transaction must roll back, that work is wasted. On workloads with unpredictable, moderate contention, rollback overhead eats into the theoretical speedup — which is part of why the realistic figure is 5-6x and not the core count.
- Execution throughput is not end-to-end TPS. Zig-EVM measures execution against in-memory state. A production system still needs a storage backend, and cold state access, disk I/O, and trie updates can become the real bottleneck — none of which the execution speedup addresses.
- 96+ opcodes is broad but you must verify coverage for your bytecode. Confirm the specific opcodes and precompiles your contracts require against the documentation.
Roadmap items
- Broader, continuously-published benchmark suites across more workload profiles, so users can match a public curve to their own traffic shape rather than a single headline.
- Continued opcode and precompile coverage expansion tracked against upstream EVM changes.
- Deeper tooling for measuring per-wave occupancy and conflict rate on your transaction traces, so you can predict your realistic speedup before integrating.
We would rather tell you where the 5-6x doesn’t hold than have you discover it in production. If your workload is contention-heavy, parallel execution is the wrong lever — and we’ll say so on a call.
Frequently Asked Questions
What is parallel EVM execution?
Parallel EVM execution runs multiple Ethereum transactions simultaneously across several CPU cores instead of one at a time in sequence. The core challenge is that the EVM allows transactions to read and write shared state, so two transactions that touch the same storage slot cannot safely run in parallel. A parallel EVM must detect these conflicts — either from pre-declared access lists or, as Zig-EVM does, dynamically via speculative execution and rollback — and schedule only mutually-independent transactions to run at the same time. The achievable speedup is bounded by how independent the workload is.
Is Zig-EVM compatible with existing Solidity bytecode?
Zig-EVM implements 96+ EVM opcodes and is designed to execute standard EVM bytecode, which is the compilation target of Solidity. Whether a specific contract runs unmodified depends on the exact opcodes and precompiles it uses; you should verify coverage for your particular bytecode against the Zig-EVM documentation. The design goal is compatibility with real EVM bytecode, not a subset dialect.
What’s the fastest EVM in 2026?
There is no single honest answer, because “fastest” depends on what you’re measuring. If you mean end-to-end client throughput, Monad reports the highest EVM TPS figures (roughly 10,000 TPS) among EVM-compatible L1s. If you mean raw runtime throughput regardless of VM, Solana’s Firedancer has reported around 1M TPS — but on the SVM, not the EVM. If you mean execution speedup from parallelism in an embeddable core, Zig-EVM delivers a measured 5-6x over sequential execution on low-conflict workloads. These are different questions with different answers, and anyone who gives you one number for “fastest EVM” without qualifying the measurement is oversimplifying.
How much does transaction conflict rate affect parallel speedup?
Enormously — it is the single dominant factor. At near-zero conflict, parallel speedup approaches the core count (minus scheduling overhead). As the conflict rate rises, speedup falls, because contended transactions must be serialized. At high conflict (most transactions touching shared state), speedup approaches 1.0x — you’re effectively running sequentially with extra bookkeeping. Zig-EVM’s measured 5-6x is a low-conflict result; your workload’s speedup depends on its own conflict profile. This is why we insist any parallel-EVM benchmark disclose its conflict rate.
Why compare an embeddable EVM core to full clients like Monad and geth?
Because those are the systems people ask about, even though they aren’t direct substitutes. Zig-EVM is a library you embed in your own process to execute transactions; Monad and geth are full clients you run as network nodes. The comparison table exists to make those structural differences clear — not to line up TPS numbers that were measured at different boundaries. An embeddable core is the right tool for simulation and agent workloads; a full client is the right tool for participating in a network. They solve different problems.
Can parallel EVM execution ever slow things down?
Yes, marginally, on the wrong workload. On a fully serial batch — every transaction depends on the previous one — a parallel scheduler does all the sequential work plus the overhead of conflict detection and any speculative execution that gets rolled back. In that adversarial case you land at or slightly below 1.0x. A well-designed scheduler minimizes this overhead and recognizes serial workloads quickly, but it cannot make an inherently serial workload parallel. This is exactly why we benchmark across a workload matrix rather than reporting one number.
Building agent infrastructure, high-frequency simulation, or an L2 execution layer and want to know whether parallel EVM helps your specific workload? Read more in our research, explore what we build for agents, or get in touch.