Skip to content

Commit

Permalink
perf: use precomputed zero hashes in process_subtree
Browse files Browse the repository at this point in the history
  • Loading branch information
estensen committed Dec 6, 2024
1 parent 74acc3d commit 024a85e
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 1 deletion.
2 changes: 2 additions & 0 deletions ssz-rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
20 changes: 19 additions & 1 deletion ssz-rs/src/merkleization/merkleize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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<u8>) {
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 024a85e

Please sign in to comment.