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]) {
|
||||
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<H256>`.
|
||||
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();
|
||||
|
||||
@ -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 }
|
||||
|
||||
@ -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<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.
|
||||
@ -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<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`].
|
||||
|
||||
@ -23,8 +23,8 @@ macro_rules! impl_compression_for_compact {
|
||||
|
||||
impl Decompress for $name
|
||||
{
|
||||
fn decompress<B: Into<bytes::Bytes>>(value: B) -> Result<$name, Error> {
|
||||
let value = value.into();
|
||||
fn decompress<B: AsRef<[u8]>>(value: B) -> Result<$name, Error> {
|
||||
let value = value.as_ref();
|
||||
let (obj, _) = Compact::from_compact(&value, value.len());
|
||||
Ok(obj)
|
||||
}
|
||||
|
||||
@ -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<T> Decompress for T
|
||||
where
|
||||
T: ScaleValue + parity_scale_codec::Decode + Sync + Send + std::fmt::Debug,
|
||||
{
|
||||
fn decompress<B: Into<bytes::Bytes>>(value: B) -> Result<T, Error> {
|
||||
decode_from_bytes(value.into()).map_err(|_| Error::DecodeError)
|
||||
fn decompress<B: AsRef<[u8]>>(value: B) -> Result<T, Error> {
|
||||
parity_scale_codec::Decode::decode(&mut value.as_ref()).map_err(|_| Error::DecodeError)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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<B: Into<Bytes>>(value: B) -> Result<Self, Error> {
|
||||
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<B: AsRef<[u8]>>(value: B) -> Result<Self, Error> {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@ -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<u8>;
|
||||
@ -15,7 +15,7 @@ impl Compress for IntegerList {
|
||||
}
|
||||
|
||||
impl Decompress for IntegerList {
|
||||
fn decompress<B: Into<Bytes>>(value: B) -> Result<Self, Error> {
|
||||
IntegerList::from_bytes(&value.into()).map_err(|_| Error::DecodeError)
|
||||
fn decompress<B: AsRef<[u8]>>(value: B) -> Result<Self, Error> {
|
||||
IntegerList::from_bytes(value.as_ref()).map_err(|_| Error::DecodeError)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<B: Into<Bytes>>(value: B) -> Result<Self, Error> {
|
||||
let value: Bytes = value.into();
|
||||
fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, Error> {
|
||||
Ok(
|
||||
$name::from_be_bytes(
|
||||
value.as_ref().try_into().map_err(|_| Error::DecodeError)?
|
||||
@ -54,8 +53,8 @@ impl Encode for Vec<u8> {
|
||||
}
|
||||
|
||||
impl Decode for Vec<u8> {
|
||||
fn decode<B: Into<Bytes>>(value: B) -> Result<Self, Error> {
|
||||
Ok(value.into().to_vec())
|
||||
fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, Error> {
|
||||
Ok(value.as_ref().to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,8 +66,8 @@ impl Encode for Address {
|
||||
}
|
||||
|
||||
impl Decode for Address {
|
||||
fn decode<B: Into<Bytes>>(value: B) -> Result<Self, Error> {
|
||||
Ok(Address::from_slice(&value.into()[..]))
|
||||
fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, Error> {
|
||||
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<B: Into<Bytes>>(value: B) -> Result<Self, Error> {
|
||||
Ok(H256::from_slice(&value.into()[..]))
|
||||
fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, Error> {
|
||||
Ok(H256::from_slice(value.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,7 +91,7 @@ impl Encode for String {
|
||||
}
|
||||
|
||||
impl Decode for String {
|
||||
fn decode<B: Into<Bytes>>(value: B) -> Result<Self, Error> {
|
||||
String::from_utf8(value.into().to_vec()).map_err(|_| Error::DecodeError)
|
||||
fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, Error> {
|
||||
String::from_utf8(value.as_ref().to_vec()).map_err(|_| Error::DecodeError)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<T> Decode for ShardedKey<T>
|
||||
where
|
||||
T: Decode,
|
||||
{
|
||||
fn decode<B: Into<Bytes>>(value: B) -> Result<Self, Error> {
|
||||
let value: Bytes = value.into();
|
||||
fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, Error> {
|
||||
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))
|
||||
}
|
||||
|
||||
@ -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<B: Into<Bytes>>(value: B) -> Result<Self, Error> {
|
||||
let value: Bytes = value.into();
|
||||
fn decode<B: AsRef<[u8]>>(value: B) -> Result<Self, Error> {
|
||||
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) })
|
||||
}
|
||||
|
||||
@ -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<T>(value: Cow<'_, [u8]>) -> Result<T::Value, Error>
|
||||
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)?,
|
||||
})
|
||||
}
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::{error::mdbx_result, Error, TransactionKind};
|
||||
use crate::{Error, TransactionKind};
|
||||
use derive_more::*;
|
||||
use std::{borrow::Cow, slice};
|
||||
|
||||
@ -29,15 +29,27 @@ impl<'tx> TableObject<'tx> for Cow<'tx, [u8]> {
|
||||
|
||||
#[doc(hidden)]
|
||||
unsafe fn decode_val<K: TransactionKind>(
|
||||
txn: *const ffi::MDBX_txn,
|
||||
_txn: *const ffi::MDBX_txn,
|
||||
data_val: &ffi::MDBX_val,
|
||||
) -> 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);
|
||||
|
||||
#[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) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "lifetimed-bytes")]
|
||||
|
||||
Reference in New Issue
Block a user