From 5900a7e1be6dda83be807bf26e0528ef2b496355 Mon Sep 17 00:00:00 2001 From: joshieDo <93316087+joshieDo@users.noreply.github.com> Date: Sat, 1 Apr 2023 01:18:20 +0800 Subject: [PATCH] perf(db): `decode` and `decompress` directly from borrowed data instead (#2050) --- crates/storage/codecs/src/lib.rs | 6 +++-- crates/storage/db/Cargo.toml | 2 +- crates/storage/db/src/abstraction/table.rs | 6 ++--- .../storage/db/src/tables/codecs/compact.rs | 4 +-- crates/storage/db/src/tables/codecs/scale.rs | 5 ++-- .../storage/db/src/tables/models/accounts.rs | 17 +++++-------- .../db/src/tables/models/integer_list.rs | 6 ++--- crates/storage/db/src/tables/models/mod.rs | 21 ++++++++-------- .../db/src/tables/models/sharded_key.rs | 14 +++++------ .../src/tables/models/storage_sharded_key.rs | 15 ++++++----- crates/storage/db/src/tables/utils.rs | 25 +++++++++++++------ crates/storage/libmdbx-rs/Cargo.toml | 4 +++ crates/storage/libmdbx-rs/src/codec.rs | 22 ++++++++++++---- 13 files changed, 84 insertions(+), 63 deletions(-) diff --git a/crates/storage/codecs/src/lib.rs b/crates/storage/codecs/src/lib.rs index b5344075c..f06bf856c 100644 --- a/crates/storage/codecs/src/lib.rs +++ b/crates/storage/codecs/src/lib.rs @@ -96,8 +96,9 @@ where } fn from_compact(mut buf: &[u8], _: usize) -> (Self, &[u8]) { - let mut list = vec![]; let length = buf.get_u16(); + let mut list = Vec::with_capacity(length as usize); + for _ in 0..length { #[allow(unused_assignments)] let mut element = T::default(); @@ -125,8 +126,9 @@ where /// To be used by fixed sized types like `Vec`. fn specialized_from_compact(mut buf: &[u8], len: usize) -> (Self, &[u8]) { - let mut list = vec![]; let length = buf.get_u16(); + let mut list = Vec::with_capacity(length as usize); + for _ in 0..length { #[allow(unused_assignments)] let mut element = T::default(); diff --git a/crates/storage/db/Cargo.toml b/crates/storage/db/Cargo.toml index e6c7647d4..8491d98df 100644 --- a/crates/storage/db/Cargo.toml +++ b/crates/storage/db/Cargo.toml @@ -12,7 +12,7 @@ description = "Staged syncing primitives used in reth." reth-primitives = { path = "../../primitives" } reth-interfaces = { path = "../../interfaces" } reth-codecs = { path = "../codecs" } -reth-libmdbx = { path = "../libmdbx-rs", optional = true } +reth-libmdbx = { path = "../libmdbx-rs", optional = true , features = ["return-borrowed"] } # codecs serde = { version = "1.0.*", default-features = false } diff --git a/crates/storage/db/src/abstraction/table.rs b/crates/storage/db/src/abstraction/table.rs index bf9da9f5a..34458bef6 100644 --- a/crates/storage/db/src/abstraction/table.rs +++ b/crates/storage/db/src/abstraction/table.rs @@ -3,7 +3,7 @@ use crate::{ transaction::{DbTx, DbTxMut}, Error, }; -use reth_primitives::bytes::Bytes; + use serde::Serialize; use std::{ fmt::Debug, @@ -22,7 +22,7 @@ pub trait Compress: Send + Sync + Sized + Debug { /// Trait that will transform the data to be read from the DB. pub trait Decompress: Send + Sync + Sized + Debug { /// Decompresses data coming from the database. - fn decompress>(value: B) -> Result; + fn decompress>(value: B) -> Result; } /// Trait that will transform the data to be saved in the DB. @@ -37,7 +37,7 @@ pub trait Encode: Send + Sync + Sized + Debug { /// Trait that will transform the data to be read from the DB. pub trait Decode: Send + Sync + Sized + Debug { /// Decodes data coming from the database. - fn decode>(value: B) -> Result; + fn decode>(value: B) -> Result; } /// Generic trait that enforces the database key to implement [`Encode`] and [`Decode`]. diff --git a/crates/storage/db/src/tables/codecs/compact.rs b/crates/storage/db/src/tables/codecs/compact.rs index c1689a4d8..e826971f7 100644 --- a/crates/storage/db/src/tables/codecs/compact.rs +++ b/crates/storage/db/src/tables/codecs/compact.rs @@ -23,8 +23,8 @@ macro_rules! impl_compression_for_compact { impl Decompress for $name { - fn decompress>(value: B) -> Result<$name, Error> { - let value = value.into(); + fn decompress>(value: B) -> Result<$name, Error> { + let value = value.as_ref(); let (obj, _) = Compact::from_compact(&value, value.len()); Ok(obj) } diff --git a/crates/storage/db/src/tables/codecs/scale.rs b/crates/storage/db/src/tables/codecs/scale.rs index 0ff87197d..5cbc18cf4 100644 --- a/crates/storage/db/src/tables/codecs/scale.rs +++ b/crates/storage/db/src/tables/codecs/scale.rs @@ -2,7 +2,6 @@ use crate::{ table::{Compress, Decompress}, Error, }; -use parity_scale_codec::decode_from_bytes; use reth_primitives::*; mod sealed { @@ -27,8 +26,8 @@ impl Decompress for T where T: ScaleValue + parity_scale_codec::Decode + Sync + Send + std::fmt::Debug, { - fn decompress>(value: B) -> Result { - decode_from_bytes(value.into()).map_err(|_| Error::DecodeError) + fn decompress>(value: B) -> Result { + parity_scale_codec::Decode::decode(&mut value.as_ref()).map_err(|_| Error::DecodeError) } } diff --git a/crates/storage/db/src/tables/models/accounts.rs b/crates/storage/db/src/tables/models/accounts.rs index 07633818b..2a3d77b33 100644 --- a/crates/storage/db/src/tables/models/accounts.rs +++ b/crates/storage/db/src/tables/models/accounts.rs @@ -6,10 +6,7 @@ use crate::{ Error, }; use reth_codecs::Compact; -use reth_primitives::{ - bytes::{BufMut, Bytes}, - Account, Address, TransitionId, -}; +use reth_primitives::{bytes::BufMut, Account, Address, TransitionId}; use serde::{Deserialize, Serialize}; /// Account as it is saved inside [`AccountChangeSet`][crate::tables::AccountChangeSet]. @@ -89,12 +86,10 @@ impl Encode for TransitionIdAddress { } impl Decode for TransitionIdAddress { - fn decode>(value: B) -> Result { - let value: Bytes = value.into(); - - let num = - u64::from_be_bytes(value.as_ref()[..8].try_into().map_err(|_| Error::DecodeError)?); - let hash = Address::from_slice(&value.slice(8..)); + fn decode>(value: B) -> Result { + let value = value.as_ref(); + let num = u64::from_be_bytes(value[..8].try_into().map_err(|_| Error::DecodeError)?); + let hash = Address::from_slice(&value[8..]); Ok(TransitionIdAddress((num, hash))) } @@ -121,7 +116,7 @@ mod test { let encoded = Encode::encode(key); assert_eq!(encoded, bytes); - let decoded: TransitionIdAddress = Decode::decode(encoded.to_vec()).unwrap(); + let decoded: TransitionIdAddress = Decode::decode(encoded).unwrap(); assert_eq!(decoded, key); } diff --git a/crates/storage/db/src/tables/models/integer_list.rs b/crates/storage/db/src/tables/models/integer_list.rs index 6293b84c8..07eacdf6a 100644 --- a/crates/storage/db/src/tables/models/integer_list.rs +++ b/crates/storage/db/src/tables/models/integer_list.rs @@ -4,7 +4,7 @@ use crate::{ table::{Compress, Decompress}, Error, }; -use reth_primitives::{bytes::Bytes, IntegerList}; +use reth_primitives::IntegerList; impl Compress for IntegerList { type Compressed = Vec; @@ -15,7 +15,7 @@ impl Compress for IntegerList { } impl Decompress for IntegerList { - fn decompress>(value: B) -> Result { - IntegerList::from_bytes(&value.into()).map_err(|_| Error::DecodeError) + fn decompress>(value: B) -> Result { + IntegerList::from_bytes(value.as_ref()).map_err(|_| Error::DecodeError) } } diff --git a/crates/storage/db/src/tables/models/mod.rs b/crates/storage/db/src/tables/models/mod.rs index 5d98e7025..0329d0f20 100644 --- a/crates/storage/db/src/tables/models/mod.rs +++ b/crates/storage/db/src/tables/models/mod.rs @@ -14,7 +14,7 @@ use crate::{ table::{Decode, Encode}, Error, }; -use reth_primitives::{bytes::Bytes, Address, H256}; +use reth_primitives::{Address, H256}; /// Macro that implements [`Encode`] and [`Decode`] for uint types. macro_rules! impl_uints { @@ -31,8 +31,7 @@ macro_rules! impl_uints { impl Decode for $name { - fn decode>(value: B) -> Result { - let value: Bytes = value.into(); + fn decode>(value: B) -> Result { Ok( $name::from_be_bytes( value.as_ref().try_into().map_err(|_| Error::DecodeError)? @@ -54,8 +53,8 @@ impl Encode for Vec { } impl Decode for Vec { - fn decode>(value: B) -> Result { - Ok(value.into().to_vec()) + fn decode>(value: B) -> Result { + Ok(value.as_ref().to_vec()) } } @@ -67,8 +66,8 @@ impl Encode for Address { } impl Decode for Address { - fn decode>(value: B) -> Result { - Ok(Address::from_slice(&value.into()[..])) + fn decode>(value: B) -> Result { + Ok(Address::from_slice(value.as_ref())) } } impl Encode for H256 { @@ -79,8 +78,8 @@ impl Encode for H256 { } impl Decode for H256 { - fn decode>(value: B) -> Result { - Ok(H256::from_slice(&value.into()[..])) + fn decode>(value: B) -> Result { + Ok(H256::from_slice(value.as_ref())) } } @@ -92,7 +91,7 @@ impl Encode for String { } impl Decode for String { - fn decode>(value: B) -> Result { - String::from_utf8(value.into().to_vec()).map_err(|_| Error::DecodeError) + fn decode>(value: B) -> Result { + String::from_utf8(value.as_ref().to_vec()).map_err(|_| Error::DecodeError) } } diff --git a/crates/storage/db/src/tables/models/sharded_key.rs b/crates/storage/db/src/tables/models/sharded_key.rs index a56b29e0f..867b8e7f4 100644 --- a/crates/storage/db/src/tables/models/sharded_key.rs +++ b/crates/storage/db/src/tables/models/sharded_key.rs @@ -4,7 +4,7 @@ use crate::{ table::{Decode, Encode}, Error, }; -use reth_primitives::{bytes::Bytes, TransitionId}; +use reth_primitives::TransitionId; /// Number of indices in one shard. pub const NUM_OF_INDICES_IN_SHARD: usize = 100; @@ -48,14 +48,14 @@ impl Decode for ShardedKey where T: Decode, { - fn decode>(value: B) -> Result { - let value: Bytes = value.into(); + fn decode>(value: B) -> Result { + let value = value.as_ref(); + let tx_num_index = value.len() - 8; - let highest_tx_number = u64::from_be_bytes( - value.as_ref()[tx_num_index..].try_into().map_err(|_| Error::DecodeError)?, - ); - let key = T::decode(value.slice(..tx_num_index))?; + let highest_tx_number = + u64::from_be_bytes(value[tx_num_index..].try_into().map_err(|_| Error::DecodeError)?); + let key = T::decode(&value[..tx_num_index])?; Ok(ShardedKey::new(key, highest_tx_number)) } diff --git a/crates/storage/db/src/tables/models/storage_sharded_key.rs b/crates/storage/db/src/tables/models/storage_sharded_key.rs index 16a8c0c76..d43ebe564 100644 --- a/crates/storage/db/src/tables/models/storage_sharded_key.rs +++ b/crates/storage/db/src/tables/models/storage_sharded_key.rs @@ -4,7 +4,7 @@ use crate::{ table::{Decode, Encode}, Error, }; -use reth_primitives::{bytes::Bytes, TransitionId, H160, H256}; +use reth_primitives::{TransitionId, H160, H256}; use super::ShardedKey; @@ -44,15 +44,14 @@ impl Encode for StorageShardedKey { } impl Decode for StorageShardedKey { - fn decode>(value: B) -> Result { - let value: Bytes = value.into(); + fn decode>(value: B) -> Result { + let value = value.as_ref(); let tx_num_index = value.len() - 8; - let highest_tx_number = u64::from_be_bytes( - value.as_ref()[tx_num_index..].try_into().map_err(|_| Error::DecodeError)?, - ); - let address = H160::decode(value.slice(..20))?; - let storage_key = H256::decode(value.slice(20..52))?; + let highest_tx_number = + u64::from_be_bytes(value[tx_num_index..].try_into().map_err(|_| Error::DecodeError)?); + let address = H160::decode(&value[..20])?; + let storage_key = H256::decode(&value[20..52])?; Ok(Self { address, sharded_key: ShardedKey::new(storage_key, highest_tx_number) }) } diff --git a/crates/storage/db/src/tables/utils.rs b/crates/storage/db/src/tables/utils.rs index 899a9b0be..a2e50f8d8 100644 --- a/crates/storage/db/src/tables/utils.rs +++ b/crates/storage/db/src/tables/utils.rs @@ -3,7 +3,7 @@ use crate::{ table::{Decode, Decompress, Table}, Error, }; -use reth_primitives::bytes::Bytes; + use std::borrow::Cow; #[macro_export] @@ -49,10 +49,15 @@ where T::Key: Decode, T::Value: Decompress, { - Ok(( - Decode::decode(Bytes::from(kv.0.into_owned()))?, - Decompress::decompress(Bytes::from(kv.1.into_owned()))?, - )) + let key = match kv.0 { + Cow::Borrowed(k) => Decode::decode(k)?, + Cow::Owned(k) => Decode::decode(k)?, + }; + let value = match kv.1 { + Cow::Borrowed(v) => Decompress::decompress(v)?, + Cow::Owned(v) => Decompress::decompress(v)?, + }; + Ok((key, value)) } /// Helper function to decode only a value from a `(key, value)` pair. @@ -60,7 +65,10 @@ pub(crate) fn decode_value<'a, T>(kv: (Cow<'a, [u8]>, Cow<'a, [u8]>)) -> Result< where T: Table, { - Decompress::decompress(Bytes::from(kv.1.into_owned())) + Ok(match kv.1 { + Cow::Borrowed(v) => Decompress::decompress(v)?, + Cow::Owned(v) => Decompress::decompress(v)?, + }) } /// Helper function to decode a value. It can be a key or subkey. @@ -68,5 +76,8 @@ pub(crate) fn decode_one(value: Cow<'_, [u8]>) -> Result where T: Table, { - Decompress::decompress(Bytes::from(value.into_owned())) + Ok(match value { + Cow::Borrowed(v) => Decompress::decompress(v)?, + Cow::Owned(v) => Decompress::decompress(v)?, + }) } diff --git a/crates/storage/libmdbx-rs/Cargo.toml b/crates/storage/libmdbx-rs/Cargo.toml index a081f0f5f..6c16fd85a 100644 --- a/crates/storage/libmdbx-rs/Cargo.toml +++ b/crates/storage/libmdbx-rs/Cargo.toml @@ -23,6 +23,10 @@ ffi = { package = "reth-mdbx-sys", path = "./mdbx-sys" } lifetimed-bytes = { version = "0.1", optional = true } +[features] +default = [] +return-borrowed = [] + [dev-dependencies] pprof = { version = "0.11", features = ["flamegraph", "frame-pointer", "criterion"] } criterion = "0.4" diff --git a/crates/storage/libmdbx-rs/src/codec.rs b/crates/storage/libmdbx-rs/src/codec.rs index 03ead5e48..e42d87ab1 100644 --- a/crates/storage/libmdbx-rs/src/codec.rs +++ b/crates/storage/libmdbx-rs/src/codec.rs @@ -1,4 +1,4 @@ -use crate::{error::mdbx_result, Error, TransactionKind}; +use crate::{Error, TransactionKind}; use derive_more::*; use std::{borrow::Cow, slice}; @@ -29,14 +29,26 @@ impl<'tx> TableObject<'tx> for Cow<'tx, [u8]> { #[doc(hidden)] unsafe fn decode_val( - txn: *const ffi::MDBX_txn, + _txn: *const ffi::MDBX_txn, data_val: &ffi::MDBX_val, ) -> Result { - let is_dirty = (!K::ONLY_CLEAN) && mdbx_result(ffi::mdbx_is_dirty(txn, data_val.iov_base))?; - let s = slice::from_raw_parts(data_val.iov_base as *const u8, data_val.iov_len); - Ok(if is_dirty { Cow::Owned(s.to_vec()) } else { Cow::Borrowed(s) }) + #[cfg(feature = "return-borrowed")] + { + Ok(Cow::Borrowed(s)) + } + + #[cfg(not(feature = "return-borrowed"))] + { + let is_dirty = (!K::ONLY_CLEAN) && + crate::error::mdbx_result::mdbx_result(ffi::mdbx_is_dirty( + _txn, + data_val.iov_base, + ))?; + + Ok(if is_dirty { Cow::Owned(s.to_vec()) } else { Cow::Borrowed(s) }) + } } }