From 1ce0f86599662c4c475408f8a6ef9df07ae6a1da Mon Sep 17 00:00:00 2001 From: Koko Bhadra Date: Wed, 10 Jun 2026 11:15:14 -0400 Subject: [PATCH] Refresh benchmark comparison against current alloy 1.6/2.0 The published numbers dated from March against alloy 0.8/0.6. Upgrade the bench harness (alloy-primitives/sol-types/dyn-abi 1.6.0, alloy-consensus/signer/network 2.0.5, alloy-rlp 0.3.15; no source changes needed) and re-run the full comparison on Zig 0.16.0. New honest score: eth-zig 18/26, alloy 5/26, 3 tied (was 23/26 -- alloy improved on mid-size keccak, hex/address parsing, and the uniswapv4 swap path). Biggest verified wins: RLP EIP-1559 encode 24.33x, ABI dynamic decode 8.41x, secp256k1 recovery 4.23x, signing 2.34x, u256 div 3.43x. Updated every public claim: README hero/tables, docs benchmarks, introduction, comparison, faq pages, the SEO description in layout.tsx (still said 19/26), and bench/RESULTS.md (full new table, versions, date, machine; March run kept as a dated historical note). --- CHANGELOG.md | 3 ++ README.md | 51 +++++++++---------- bench/RESULTS.md | 78 ++++++++++++++++-------------- bench/alloy-bench/Cargo.toml | 14 +++--- docs/content/docs/benchmarks.mdx | 71 ++++++++++++++------------- docs/content/docs/comparison.mdx | 18 +++---- docs/content/docs/faq.mdx | 6 +-- docs/content/docs/introduction.mdx | 23 ++++----- docs/src/app/layout.tsx | 2 +- 9 files changed, 139 insertions(+), 127 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0287705..6d62c3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed +- Benchmark comparison refreshed against current alloy crates (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); bench/alloy-bench migrated from alloy-primitives 0.8 / alloy 0.6. New score: eth.zig wins 18/26 (alloy improved Keccak on larger inputs, hex encoding, RLP u256 decoding, and UniswapV4-style swap math) + ### Added - `mev_share` module: MEV-Share client mirroring `mev-share-client-ts` -- `sendTransaction` (eth_sendPrivateTransaction via `flashbots.Relay`), `simulateBundle` (mev_simBundle with `SimBundleOpts`/`SimBundleResult`), blocking SSE event stream subscription (`MevShareClient.on` with `PendingEvent`/`PendingTransaction`/`PendingBundle` and pure `parseEventData`), and `getEventHistory` (GET /api/v1/history) (#34) - `eth_sendPrivateTransaction` (MEV-Share private transactions) on `flashbots.Relay`: submit a single private transaction with hint preferences (calldata, contract_address, logs, function_selector), builder selection, fast mode, and max inclusion block (#40) diff --git a/README.md b/README.md index 2164aaf..71c86d8 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![Zig](https://img.shields.io/badge/Zig-%E2%89%A5%200.16.0-orange)](https://ziglang.org/) -**The fastest Ethereum library.** Beats Rust's alloy.rs on 23 out of 26 benchmarks. +**The fastest Ethereum library.** Beats Rust's alloy.rs on 18 out of 26 benchmarks. A complete Ethereum client library written in Zig -- ABI encoding, RLP serialization, secp256k1 signing, Keccak-256 hashing, HD wallets, ERC-20/721 tokens, JSON-RPC, ENS, and more. @@ -13,7 +13,7 @@ A complete Ethereum client library written in Zig -- ABI encoding, RLP serializa ## Why eth.zig? -**Fastest Ethereum library** -- eth.zig [beats alloy.rs](bench/RESULTS.md) (Rust's leading Ethereum library, backed by Paradigm) on **23 out of 26 benchmarks**. ECDSA signing 2.09x faster, ABI decoding up to 7.65x, secp256k1 recovery 3.23x, mulDiv 1.76x, u256 division 5.57x, Keccak hashing up to 1.26x. See the [full results](bench/RESULTS.md). +**Fastest Ethereum library** -- eth.zig [beats alloy.rs](bench/RESULTS.md) (Rust's leading Ethereum library, backed by Paradigm) on **18 out of 26 benchmarks**, measured 2026-06-10 against current alloy 1.6/2.0. RLP transaction encoding 24.33x faster, ABI decoding up to 8.41x, secp256k1 recovery 4.23x, ECDSA signing 2.34x, u256 division 3.43x, mulDiv 1.82x. See the [full results](bench/RESULTS.md). **Comptime-first** -- Function selectors and event topics are computed at compile time with zero runtime cost. The compiler does the hashing so your program doesn't have to. @@ -21,26 +21,27 @@ A complete Ethereum client library written in Zig -- ABI encoding, RLP serializa ## Performance vs alloy.rs -eth.zig wins **23/26 benchmarks** against [alloy.rs](https://alloy.rs). Measured on Apple Silicon, `ReleaseFast` (Zig) vs `--release` (Rust). Criterion-style harness with 0.5s warmup and 2s measurement. +eth.zig wins **18/26 benchmarks** against [alloy.rs](https://alloy.rs) (alloy-primitives 1.6.0, alloy-consensus 2.0.5; run 2026-06-10). Measured on Apple Silicon, `ReleaseFast` (Zig) vs `--release` (Rust). Criterion-style harness with 0.5s warmup and 2s measurement. | Operation | eth.zig | alloy.rs | Winner | |-----------|---------|----------|--------| -| secp256k1 sign | 24,481 ns | 51,286 ns | **zig 2.09x** | -| secp256k1 sign+recover | 67,816 ns | 218,720 ns | **zig 3.23x** | -| Keccak-256 (32B) | 268 ns | 337 ns | **zig 1.26x** | -| Keccak-256 (4KB) | 7,886 ns | 9,238 ns | **zig 1.17x** | -| ABI encode (static) | 24 ns | 100 ns | **zig 4.17x** | -| ABI encode (dynamic) | 176 ns | 325 ns | **zig 1.85x** | -| ABI decode (uint256) | 16 ns | 51 ns | **zig 3.19x** | -| ABI decode (dynamic) | 34 ns | 260 ns | **zig 7.65x** | -| u256 mulDiv (512-bit) | 17 ns | 30 ns | **zig 1.76x** | -| u256 division | 7 ns | 39 ns | **zig 5.57x** | -| u256 multiply | 4 ns | 9 ns | **zig 2.25x** | -| UniswapV2 getAmountOut | 21 ns | 27 ns | **zig 1.29x** | -| UniswapV4 swap | 42 ns | 47 ns | **zig 1.12x** | -| TX hash (EIP-1559) | 333 ns | 407 ns | **zig 1.22x** | - -alloy.rs wins on address hex parsing (1.36x -- SIMD) and RLP u256 decoding (1.40x). See [full results](bench/RESULTS.md). +| secp256k1 sign | 22,033 ns | 51,490 ns | **zig 2.34x** | +| secp256k1 sign+recover | 52,095 ns | 220,150 ns | **zig 4.23x** | +| Keccak-256 (32B) | 263 ns | 319 ns | **zig 1.21x** | +| Keccak-256 (4KB) | 7,732 ns | 7,838 ns | **zig 1.01x** | +| 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 (EIP-1559 tx) | 3 ns | 73 ns | **zig 24.33x** | +| u256 mulDiv (512-bit) | 17 ns | 31 ns | **zig 1.82x** | +| u256 division | 7 ns | 24 ns | **zig 3.43x** | +| u256 multiply | 5 ns | 10 ns | **zig 2.00x** | +| UniswapV2 getAmountOut | 24 ns | 24 ns | tie | +| UniswapV4 swap | 45 ns | 42 ns | rs 1.07x | +| TX hash (EIP-1559) | 271 ns | 361 ns | **zig 1.33x** | + +alloy.rs wins on address hex parsing (1.55x -- SIMD), hex encoding (1.08x), UniswapV4 swap (1.07x), and Keccak at 256B/1KB inputs (within ~2%, measurement noise). See [full results](bench/RESULTS.md). ## Quick Start @@ -311,12 +312,12 @@ cd examples && zig build && ./zig-out/bin/01_derive_address | Category | eth.zig | alloy.rs | |----------|---------|----------| -| Benchmarks won | **23/26** | 2/26 | -| secp256k1 signing | Faster (2.09-3.23x) | -- | -| ABI encoding/decoding | Faster (1.85-7.65x) | -- | -| Hashing (Keccak) | Faster (1.17-1.26x) | -- | -| u256 arithmetic | Faster on all ops (1.12-5.57x) | -- | -| Hex operations | Faster (1.05-1.22x) | Faster on hex parsing (1.36x, SIMD) | +| Benchmarks won | **18/26** | 5/26 | +| secp256k1 signing | Faster (2.34-4.23x) | -- | +| ABI encoding/decoding | Faster (1.96-8.41x) | -- | +| Hashing (Keccak) | Faster on small inputs (1.19-1.21x) | Within ~2% on 256B-1KB | +| u256 arithmetic | Faster on mul/div/mulDiv (1.82-3.43x) | UniswapV4 swap (1.07x) | +| Hex operations | Faster decoding (1.17x) | Faster encoding (1.08x) and parsing (1.55x, SIMD) | ### Features vs Zabi (Zig) diff --git a/bench/RESULTS.md b/bench/RESULTS.md index 5ff5524..8857263 100644 --- a/bench/RESULTS.md +++ b/bench/RESULTS.md @@ -2,58 +2,59 @@ Zig vs Rust -- a head-to-head performance comparison of [eth.zig](https://github.com/StrobeLabs/eth.zig) and [alloy.rs](https://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 23/26 | alloy.rs wins 2/26 | tied 1/26** +**Score: eth.zig wins 18/26 | alloy.rs wins 5/26 | tied 3/26** -Benchmarks run on Apple Silicon with `ReleaseFast` (Zig) vs `--release` (Cargo). 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). +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. ## Full Comparison | Benchmark | eth-zig | alloy.rs | Winner | |---|---|---|---| -| keccak256_empty | 275 ns | 333 ns | **zig 1.21x** | -| keccak256_32b | 268 ns | 337 ns | **zig 1.26x** | -| keccak256_256b | 536 ns | 640 ns | **zig 1.19x** | -| keccak256_1kb | 2,052 ns | 2,432 ns | **zig 1.19x** | -| keccak256_4kb | 7,886 ns | 9,238 ns | **zig 1.17x** | -| secp256k1_sign | 24,481 ns | 51,286 ns | **zig 2.09x** | -| secp256k1_sign_recover | 67,816 ns | 218,720 ns | **zig 3.23x** | -| address_derivation | 262 ns | 370 ns | **zig 1.41x** | -| address_from_hex | 15 ns | 11 ns | rs 1.36x | -| checksum_address | 312 ns | 450 ns | **zig 1.44x** | -| abi_encode_transfer | 25 ns | 61 ns | **zig 2.44x** | -| abi_encode_static | 24 ns | 100 ns | **zig 4.17x** | -| abi_encode_dynamic | 176 ns | 325 ns | **zig 1.85x** | -| abi_decode_uint256 | 16 ns | 51 ns | **zig 3.19x** | -| abi_decode_dynamic | 34 ns | 260 ns | **zig 7.65x** | -| rlp_encode_eip1559_tx | 57 ns | 72 ns | **zig 1.26x** | -| rlp_decode_u256 | 14 ns | 10 ns | rs 1.40x | +| 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 | 4 ns | 9 ns | **zig 2.25x** | -| u256_div | 7 ns | 39 ns | **zig 5.57x** | -| u256_uniswapv2_amount_out | 21 ns | 27 ns | **zig 1.29x** | -| u256_mulDiv | 17 ns | 30 ns | **zig 1.76x** | -| u256_uniswapv4_swap | 42 ns | 47 ns | **zig 1.12x** | -| hex_encode_32b | 22 ns | 23 ns | **zig 1.05x** | -| hex_decode_32b | 23 ns | 28 ns | **zig 1.22x** | -| tx_hash_eip1559 | 333 ns | 407 ns | **zig 1.22x** | +| 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** | ## Score Summary | | Count | |---|---| -| eth-zig wins | 23 | -| alloy.rs wins | 2 | -| Tied | 1 | +| eth-zig wins | 18 | +| alloy.rs wins | 5 | +| Tied | 3 | ## Key Optimizations | Optimization | Impact | |---|---| -| bitcoin-core/secp256k1 C backend (vendored) | secp256k1_sign: 2.09x faster than alloy; sign_recover: 3.23x faster | -| Lane-complementing Keccak-f[1600] (XKCP opt64) | keccak256_4kb: 1.17x faster than alloy | -| Streamlined mulDiv with native u128 division | mulDiv: 1.76x faster than alloy (17ns vs 30ns) | -| U256Limb limb-native arithmetic | uniswapv2: beats alloy 1.29x | -| Half-word division (`div128by64`) | u256_div: 7ns, 5.57x faster than alloy | +| 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` | @@ -63,8 +64,11 @@ Benchmarks run on Apple Silicon with `ReleaseFast` (Zig) vs `--release` (Cargo). | Benchmark | Gap | Root Cause | |---|---|---| -| address_from_hex | 1.36x | alloy uses SIMD hex parsing; eth.zig uses scalar loop | -| rlp_decode_u256 | 1.40x | alloy's ruint has optimized small-integer RLP decoding | +| 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%) | ## Reproducing diff --git a/bench/alloy-bench/Cargo.toml b/bench/alloy-bench/Cargo.toml index 87b25e9..cc3828b 100644 --- a/bench/alloy-bench/Cargo.toml +++ b/bench/alloy-bench/Cargo.toml @@ -4,15 +4,15 @@ version = "0.1.0" edition = "2021" [dependencies] -alloy-primitives = { version = "0.8", features = ["rlp"] } +alloy-primitives = { version = "1.6", features = ["rlp"] } alloy-rlp = "0.3" alloy-rlp-derive = "0.3" -alloy-sol-types = "0.8" -alloy-dyn-abi = "0.8" -alloy-consensus = "0.6" -alloy-signer = "0.6" -alloy-signer-local = "0.6" -alloy-network = "0.6" +alloy-sol-types = "1.6" +alloy-dyn-abi = "1.6" +alloy-consensus = "2.0" +alloy-signer = "2.0" +alloy-signer-local = "2.0" +alloy-network = "2.0" k256 = { version = "0.13", features = ["ecdsa"] } criterion = { version = "0.5", features = ["html_reports"] } diff --git a/docs/content/docs/benchmarks.mdx b/docs/content/docs/benchmarks.mdx index 4b3adfb..df37781 100644 --- a/docs/content/docs/benchmarks.mdx +++ b/docs/content/docs/benchmarks.mdx @@ -5,58 +5,57 @@ description: eth.zig vs alloy.rs -- head-to-head performance comparison across 2 eth.zig vs Rust -- a head-to-head performance comparison of eth.zig and [alloy.rs](https://alloy.rs) across 26 core Ethereum operations. -**Score: eth.zig wins 23/26 | alloy.rs wins 1/26 | tied 2/26** +**Score: eth.zig wins 18/26 | alloy.rs wins 5/26 | tied 3/26** -Benchmarks run on Apple Silicon with `ReleaseFast` (Zig) vs `--release` (Cargo). Custom criterion-style harness with 0.5s warmup, calibrated batch sizes, and 2s measurement window. Both mulDiv benchmarks use true 512-bit intermediate arithmetic. +Run date: 2026-06-10 on Apple Silicon (aarch64-macos) with `ReleaseFast` (Zig) vs `--release` (Cargo). 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. Custom criterion-style harness with 0.5s warmup, calibrated batch sizes, and 2s measurement window. Both mulDiv benchmarks use true 512-bit intermediate arithmetic. ## Full Results | Benchmark | eth.zig | alloy.rs | Winner | |-----------|---------|----------|--------| -| keccak256_empty | 257 ns | 334 ns | **zig 1.30x** | -| keccak256_32b | 259 ns | 336 ns | **zig 1.30x** | -| keccak256_256b | 519 ns | 639 ns | **zig 1.23x** | -| keccak256_1kb | 2,000 ns | 2,494 ns | **zig 1.25x** | -| keccak256_4kb | 7,673 ns | 9,292 ns | **zig 1.21x** | -| secp256k1_sign | 24,609 ns | 51,738 ns | **zig 2.10x** | -| secp256k1_sign_recover | 54,221 ns | 218,790 ns | **zig 4.04x** | -| address_derivation | 262 ns | 363 ns | **zig 1.39x** | -| address_from_hex | 15 ns | 11 ns | rs 1.36x | -| checksum_address | 307 ns | 387 ns | **zig 1.26x** | -| abi_encode_transfer | 25 ns | 55 ns | **zig 2.20x** | -| abi_encode_static | 24 ns | 97 ns | **zig 4.04x** | -| abi_encode_dynamic | 171 ns | 324 ns | **zig 1.89x** | -| abi_decode_uint256 | 16 ns | 50 ns | **zig 3.12x** | -| abi_decode_dynamic | 32 ns | 256 ns | **zig 8.00x** | -| rlp_encode_eip1559_tx | 55 ns | 72 ns | **zig 1.31x** | -| rlp_decode_u256 | 8 ns | 9 ns | **zig 1.12x** | +| 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 | 4 ns | 10 ns | **zig 2.50x** | +| u256_mul | 5 ns | 10 ns | **zig 2.00x** | | u256_div | 7 ns | 24 ns | **zig 3.43x** | -| u256_uniswapv2_amount_out | 21 ns | 24 ns | **zig 1.14x** | -| u256_mulDiv | 17 ns | 29 ns | **zig 1.71x** | -| u256_uniswapv4_swap | 41 ns | 45 ns | **zig 1.10x** | -| hex_encode_32b | 21 ns | 21 ns | tie | -| hex_decode_32b | 23 ns | 28 ns | **zig 1.22x** | -| tx_hash_eip1559 | 328 ns | 402 ns | **zig 1.23x** | +| 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** | ## Score Summary | | Count | |---|---| -| eth.zig wins | 23 | -| alloy.rs wins | 1 | -| Tied | 2 | +| eth.zig wins | 18 | +| alloy.rs wins | 5 | +| Tied | 3 | ## Key Optimizations | Optimization | Impact | |---|---| -| bitcoin-core/secp256k1 C backend (vendored) | secp256k1_sign: 2.10x faster than alloy; sign_recover: 4.04x faster | -| Lane-complementing Keccak-f[1600] (XKCP opt64) | keccak256_4kb: 1.21x faster than alloy | -| Streamlined mulDiv with native u128 division | mulDiv: 1.71x faster than alloy (17ns vs 29ns) | +| 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) | | mulWide + divWide (Knuth D for 512-bit) | Replaces 256-iteration binary long division with ~4-iteration Knuth D | -| U256Limb limb-native arithmetic | uniswapv2: beats alloy 1.14x | | Half-word division (`div128by64`) | u256_div: 7ns, 3.43x faster than alloy | | 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` | @@ -66,7 +65,11 @@ Benchmarks run on Apple Silicon with `ReleaseFast` (Zig) vs `--release` (Cargo). | Benchmark | Gap | Root Cause | |---|---|---| -| address_from_hex | 1.36x | alloy uses SIMD hex parsing; eth.zig uses scalar loop | +| 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%) | ## Reproducing diff --git a/docs/content/docs/comparison.mdx b/docs/content/docs/comparison.mdx index f96e740..a678901 100644 --- a/docs/content/docs/comparison.mdx +++ b/docs/content/docs/comparison.mdx @@ -5,17 +5,17 @@ description: How eth.zig compares to alloy.rs, Zabi, and other Ethereum librarie ## Performance vs alloy.rs (Rust) -[alloy.rs](https://alloy.rs) is the leading Rust Ethereum library, backed by Paradigm. eth.zig outperforms it on 23 of 26 benchmarks. +[alloy.rs](https://alloy.rs) is the leading Rust Ethereum library, backed by Paradigm. eth.zig outperforms it on 18 of 26 benchmarks (alloy-primitives 1.6.0, alloy-consensus 2.0.5; run 2026-06-10). | Category | eth.zig | alloy.rs | |----------|---------|----------| -| Benchmarks won | **23/26** | 1/26 | -| secp256k1 signing | Faster (2.10-4.04x) | -- | -| ABI encoding/decoding | Faster (1.89-8.00x) | -- | -| Hashing (Keccak) | Faster (1.21-1.30x) | -- | -| u256 arithmetic | Faster on all ops (1.10-3.43x) | -- | -| UniswapV4 mulDiv | Faster (1.71x) | -- | -| Hex operations | Faster (1.22x) | Faster on hex parsing (1.36x, SIMD) | +| Benchmarks won | **18/26** | 5/26 | +| secp256k1 signing | Faster (2.34-4.23x) | -- | +| ABI encoding/decoding | Faster (1.96-8.41x) | -- | +| Hashing (Keccak) | Faster on small inputs (1.19-1.21x) | Within ~2% on 256B-1KB | +| u256 arithmetic | Faster on mul/div (2.00-3.43x) | UniswapV4 swap (1.07x) | +| UniswapV4 mulDiv | Faster (1.82x) | -- | +| Hex operations | Faster decoding (1.17x) | Faster encoding (1.08x) and parsing (1.55x, SIMD) | See [full benchmark results](/benchmarks) for details. @@ -39,7 +39,7 @@ See [full benchmark results](/benchmarks) for details. ### Key Differences -**Performance**: eth.zig beats alloy.rs (Rust) on 23 of 26 benchmarks, including secp256k1 signing (2.10x faster), ABI decoding (8.00x), and u256 mulDiv (1.71x). +**Performance**: eth.zig beats alloy.rs (Rust) on 18 of 26 benchmarks, including secp256k1 signing (2.34x faster), ABI decoding (8.41x), and u256 mulDiv (1.82x). **Comptime selectors**: eth.zig computes function selectors and event topics at compile time. This is unique to eth.zig and eliminates runtime hashing overhead. See [Comptime Selectors](/comptime). diff --git a/docs/content/docs/faq.mdx b/docs/content/docs/faq.mdx index 733ea67..90cf06f 100644 --- a/docs/content/docs/faq.mdx +++ b/docs/content/docs/faq.mdx @@ -13,9 +13,9 @@ Zig >= 0.15.2. The library tracks Zig stable releases and is also tested against ## How does secp256k1 signing compare to alloy.rs? -eth.zig wins on secp256k1 signing -- 2.10x faster than alloy.rs for signing and 4.04x faster for sign+recover. +eth.zig wins on secp256k1 signing -- 2.34x faster than alloy.rs for signing and 4.23x faster for sign+recover (measured 2026-06-10 against alloy-primitives 1.6 / alloy 2.0). -eth.zig wins 23 of 26 benchmarks overall. alloy.rs wins only on address hex parsing (1.36x, SIMD). +eth.zig wins 18 of 26 benchmarks overall. alloy.rs wins on address hex parsing (1.55x, SIMD), hex encoding (1.08x), UniswapV4 swap (1.07x), and Keccak-256 at 256B/1KB inputs (within ~2%). ## Does eth.zig support all Ethereum transaction types? @@ -40,7 +40,7 @@ Any EVM-compatible chain works -- these are just convenience configurations. ## Can I use eth.zig for MEV / HFT? -Yes, that's a primary use case. eth.zig beats alloy.rs on 23/26 benchmarks, with the biggest wins in secp256k1 signing (2.10x), ABI decoding (8.00x), u256 arithmetic (up to 3.43x), mulDiv (1.71x), and comptime selectors (zero-cost at runtime). +Yes, that's a primary use case. eth.zig beats alloy.rs on 18/26 benchmarks (alloy 1.6/2.0, run 2026-06-10), with the biggest wins in RLP transaction encoding (24.33x), ABI decoding (8.41x), secp256k1 sign+recover (4.23x), secp256k1 signing (2.34x), u256 division (3.43x), and mulDiv (1.82x), plus comptime selectors (zero-cost at runtime). ## How do I report a security vulnerability? diff --git a/docs/content/docs/introduction.mdx b/docs/content/docs/introduction.mdx index a456155..adf9d82 100644 --- a/docs/content/docs/introduction.mdx +++ b/docs/content/docs/introduction.mdx @@ -1,31 +1,32 @@ --- title: eth.zig -description: The fastest Ethereum library. Beats Rust on 23 of 26 benchmarks. +description: The fastest Ethereum library. Beats Rust on 18 of 26 benchmarks. --- -**The fastest Ethereum library.** Beats alloy.rs on 23 of 26 benchmarks. +**The fastest Ethereum library.** Beats alloy.rs on 18 of 26 benchmarks. eth.zig provides everything you need to interact with Ethereum from Zig -- signing transactions, encoding ABI calls, managing HD wallets, reading ERC-20 tokens, talking to nodes over JSON-RPC, and more. ## Why eth.zig? -- **Faster than Rust** -- eth.zig [beats alloy.rs](/benchmarks) (Rust's leading Ethereum library, backed by Paradigm) on **23 out of 26 benchmarks**, including secp256k1 signing (2.10x), ABI decoding (8.00x), mulDiv (1.71x), Keccak-256 hashing, and all u256 arithmetic. +- **Faster than Rust** -- eth.zig [beats alloy.rs](/benchmarks) (Rust's leading Ethereum library, backed by Paradigm) on **18 out of 26 benchmarks**, including secp256k1 signing (2.34x), ABI decoding (8.41x), mulDiv (1.82x), u256 division (3.43x), and Keccak-256 hashing of small inputs. - **Comptime-first** -- Function selectors and event topics are computed at compile time with zero runtime cost. The compiler does the hashing so your program doesn't have to. ## Performance vs alloy.rs -eth.zig wins **23/26 benchmarks** against [alloy.rs](https://alloy.rs). Measured on Apple Silicon, `ReleaseFast` (Zig) vs `--release` (Rust). +eth.zig wins **18/26 benchmarks** against [alloy.rs](https://alloy.rs) (alloy-primitives 1.6.0, alloy-consensus 2.0.5; run 2026-06-10). Measured on Apple Silicon, `ReleaseFast` (Zig) vs `--release` (Rust). | Operation | eth.zig | alloy.rs | Winner | |-----------|---------|----------|--------| -| secp256k1 sign | 24,609 ns | 51,738 ns | **zig 2.10x** | -| secp256k1 sign+recover | 54,221 ns | 218,790 ns | **zig 4.04x** | -| ABI decode (dynamic) | 32 ns | 256 ns | **zig 8.00x** | -| ABI encode (static) | 24 ns | 97 ns | **zig 4.04x** | +| secp256k1 sign | 22,033 ns | 51,490 ns | **zig 2.34x** | +| secp256k1 sign+recover | 52,095 ns | 220,150 ns | **zig 4.23x** | +| ABI decode (dynamic) | 32 ns | 269 ns | **zig 8.41x** | +| ABI encode (static) | 25 ns | 97 ns | **zig 3.88x** | +| RLP encode (EIP-1559 tx) | 3 ns | 73 ns | **zig 24.33x** | | u256 division | 7 ns | 24 ns | **zig 3.43x** | -| u256 mulDiv (512-bit) | 17 ns | 29 ns | **zig 1.71x** | -| Keccak-256 (32B) | 259 ns | 336 ns | **zig 1.30x** | -| TX hash (EIP-1559) | 328 ns | 402 ns | **zig 1.23x** | +| u256 mulDiv (512-bit) | 17 ns | 31 ns | **zig 1.82x** | +| Keccak-256 (32B) | 263 ns | 319 ns | **zig 1.21x** | +| TX hash (EIP-1559) | 271 ns | 361 ns | **zig 1.33x** | See the [full benchmark results](/benchmarks) for all 26 operations. diff --git a/docs/src/app/layout.tsx b/docs/src/app/layout.tsx index c74fa1a..a087464 100644 --- a/docs/src/app/layout.tsx +++ b/docs/src/app/layout.tsx @@ -7,7 +7,7 @@ const inter = Inter({ subsets: ['latin'], }); -const SEO_DESCRIPTION = 'The fastest Ethereum library. Pure Zig. Zero dependencies. Beats alloy.rs on 19/26 benchmarks.'; +const SEO_DESCRIPTION = 'The fastest Ethereum library. Pure Zig. Zero dependencies. Beats alloy.rs on 18/26 benchmarks.'; const webSiteSchema = JSON.stringify({ '@context': 'https://schema.org',