From 024a85ec724039c1f82ced433ea07d010fe7a066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20Anda=20Estensen?= Date: Fri, 6 Dec 2024 12:10:25 +0100 Subject: [PATCH] perf: use precomputed zero hashes in process_subtree --- ssz-rs/Cargo.toml | 2 ++ ssz-rs/src/merkleization/merkleize.rs | 20 +++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/ssz-rs/Cargo.toml b/ssz-rs/Cargo.toml index 36f11cea..ec56eb38 100644 --- a/ssz-rs/Cargo.toml +++ b/ssz-rs/Cargo.toml @@ -19,6 +19,8 @@ serde = ["dep:serde", "alloy-primitives/serde"] [dependencies] bitvec = { version = "1.0.0", default-features = false, features = ["alloc"] } +lazy_static = "1.5" +once_cell = "1.20" rayon = "1.10" ssz_rs_derive = { path = "../ssz-rs-derive", version = "0.9.0" } sha2 = { version = "0.9.8", default-features = false } diff --git a/ssz-rs/src/merkleization/merkleize.rs b/ssz-rs/src/merkleization/merkleize.rs index 7efb8899..54ccd54a 100644 --- a/ssz-rs/src/merkleization/merkleize.rs +++ b/ssz-rs/src/merkleization/merkleize.rs @@ -7,6 +7,7 @@ use crate::{ }; #[cfg(feature = "serde")] use alloy_primitives::hex::FromHex; +use once_cell::sync::Lazy; // The generalized index for the root of the "decorated" type in any Merkleized type that supports // decoration. @@ -25,6 +26,13 @@ pub trait HashTreeRoot { } } +/// Precomputed hash of two 32-byte zero arrays concatenated. +static ZERO_LEAF_HASH: Lazy<[u8; BYTES_PER_CHUNK]> = Lazy::new(|| { + let left_zero = [0u8; BYTES_PER_CHUNK]; + let right_zero = [0u8; BYTES_PER_CHUNK]; + hash_chunks(&left_zero, &right_zero) +}); + // Ensures `buffer` can be exactly broken up into `BYTES_PER_CHUNK` chunks of bytes // via padding any partial chunks at the end of `buffer` pub fn pack_bytes(buffer: &mut Vec) { @@ -464,8 +472,18 @@ fn process_subtree(buffer: &mut [u8], size: usize) { let left = &buffer[i * BYTES_PER_CHUNK..(i + 1) * BYTES_PER_CHUNK]; let right = &buffer[(i + 1) * BYTES_PER_CHUNK..(i + 2) * BYTES_PER_CHUNK]; + // Check if both left and right are all zero + let left_is_zero = left.iter().all(|&byte| byte == 0); + let right_is_zero = right.iter().all(|&byte| byte == 0); + // Compute parent hash - let hash = hash_chunks(left, right); + let hash = if left_is_zero && right_is_zero { + // Use the precomputed zero hash + *ZERO_LEAF_HASH + } else { + // Compute the hash normally + hash_chunks(left, right) + }; // Store at parent position (i/2) // SAFETY: checked subtraction is unnecessary, as i >= 1; qed