perf(db): decode and decompress directly from borrowed data instead (#2050)

This commit is contained in:
joshieDo
2023-04-01 01:18:20 +08:00
committed by GitHub
parent e72b2864b2
commit 5900a7e1be
13 changed files with 84 additions and 63 deletions

View File

@ -96,8 +96,9 @@ where
} }
fn from_compact(mut buf: &[u8], _: usize) -> (Self, &[u8]) { fn from_compact(mut buf: &[u8], _: usize) -> (Self, &[u8]) {
let mut list = vec![];
let length = buf.get_u16(); let length = buf.get_u16();
let mut list = Vec::with_capacity(length as usize);
for _ in 0..length { for _ in 0..length {
#[allow(unused_assignments)] #[allow(unused_assignments)]
let mut element = T::default(); let mut element = T::default();
@ -125,8 +126,9 @@ where
/// To be used by fixed sized types like `Vec<H256>`. /// To be used by fixed sized types like `Vec<H256>`.
fn specialized_from_compact(mut buf: &[u8], len: usize) -> (Self, &[u8]) { fn specialized_from_compact(mut buf: &[u8], len: usize) -> (Self, &[u8]) {
let mut list = vec![];
let length = buf.get_u16(); let length = buf.get_u16();
let mut list = Vec::with_capacity(length as usize);
for _ in 0..length { for _ in 0..length {
#[allow(unused_assignments)] #[allow(unused_assignments)]
let mut element = T::default(); let mut element = T::default();

View File

@ -12,7 +12,7 @@ description = "Staged syncing primitives used in reth."
reth-primitives = { path = "../../primitives" } reth-primitives = { path = "../../primitives" }
reth-interfaces = { path = "../../interfaces" } reth-interfaces = { path = "../../interfaces" }
reth-codecs = { path = "../codecs" } reth-codecs = { path = "../codecs" }
reth-libmdbx = { path = "../libmdbx-rs", optional = true } reth-libmdbx = { path = "../libmdbx-rs", optional = true , features = ["return-borrowed"] }
# codecs # codecs
serde = { version = "1.0.*", default-features = false } serde = { version = "1.0.*", default-features = false }

View File

@ -3,7 +3,7 @@ use crate::{
transaction::{DbTx, DbTxMut}, transaction::{DbTx, DbTxMut},
Error, Error,
}; };
use reth_primitives::bytes::Bytes;
use serde::Serialize; use serde::Serialize;
use std::{ use std::{
fmt::Debug, 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. /// Trait that will transform the data to be read from the DB.
pub trait Decompress: Send + Sync + Sized + Debug { pub trait Decompress: Send + Sync + Sized + Debug {
/// Decompresses data coming from the database. /// Decompresses data coming from the database.
fn decompress<B: Into<Bytes>>(value: B) -> Result<Self, Error>; fn decompress<B: AsRef<[u8]>>(value: B) -> Result<Self, Error>;
} }
/// Trait that will transform the data to be saved in the DB. /// 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. /// Trait that will transform the data to be read from the DB.
pub trait Decode: Send + Sync + Sized + Debug { pub trait Decode: Send + Sync + Sized + Debug {
/// Decodes data coming from the database. /// Decodes data coming from the database.
fn decode<B: Into<Bytes>>(value: B) -> Result<Self, Error>; fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, Error>;
} }
/// Generic trait that enforces the database key to implement [`Encode`] and [`Decode`]. /// Generic trait that enforces the database key to implement [`Encode`] and [`Decode`].

View File

@ -23,8 +23,8 @@ macro_rules! impl_compression_for_compact {
impl Decompress for $name impl Decompress for $name
{ {
fn decompress<B: Into<bytes::Bytes>>(value: B) -> Result<$name, Error> { fn decompress<B: AsRef<[u8]>>(value: B) -> Result<$name, Error> {
let value = value.into(); let value = value.as_ref();
let (obj, _) = Compact::from_compact(&value, value.len()); let (obj, _) = Compact::from_compact(&value, value.len());
Ok(obj) Ok(obj)
} }

View File

@ -2,7 +2,6 @@ use crate::{
table::{Compress, Decompress}, table::{Compress, Decompress},
Error, Error,
}; };
use parity_scale_codec::decode_from_bytes;
use reth_primitives::*; use reth_primitives::*;
mod sealed { mod sealed {
@ -27,8 +26,8 @@ impl<T> Decompress for T
where where
T: ScaleValue + parity_scale_codec::Decode + Sync + Send + std::fmt::Debug, T: ScaleValue + parity_scale_codec::Decode + Sync + Send + std::fmt::Debug,
{ {
fn decompress<B: Into<bytes::Bytes>>(value: B) -> Result<T, Error> { fn decompress<B: AsRef<[u8]>>(value: B) -> Result<T, Error> {
decode_from_bytes(value.into()).map_err(|_| Error::DecodeError) parity_scale_codec::Decode::decode(&mut value.as_ref()).map_err(|_| Error::DecodeError)
} }
} }

View File

@ -6,10 +6,7 @@ use crate::{
Error, Error,
}; };
use reth_codecs::Compact; use reth_codecs::Compact;
use reth_primitives::{ use reth_primitives::{bytes::BufMut, Account, Address, TransitionId};
bytes::{BufMut, Bytes},
Account, Address, TransitionId,
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Account as it is saved inside [`AccountChangeSet`][crate::tables::AccountChangeSet]. /// Account as it is saved inside [`AccountChangeSet`][crate::tables::AccountChangeSet].
@ -89,12 +86,10 @@ impl Encode for TransitionIdAddress {
} }
impl Decode for TransitionIdAddress { impl Decode for TransitionIdAddress {
fn decode<B: Into<Bytes>>(value: B) -> Result<Self, Error> { fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, Error> {
let value: Bytes = value.into(); let value = value.as_ref();
let num = u64::from_be_bytes(value[..8].try_into().map_err(|_| Error::DecodeError)?);
let num = let hash = Address::from_slice(&value[8..]);
u64::from_be_bytes(value.as_ref()[..8].try_into().map_err(|_| Error::DecodeError)?);
let hash = Address::from_slice(&value.slice(8..));
Ok(TransitionIdAddress((num, hash))) Ok(TransitionIdAddress((num, hash)))
} }
@ -121,7 +116,7 @@ mod test {
let encoded = Encode::encode(key); let encoded = Encode::encode(key);
assert_eq!(encoded, bytes); assert_eq!(encoded, bytes);
let decoded: TransitionIdAddress = Decode::decode(encoded.to_vec()).unwrap(); let decoded: TransitionIdAddress = Decode::decode(encoded).unwrap();
assert_eq!(decoded, key); assert_eq!(decoded, key);
} }

View File

@ -4,7 +4,7 @@ use crate::{
table::{Compress, Decompress}, table::{Compress, Decompress},
Error, Error,
}; };
use reth_primitives::{bytes::Bytes, IntegerList}; use reth_primitives::IntegerList;
impl Compress for IntegerList { impl Compress for IntegerList {
type Compressed = Vec<u8>; type Compressed = Vec<u8>;
@ -15,7 +15,7 @@ impl Compress for IntegerList {
} }
impl Decompress for IntegerList { impl Decompress for IntegerList {
fn decompress<B: Into<Bytes>>(value: B) -> Result<Self, Error> { fn decompress<B: AsRef<[u8]>>(value: B) -> Result<Self, Error> {
IntegerList::from_bytes(&value.into()).map_err(|_| Error::DecodeError) IntegerList::from_bytes(value.as_ref()).map_err(|_| Error::DecodeError)
} }
} }

View File

@ -14,7 +14,7 @@ use crate::{
table::{Decode, Encode}, table::{Decode, Encode},
Error, Error,
}; };
use reth_primitives::{bytes::Bytes, Address, H256}; use reth_primitives::{Address, H256};
/// Macro that implements [`Encode`] and [`Decode`] for uint types. /// Macro that implements [`Encode`] and [`Decode`] for uint types.
macro_rules! impl_uints { macro_rules! impl_uints {
@ -31,8 +31,7 @@ macro_rules! impl_uints {
impl Decode for $name impl Decode for $name
{ {
fn decode<B: Into<Bytes>>(value: B) -> Result<Self, Error> { fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, Error> {
let value: Bytes = value.into();
Ok( Ok(
$name::from_be_bytes( $name::from_be_bytes(
value.as_ref().try_into().map_err(|_| Error::DecodeError)? value.as_ref().try_into().map_err(|_| Error::DecodeError)?
@ -54,8 +53,8 @@ impl Encode for Vec<u8> {
} }
impl Decode for Vec<u8> { impl Decode for Vec<u8> {
fn decode<B: Into<Bytes>>(value: B) -> Result<Self, Error> { fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, Error> {
Ok(value.into().to_vec()) Ok(value.as_ref().to_vec())
} }
} }
@ -67,8 +66,8 @@ impl Encode for Address {
} }
impl Decode for Address { impl Decode for Address {
fn decode<B: Into<Bytes>>(value: B) -> Result<Self, Error> { fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, Error> {
Ok(Address::from_slice(&value.into()[..])) Ok(Address::from_slice(value.as_ref()))
} }
} }
impl Encode for H256 { impl Encode for H256 {
@ -79,8 +78,8 @@ impl Encode for H256 {
} }
impl Decode for H256 { impl Decode for H256 {
fn decode<B: Into<Bytes>>(value: B) -> Result<Self, Error> { fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, Error> {
Ok(H256::from_slice(&value.into()[..])) Ok(H256::from_slice(value.as_ref()))
} }
} }
@ -92,7 +91,7 @@ impl Encode for String {
} }
impl Decode for String { impl Decode for String {
fn decode<B: Into<Bytes>>(value: B) -> Result<Self, Error> { fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, Error> {
String::from_utf8(value.into().to_vec()).map_err(|_| Error::DecodeError) String::from_utf8(value.as_ref().to_vec()).map_err(|_| Error::DecodeError)
} }
} }

View File

@ -4,7 +4,7 @@ use crate::{
table::{Decode, Encode}, table::{Decode, Encode},
Error, Error,
}; };
use reth_primitives::{bytes::Bytes, TransitionId}; use reth_primitives::TransitionId;
/// Number of indices in one shard. /// Number of indices in one shard.
pub const NUM_OF_INDICES_IN_SHARD: usize = 100; pub const NUM_OF_INDICES_IN_SHARD: usize = 100;
@ -48,14 +48,14 @@ impl<T> Decode for ShardedKey<T>
where where
T: Decode, T: Decode,
{ {
fn decode<B: Into<Bytes>>(value: B) -> Result<Self, Error> { fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, Error> {
let value: Bytes = value.into(); let value = value.as_ref();
let tx_num_index = value.len() - 8; let tx_num_index = value.len() - 8;
let highest_tx_number = u64::from_be_bytes( let highest_tx_number =
value.as_ref()[tx_num_index..].try_into().map_err(|_| Error::DecodeError)?, u64::from_be_bytes(value[tx_num_index..].try_into().map_err(|_| Error::DecodeError)?);
); let key = T::decode(&value[..tx_num_index])?;
let key = T::decode(value.slice(..tx_num_index))?;
Ok(ShardedKey::new(key, highest_tx_number)) Ok(ShardedKey::new(key, highest_tx_number))
} }

View File

@ -4,7 +4,7 @@ use crate::{
table::{Decode, Encode}, table::{Decode, Encode},
Error, Error,
}; };
use reth_primitives::{bytes::Bytes, TransitionId, H160, H256}; use reth_primitives::{TransitionId, H160, H256};
use super::ShardedKey; use super::ShardedKey;
@ -44,15 +44,14 @@ impl Encode for StorageShardedKey {
} }
impl Decode for StorageShardedKey { impl Decode for StorageShardedKey {
fn decode<B: Into<Bytes>>(value: B) -> Result<Self, Error> { fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, Error> {
let value: Bytes = value.into(); let value = value.as_ref();
let tx_num_index = value.len() - 8; let tx_num_index = value.len() - 8;
let highest_tx_number = u64::from_be_bytes( let highest_tx_number =
value.as_ref()[tx_num_index..].try_into().map_err(|_| Error::DecodeError)?, u64::from_be_bytes(value[tx_num_index..].try_into().map_err(|_| Error::DecodeError)?);
); let address = H160::decode(&value[..20])?;
let address = H160::decode(value.slice(..20))?; let storage_key = H256::decode(&value[20..52])?;
let storage_key = H256::decode(value.slice(20..52))?;
Ok(Self { address, sharded_key: ShardedKey::new(storage_key, highest_tx_number) }) Ok(Self { address, sharded_key: ShardedKey::new(storage_key, highest_tx_number) })
} }

View File

@ -3,7 +3,7 @@ use crate::{
table::{Decode, Decompress, Table}, table::{Decode, Decompress, Table},
Error, Error,
}; };
use reth_primitives::bytes::Bytes;
use std::borrow::Cow; use std::borrow::Cow;
#[macro_export] #[macro_export]
@ -49,10 +49,15 @@ where
T::Key: Decode, T::Key: Decode,
T::Value: Decompress, T::Value: Decompress,
{ {
Ok(( let key = match kv.0 {
Decode::decode(Bytes::from(kv.0.into_owned()))?, Cow::Borrowed(k) => Decode::decode(k)?,
Decompress::decompress(Bytes::from(kv.1.into_owned()))?, 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. /// 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 where
T: Table, 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. /// Helper function to decode a value. It can be a key or subkey.
@ -68,5 +76,8 @@ pub(crate) fn decode_one<T>(value: Cow<'_, [u8]>) -> Result<T::Value, Error>
where where
T: Table, T: Table,
{ {
Decompress::decompress(Bytes::from(value.into_owned())) Ok(match value {
Cow::Borrowed(v) => Decompress::decompress(v)?,
Cow::Owned(v) => Decompress::decompress(v)?,
})
} }

View File

@ -23,6 +23,10 @@ ffi = { package = "reth-mdbx-sys", path = "./mdbx-sys" }
lifetimed-bytes = { version = "0.1", optional = true } lifetimed-bytes = { version = "0.1", optional = true }
[features]
default = []
return-borrowed = []
[dev-dependencies] [dev-dependencies]
pprof = { version = "0.11", features = ["flamegraph", "frame-pointer", "criterion"] } pprof = { version = "0.11", features = ["flamegraph", "frame-pointer", "criterion"] }
criterion = "0.4" criterion = "0.4"

View File

@ -1,4 +1,4 @@
use crate::{error::mdbx_result, Error, TransactionKind}; use crate::{Error, TransactionKind};
use derive_more::*; use derive_more::*;
use std::{borrow::Cow, slice}; use std::{borrow::Cow, slice};
@ -29,15 +29,27 @@ impl<'tx> TableObject<'tx> for Cow<'tx, [u8]> {
#[doc(hidden)] #[doc(hidden)]
unsafe fn decode_val<K: TransactionKind>( unsafe fn decode_val<K: TransactionKind>(
txn: *const ffi::MDBX_txn, _txn: *const ffi::MDBX_txn,
data_val: &ffi::MDBX_val, data_val: &ffi::MDBX_val,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
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); let s = slice::from_raw_parts(data_val.iov_base as *const u8, data_val.iov_len);
#[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) }) Ok(if is_dirty { Cow::Owned(s.to_vec()) } else { Cow::Borrowed(s) })
} }
}
} }
#[cfg(feature = "lifetimed-bytes")] #[cfg(feature = "lifetimed-bytes")]