Zig vs Rust -- a head-to-head performance comparison of eth.zig and alloy.rs across 26 core Ethereum operations: Keccak-256 hashing, ABI encoding/decoding, RLP serialization, secp256k1 ECDSA signing, u256 arithmetic (including UniswapV4 mulDiv with true 512-bit intermediate), hex operations, address derivation, and EIP-1559 transaction hashing.
Score: eth.zig wins 18/26 | alloy.rs wins 5/26 | tied 3/26
Run date: 2026-06-10 on Apple Silicon (aarch64-macos), Zig 0.16.0 ReleaseFast vs Rust 1.94.0 --release. alloy crate versions: alloy-primitives 1.6.0, alloy-sol-types 1.6.0, alloy-dyn-abi 1.6.0, alloy-consensus 2.0.5, alloy-signer 2.0.5, alloy-rlp 0.3.15, k256 0.13. Custom criterion-style harness with 0.5s warmup, calibrated batch sizes, and 2s measurement window. Both mulDiv benchmarks use true 512-bit intermediate arithmetic (eth.zig's mulDiv, alloy's U512 from ruint).
Historical note: an earlier comparison (March 2026, against alloy-primitives 0.8 / alloy 0.6) scored eth.zig 23/26. alloy has since improved several hot paths (Keccak on larger inputs, RLP u256 decoding, hex encoding, UniswapV4-style swap math), and the table below is the current head-to-head against alloy 1.6/2.0.
| Benchmark | eth-zig | alloy.rs | Winner |
|---|---|---|---|
| keccak256_empty | 261 ns | 310 ns | zig 1.19x |
| keccak256_32b | 263 ns | 319 ns | zig 1.21x |
| keccak256_256b | 534 ns | 532 ns | rs 1.00x |
| keccak256_1kb | 2,073 ns | 2,039 ns | rs 1.02x |
| keccak256_4kb | 7,732 ns | 7,838 ns | zig 1.01x |
| secp256k1_sign | 22,033 ns | 51,490 ns | zig 2.34x |
| secp256k1_sign_recover | 52,095 ns | 220,150 ns | zig 4.23x |
| address_derivation | 265 ns | 328 ns | zig 1.24x |
| address_from_hex | 17 ns | 11 ns | rs 1.55x |
| checksum_address | 304 ns | 341 ns | zig 1.12x |
| abi_encode_transfer | 24 ns | 47 ns | zig 1.96x |
| abi_encode_static | 25 ns | 97 ns | zig 3.88x |
| abi_encode_dynamic | 171 ns | 337 ns | zig 1.97x |
| abi_decode_uint256 | 14 ns | 51 ns | zig 3.64x |
| abi_decode_dynamic | 32 ns | 269 ns | zig 8.41x |
| rlp_encode_eip1559_tx | 3 ns | 73 ns | zig 24.33x |
| rlp_decode_u256 | 7 ns | 7 ns | tie |
| u256_add | 4 ns | 4 ns | tie |
| u256_mul | 5 ns | 10 ns | zig 2.00x |
| u256_div | 7 ns | 24 ns | zig 3.43x |
| u256_uniswapv2_amount_out | 24 ns | 24 ns | tie |
| u256_mulDiv | 17 ns | 31 ns | zig 1.82x |
| u256_uniswapv4_swap | 45 ns | 42 ns | rs 1.07x |
| hex_encode_32b | 26 ns | 24 ns | rs 1.08x |
| hex_decode_32b | 24 ns | 28 ns | zig 1.17x |
| tx_hash_eip1559 | 271 ns | 361 ns | zig 1.33x |
| Count | |
|---|---|
| eth-zig wins | 18 |
| alloy.rs wins | 5 |
| Tied | 3 |
| Optimization | Impact |
|---|---|
| bitcoin-core/secp256k1 C backend (vendored) | secp256k1_sign: 2.34x faster than alloy; sign_recover: 4.23x faster |
| Lane-complementing Keccak-f[1600] (XKCP opt64) | keccak256_32b: 1.21x faster than alloy (larger inputs are now within ~2% of alloy 1.6) |
| Streamlined mulDiv with native u128 division | mulDiv: 1.82x faster than alloy (17ns vs 31ns) |
Half-word division (div128by64) |
u256_div: 7ns, 3.43x faster than alloy |
| mulWide + divWide (Knuth D for 512-bit) | Replaces 256-iteration binary long division with ~4-iteration Knuth D |
| FixedBufferAllocator in benchmarks | Eliminates allocator overhead for ABI/RLP/TX benchmarks |
| Vendored bitcoin-core/secp256k1 + XKCP keccak | Best-in-class C backends compiled by zig build |
| Custom criterion-style harness | Accurate timing in the sub-25ns regime; zbench had ~25ns floor on macOS |
| Benchmark | Gap | Root Cause |
|---|---|---|
| address_from_hex | 1.55x | alloy uses SIMD hex parsing; eth.zig uses scalar loop |
| hex_encode_32b | 1.08x | alloy's const-hex crate uses vectorized encoding |
| u256_uniswapv4_swap | 1.07x | ruint's U512 widening mul/div edges out eth.zig on this path |
| keccak256_256b | 1.00x | within measurement noise (~0.4%) |
| keccak256_1kb | 1.02x | within measurement noise (~2%) |
# Full comparison (requires Zig, Rust, Python 3)
bash bench/compare.sh
# eth-zig benchmarks only
zig build bench
# alloy benchmarks only
(cd bench/alloy-bench && cargo bench --bench eth_comparison)