mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
perf(db): decode and decompress directly from borrowed data instead (#2050)
This commit is contained in:
@ -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();
|
||||||
|
|||||||
@ -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 }
|
||||||
|
|||||||
@ -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`].
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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) })
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)?,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
@ -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,16 +29,28 @@ 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")]
|
||||||
impl<'tx> TableObject<'tx> for lifetimed_bytes::Bytes<'tx> {
|
impl<'tx> TableObject<'tx> for lifetimed_bytes::Bytes<'tx> {
|
||||||
|
|||||||
Reference in New Issue
Block a user