Benchmarking EVM Implementations: Zig EVM vs revm vs evmone
A detailed benchmark comparison of EVM implementations including Zig EVM, revm, and evmone, covering methodology, opcode performance, and architecture tradeoffs.
TL;DR: We benchmarked three EVM implementations — Zig EVM, revm (Rust), and evmone (C++) — across opcode execution, arithmetic throughput, and transaction processing. revm and evmone lead in single-threaded per-opcode performance, while the Zig EVM’s parallel execution engine provides the highest batch throughput for independent transactions. This article details the methodology, results, and tradeoffs to help you choose the right implementation.
Why Benchmark EVM Implementations?
EVM performance directly impacts blockchain throughput, block processing time, and user experience. Node operators need fast EVMs to stay synchronized with the chain. Researchers need them to run simulations. Tool builders need them for testing and debugging.
But “fastest” depends entirely on what you measure. An EVM that executes individual opcodes in 2 nanoseconds but cannot parallelize transactions may be slower in aggregate than one with 5-nanosecond opcodes and 8-thread parallel execution.
Meaningful EVM benchmarks must specify the workload, measurement methodology, and hardware environment. A benchmark result without these three elements is not actionable.
Implementations Under Test
revm (Rust)
revm is the EVM used by the Reth Ethereum client. It is written in Rust and has become the de facto reference for high-performance EVM execution.
- Language: Rust
- Dispatch: Match statement with computed goto optimization
- 256-bit arithmetic:
ruintcrate (optimized native types) - Test compliance: Full Ethereum test suite
- Notable feature: Comprehensive precompile support, database abstraction
evmone (C++)
evmone is the Ethereum Foundation’s reference high-performance EVM, written in C++. It pioneered several optimization techniques that other implementations have adopted.
- Language: C++20
- Dispatch: Computed goto / switch with advanced tail-call optimization
- 256-bit arithmetic:
intxlibrary (custom 256-bit integers) - Test compliance: Full Ethereum test suite
- Notable feature: EVMC interface, advanced dispatch optimization
Zig EVM
The Zig EVM is a research-oriented implementation focused on parallel execution and educational clarity.
- Language: Zig
- Dispatch: HashMap-based function pointer lookup
- 256-bit arithmetic: Native Zig
u256+ custom BigInt module - Test compliance: Partial (96 opcodes, growing)
- Notable feature: Parallel execution with speculative rollback
Architecture Comparison
| Feature | revm | evmone | Zig EVM |
|---|---|---|---|
| Language | Rust | C++20 | Zig |
| Opcodes | 144+ | 144+ | 96 |
| Dispatch mechanism | Match + computed goto | Computed goto | HashMap |
| Memory safety | Compile-time (borrow checker) | Manual | Compile-time (optional safety) |
| Parallel execution | External (via Reth) | No built-in | Built-in (wave + speculative) |
| Precompiles | Full set | Full set | Partial |
| EVMC compatible | No (own interface) | Yes | No |
| Build system | Cargo | CMake | Zig build |
| Binary size | ~2 MB | ~1.5 MB | ~800 KB |
| Compilation time | ~30s | ~20s | ~5s |
Dispatch Mechanism Impact
The dispatch mechanism is the innermost loop of EVM execution. Every opcode lookup passes through it.
Computed goto (used by evmone and revm in release mode) eliminates branch prediction overhead by jumping directly to the next opcode handler. The CPU does not need to predict which branch of a switch statement will be taken.
HashMap lookup (used by Zig EVM) adds approximately 3-5 nanoseconds of overhead per opcode compared to computed goto. This is negligible for complex opcodes like SSTORE (which takes microseconds) but measurable for simple opcodes like ADD (which takes 2-3 nanoseconds).
// Zig EVM dispatch: HashMap lookup (~5ns overhead)
const handler = self.opcodes.get(opcode) orelse return error.InvalidOpcode;
try handler.execute(self);
// Equivalent computed goto approach (evmone style, pseudocode):
// goto *dispatch_table[opcode];
// Each handler ends with: goto *dispatch_table[*pc++];
For the Zig EVM, the HashMap approach was chosen for development velocity and extensibility. Switching to an array-based dispatch table is a straightforward optimization that eliminates the gap.
Benchmark Methodology
Hardware
All benchmarks were run on the following system:
- CPU: AMD Ryzen 9 7950X (16 cores, 32 threads)
- RAM: 64 GB DDR5-5600
- OS: Linux 6.x
- Compiler flags: All implementations compiled with maximum optimization (
-O3,ReleaseFast)
Measurement Protocol
- Warmup: 1,000 iterations discarded to stabilize CPU caches and branch predictors
- Measurement: 10,000 iterations with per-iteration timing
- Statistical analysis: Median reported (not mean) to reduce outlier impact
- Isolation: Single-threaded benchmarks pinned to one core; parallel benchmarks use all available cores
Benchmark Suite
We measure four categories:
- Per-opcode latency: Time to execute a single opcode in isolation
- Arithmetic throughput: Extended sequences of 256-bit arithmetic
- Memory operations: MLOAD/MSTORE with varying access patterns
- End-to-end transactions: Complete transaction execution including dispatch, gas metering, and state access
Benchmark Results
Per-Opcode Latency (Single Opcode, Nanoseconds)
| Opcode | revm | evmone | Zig EVM | Notes |
|---|---|---|---|---|
| ADD | 2.1 | 1.8 | 4.9 | Stack pop + push + addition |
| MUL | 2.4 | 2.1 | 5.2 | 256-bit multiplication |
| DIV | 8.7 | 7.2 | 12.4 | 256-bit division (expensive) |
| SDIV | 11.3 | 9.8 | 15.1 | Signed division (sign handling) |
| MOD | 9.1 | 7.6 | 13.0 | 256-bit modulus |
| ADDMOD | 10.2 | 8.8 | 14.7 | Modular addition (512-bit intermediate) |
| MULMOD | 14.5 | 12.1 | 18.3 | Modular multiplication |
| PUSH1 | 1.5 | 1.2 | 4.1 | Read 1 byte, push to stack |
| MLOAD | 3.2 | 2.8 | 6.1 | Memory read (warm) |
| MSTORE | 3.0 | 2.6 | 5.8 | Memory write (warm) |
| JUMP | 2.8 | 2.4 | 5.5 | PC update + JUMPDEST validation |
| DUP1 | 1.4 | 1.1 | 3.8 | Duplicate top of stack |
| SWAP1 | 1.6 | 1.3 | 4.0 | Swap top two stack items |
Analysis: evmone consistently leads in per-opcode latency due to its computed goto dispatch and hand-optimized C++ intx library. revm is within 15-20% of evmone. The Zig EVM’s HashMap dispatch adds approximately 3ns overhead visible in every measurement.
Arithmetic Throughput (Million Operations per Second)
Running 10,000 consecutive arithmetic operations:
| Operation | revm (Mops) | evmone (Mops) | Zig EVM (Mops) |
|---|---|---|---|
| ADD chain | 410 | 480 | 185 |
| MUL chain | 360 | 420 | 170 |
| DIV chain | 105 | 125 | 72 |
| Mixed arithmetic | 210 | 245 | 120 |
The gap narrows for division-heavy workloads because the division algorithm itself dominates execution time, reducing the relative impact of dispatch overhead.
End-to-End Transaction Processing
This is the most practically relevant benchmark. It measures complete transaction execution including ABI decoding, dispatch, gas metering, storage access, and state updates.
Single-Threaded (Transactions per Second)
| Workload | revm | evmone | Zig EVM |
|---|---|---|---|
| Simple transfer | 185,000 | 195,000 | 82,000 |
| ERC-20 transfer | 45,000 | 48,000 | 22,000 |
| Uniswap V2 swap | 12,000 | 13,500 | 6,200 |
| Complex DeFi | 4,800 | 5,200 | 2,400 |
Multi-Threaded Batch Processing (Transactions per Second)
This is where the Zig EVM’s parallel execution changes the picture:
| Workload | revm (1 thread) | evmone (1 thread) | Zig EVM (8 threads) |
|---|---|---|---|
| Simple transfer (independent) | 185,000 | 195,000 | 468,000 |
| ERC-20 transfer (mixed deps) | 45,000 | 48,000 | 98,000 |
| Uniswap V2 swap (low conflict) | 12,000 | 13,500 | 31,000 |
| Complex DeFi (high conflict) | 4,800 | 5,200 | 8,400 |
For independent transaction batches, the Zig EVM’s 8-thread parallel execution achieves 2.4x the throughput of revm and evmone in single-threaded mode, despite having slower per-opcode performance. The advantage diminishes as conflict rates increase.
Parallel Scaling Characteristics
| Threads | Zig EVM (independent txs) | Zig EVM (10% conflicts) | Zig EVM (30% conflicts) |
|---|---|---|---|
| 1 | 82,000 | 82,000 | 82,000 |
| 2 | 158,000 | 148,000 | 132,000 |
| 4 | 298,000 | 264,000 | 215,000 |
| 8 | 468,000 | 385,000 | 272,000 |
| 16 | 510,000 | 401,000 | 285,000 |
Scaling flattens beyond 8 threads due to memory bandwidth saturation and synchronization overhead in the state merging phase.
What These Numbers Mean in Practice
For Node Operators
If you are running an Ethereum node, single-threaded performance is what matters today because Ethereum mainnet processes transactions sequentially. revm (via Reth) or evmone (via various clients) are the optimal choices.
For L2 and Alt-L1 Builders
If you are building a blockchain that can define its own execution model, parallel execution throughput becomes the key metric. The Zig EVM’s wave-based parallel execution demonstrates that 5-6x throughput improvements are achievable with commodity hardware.
For Researchers and Tool Builders
The Zig EVM’s value proposition is not raw speed — it is clarity, hackability, and built-in parallel execution. The modular opcode architecture makes it straightforward to add custom opcodes, instrument execution, or experiment with alternative gas models.
# Run Zig EVM benchmarks yourself
git clone https://github.com/cryptuon/zig-evm.git
cd zig-evm
# Single-threaded benchmark
zig build benchmark -Doptimize=ReleaseFast
# Parallel execution benchmark
zig build parallel-opt -Doptimize=ReleaseFast
# Quick demo benchmark
zig build demo
Tradeoff Analysis
revm Strengths
- Highest single-threaded throughput in the Rust ecosystem
- Memory safety without runtime overhead
- Excellent documentation and active community
- Full Ethereum test suite compliance
- Used in production by Reth
revm Limitations
- Parallel execution depends on the client (Reth), not the EVM itself
- Rust’s compilation times are significant for rapid iteration
- Complex trait-based architecture has a steep learning curve
evmone Strengths
- Lowest per-opcode latency of any implementation
- EVMC interface enables use with multiple clients
- Mature and battle-tested codebase
- Ethereum Foundation backing
evmone Limitations
- C++ memory management requires careful auditing
- No built-in parallel execution
- Smaller contributor community than revm
Zig EVM Strengths
- Built-in parallel execution with multiple strategies
- Fast compilation (~5 seconds vs. ~30 seconds for revm)
- Clean, readable codebase designed for education and research
- Modular opcode architecture for easy extension
- Small binary size (~800 KB)
Zig EVM Limitations
- 96 of 144+ opcodes implemented (not yet production-complete)
- HashMap dispatch adds per-opcode overhead
- Smaller ecosystem and community
- Not yet tested against full Ethereum test suite
What to Measure in Your Own Benchmarks
If you are evaluating EVM implementations for your project, measure what matters for your use case:
| Use Case | Primary Metric | Secondary Metric |
|---|---|---|
| Full node | Single-threaded tx/s | Memory usage |
| L2 sequencer | Multi-threaded tx/s | Latency (p99) |
| Block explorer | End-to-end tx/s | Trace generation speed |
| Security tooling | Correctness | Opcode coverage |
| Research | Hackability | Compilation time |
| Gas estimation | Gas accuracy | Single tx latency |
Benchmark Pitfalls to Avoid
- Do not benchmark debug builds. Always use release/optimized compilation. Debug builds can be 10-50x slower and produce misleading comparisons.
- Do not measure cold cache. Run warmup iterations before timing. CPU cache state dramatically affects results.
- Do not use mean for timing data. Use median or percentiles. GC pauses, context switches, and interrupts create outliers that skew means.
- Do not extrapolate from microbenchmarks. A 2x speedup on
ADDdoes not translate to 2x speedup on real transactions whereADDis 1% of execution time. - Do not ignore memory usage. An EVM that is 10% faster but uses 3x the memory may be worse for your deployment.
Key Takeaways
- evmone has the lowest per-opcode latency due to computed goto dispatch and hand-optimized 256-bit arithmetic in C++.
- revm offers the best balance of performance, safety, ecosystem support, and production readiness for single-threaded Ethereum execution.
- The Zig EVM achieves the highest batch throughput through built-in parallel execution, processing 2.4x more independent transactions per second than single-threaded alternatives with 8 threads.
- Dispatch mechanism matters less than you think. For realistic workloads, the difference between computed goto and HashMap lookup is under 15% of total execution time because complex opcodes (storage, calls) dominate.
- Parallel execution is the multiplier. Single-threaded optimizations yield incremental gains; parallelism yields multiplicative gains for suitable workloads.
- Choose based on your use case: revm for production Rust nodes, evmone for C++ integration and lowest latency, Zig EVM for parallel execution research and education.
Try It Yourself
All Zig EVM benchmark code is open source:
git clone https://github.com/cryptuon/zig-evm.git
cd zig-evm
# Run all benchmarks
zig build benchmark -Doptimize=ReleaseFast
# Run parallel execution benchmarks
zig build parallel-opt -Doptimize=ReleaseFast
# Interactive exploration
# Visit /playground/ for browser-based execution
Explore opcode-level execution behavior in the Zig EVM Playground, or dive into the source code at github.com/cryptuon/zig-evm.
Frequently Asked Questions
- What is the fastest EVM implementation?
- revm (Rust) and evmone (C++) are currently the fastest production EVM implementations, optimizing for different workloads. revm excels in real-world transaction throughput due to Rust's memory safety with zero overhead, while evmone achieves the lowest per-opcode latency through advanced C++ optimization. The Zig EVM prioritizes parallel execution throughput.
- How do you benchmark an EVM implementation?
- EVM benchmarks measure per-opcode execution time, arithmetic throughput (especially 256-bit operations), memory operation latency, end-to-end transaction processing rate, and gas computation overhead. Reliable benchmarks use warmup iterations, statistical sampling, and standardized bytecode sequences to ensure comparable results.
- What language is revm written in?
- revm is written in Rust. It is the EVM implementation used by the Reth Ethereum client and is known for its combination of memory safety, performance, and comprehensive Ethereum test suite compliance.
- Should I use revm or evmone for my project?
- Use revm if you are building in the Rust ecosystem, need comprehensive Ethereum compatibility, or want active community support. Use evmone if you need the lowest possible per-opcode latency, are building in C/C++, or are embedding an EVM in a constrained environment. Use the Zig EVM if you are researching parallel execution or working in the Zig ecosystem.
- How does parallel execution affect EVM benchmarks?
- Parallel execution multiplies throughput for independent transaction batches. The Zig EVM achieves 5-6x throughput improvement with 8 threads, which can make it faster for batch processing than single-threaded implementations with lower per-opcode latency. The relevant metric depends on whether your workload is latency-sensitive or throughput-sensitive.
Try Zig EVM
Explore the interactive playground or dive into the source code.