optimization for reading peak hashes from backend file (#3575)

This commit is contained in:
Antioch Peverell 2021-02-24 16:17:28 +00:00 committed by GitHub
parent 7487ffd75b
commit adddff9155
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 43 additions and 8 deletions

View file

@ -46,6 +46,11 @@ pub trait Backend<T: PMMRable> {
/// (ignoring the remove log). /// (ignoring the remove log).
fn get_from_file(&self, position: u64) -> Option<Hash>; fn get_from_file(&self, position: u64) -> Option<Hash>;
/// Get hash for peak pos.
/// Optimized for reading peak hashes rather than arbitrary pos hashes.
/// Peaks can be assumed to not be compacted.
fn get_peak_from_file(&self, position: u64) -> Option<Hash>;
/// Get a Data Element by original insertion position /// Get a Data Element by original insertion position
/// (ignoring the remove log). /// (ignoring the remove log).
fn get_data_from_file(&self, position: u64) -> Option<T::E>; fn get_data_from_file(&self, position: u64) -> Option<T::E>;

View file

@ -39,6 +39,11 @@ pub trait ReadablePMMR {
/// Get the hash from the underlying MMR file (ignores the remove log). /// Get the hash from the underlying MMR file (ignores the remove log).
fn get_from_file(&self, pos: u64) -> Option<Hash>; fn get_from_file(&self, pos: u64) -> Option<Hash>;
/// Get the hash for the provided peak pos.
/// Optimized for reading peak hashes rather than arbitrary pos hashes.
/// Peaks can be assumed to not be compacted.
fn get_peak_from_file(&self, pos: u64) -> Option<Hash>;
/// Get the data element at provided position in the MMR (ignores the remove log). /// Get the data element at provided position in the MMR (ignores the remove log).
fn get_data_from_file(&self, pos: u64) -> Option<Self::Item>; fn get_data_from_file(&self, pos: u64) -> Option<Self::Item>;
@ -84,11 +89,7 @@ pub trait ReadablePMMR {
fn peaks(&self) -> Vec<Hash> { fn peaks(&self) -> Vec<Hash> {
peaks(self.unpruned_size()) peaks(self.unpruned_size())
.into_iter() .into_iter()
.filter_map(move |pi| { .filter_map(move |pi| self.get_peak_from_file(pi))
// here we want to get from underlying hash file
// as the pos *may* have been "removed"
self.get_from_file(pi)
})
.collect() .collect()
} }
@ -98,7 +99,7 @@ pub trait ReadablePMMR {
let mut res = peaks(self.unpruned_size()) let mut res = peaks(self.unpruned_size())
.into_iter() .into_iter()
.filter(|&x| x < peak_pos) .filter(|&x| x < peak_pos)
.filter_map(|x| self.get_from_file(x)) .filter_map(|x| self.get_peak_from_file(x))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if let Some(rhs) = rhs { if let Some(rhs) = rhs {
res.push(rhs); res.push(rhs);
@ -227,7 +228,7 @@ where
let left_sibling = pos + 1 - 2 * peak; let left_sibling = pos + 1 - 2 * peak;
let left_hash = self let left_hash = self
.backend .backend
.get_from_file(left_sibling) .get_peak_from_file(left_sibling)
.ok_or("missing left sibling in tree, should not have been pruned")?; .ok_or("missing left sibling in tree, should not have been pruned")?;
peak *= 2; peak *= 2;
pos += 1; pos += 1;
@ -414,6 +415,14 @@ where
} }
} }
fn get_peak_from_file(&self, pos: u64) -> Option<Hash> {
if pos > self.last_pos {
None
} else {
self.backend.get_peak_from_file(pos)
}
}
fn get_data_from_file(&self, pos: u64) -> Option<Self::Item> { fn get_data_from_file(&self, pos: u64) -> Option<Self::Item> {
if pos > self.last_pos { if pos > self.last_pos {
None None

View file

@ -148,6 +148,14 @@ where
} }
} }
fn get_peak_from_file(&self, pos: u64) -> Option<Hash> {
if pos > self.last_pos {
None
} else {
self.backend.get_peak_from_file(pos)
}
}
fn get_data_from_file(&self, pos: u64) -> Option<Self::Item> { fn get_data_from_file(&self, pos: u64) -> Option<Self::Item> {
if pos > self.last_pos { if pos > self.last_pos {
None None

View file

@ -64,6 +64,10 @@ impl<T: PMMRable> Backend<T> for VecBackend<T> {
self.hashes.get(idx).cloned() self.hashes.get(idx).cloned()
} }
fn get_peak_from_file(&self, position: u64) -> Option<Hash> {
self.get_from_file(position)
}
fn get_data_from_file(&self, position: u64) -> Option<T::E> { fn get_data_from_file(&self, position: u64) -> Option<T::E> {
if let Some(data) = &self.data { if let Some(data) = &self.data {
let idx = usize::try_from(pmmr::n_leaves(position).saturating_sub(1)) let idx = usize::try_from(pmmr::n_leaves(position).saturating_sub(1))

View file

@ -94,6 +94,11 @@ impl<T: PMMRable> Backend<T> for PMMRBackend<T> {
self.hash_file.read(position - shift) self.hash_file.read(position - shift)
} }
fn get_peak_from_file(&self, position: u64) -> Option<Hash> {
let shift = self.prune_list.get_shift(position);
self.hash_file.read(position - shift)
}
fn get_data_from_file(&self, position: u64) -> Option<T::E> { fn get_data_from_file(&self, position: u64) -> Option<T::E> {
if !pmmr::is_leaf(position) { if !pmmr::is_leaf(position) {
return None; return None;
@ -284,11 +289,15 @@ impl<T: PMMRable> PMMRBackend<T> {
self.prune_list.is_pruned_root(pos) self.prune_list.is_pruned_root(pos)
} }
// Check if pos is pruned but not a pruned root itself.
// Checking for pruned root is faster so we do this check first.
// We can do a fast initial check as well -
// if its in the current leaf_set then we know it is not compacted.
fn is_compacted(&self, pos: u64) -> bool { fn is_compacted(&self, pos: u64) -> bool {
if self.leaf_set.includes(pos) { if self.leaf_set.includes(pos) {
return false; return false;
} }
self.is_pruned(pos) && !self.is_pruned_root(pos) !self.is_pruned_root(pos) && self.is_pruned(pos)
} }
/// Number of hashes in the PMMR stored by this backend. Only produces the /// Number of hashes in the PMMR stored by this backend. Only produces the