Performance Tuning¶
How to get the most out of Zig EVM's parallel batch executor.
Source of numbers
The only published performance numbers for Zig EVM come from the project README: 5.1x / 5.9x / 6.0x speedup for 100 / 500 / 1000 independent transactions at 8 threads. All other numbers depend on your hardware, workload, and build mode — measure with the bundled benchmark targets before tuning.
Build Mode¶
Always benchmark and deploy with an optimized build:
zig build -Doptimize=ReleaseFast
zig build bench -Doptimize=ReleaseFast
zig build benchmark -Doptimize=ReleaseFast
zig build bench-full -Doptimize=ReleaseFast
Debug builds include safety checks and are not representative of production performance.
Parallel Execution Tuning¶
The optimized scheduler lives in src/parallel_optimized.zig and is driven via the C ABI BatchConfig struct (see include/zigevm.h). Tunables:
| Field | Meaning |
|---|---|
max_threads | Maximum worker threads in the pool |
enable_parallel | Enable wave-based parallel execution |
enable_speculation | Enable optimistic / speculative execution |
chain_id, block_*, coinbase | Block context applied to every tx |
Workload Characteristics¶
Parallelism is bounded by transaction conflicts. The dependency analyzer detects conflicts on:
- Sender / receiver addresses (balance + nonce)
- Storage slots read or written
Higher parallelism: independent transfers, distinct senders, no shared contract state.
Lower parallelism: bursts from the same sender (nonce ordering forces serialization), hot contracts (DEX pairs, single AMM pool), or write-heavy access to the same storage slots.
Thread Count¶
There is no universally optimal value — it depends on physical core count, hyper-threading behaviour, and your workload's parallelism ceiling. The README's 5–6x table was measured with max_threads = 8; start there and benchmark for your machine.
Measuring¶
Use the bundled targets to collect numbers on your own hardware:
zig build benchmark # parallel optimization benchmarks
zig build bench # simple benchmarks
zig build bench-full # comprehensive suite
zig build demo # benchmark demonstration
zig build parallel-opt # optimized parallel example
The BatchStats struct returned from batch_execute exposes:
total_transactions,successful_transactions,failed_transactions,reverted_transactionstotal_gas_usedexecution_time_nsparallel_waves,max_parallelism
Track these to understand whether you are bottlenecked on dependency chains (low max_parallelism) or raw execution speed.
Memory and Allocators¶
In Zig you can pass any allocator into EVM.init. For short-lived runs (e.g. simulating a single batch) an arena allocator avoids per-call free overhead:
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
var evm = try EVM.init(arena.allocator());
// no need to deinit individual structures; arena frees in bulk
For long-running services, prefer a general-purpose allocator and reuse EVM instances by calling evm_reset between executions instead of destroying and recreating them.
Gas¶
Gas costs in src/main.zig's getGasCost match Ethereum semantics: zero-cost ops (STOP), cheap ops (most arithmetic and bitwise = 3), medium ops (SIGNEXTEND, SELFBALANCE = 5), SHA3 = 30 base, and storage / account access charged in-opcode per EIP-2929. The fastest way to reduce gas is to reduce SSTORE and external account access.
What's Not Yet Benchmarked¶
The README's performance table is the only published, source-grounded number. The repo does not currently include comparison data against other EVM implementations; do not rely on third-party numbers without measuring under identical workloads.