Warning
This post was published 42 days ago. The information described in this article may have changed.
I have a trait that I want to use with frunk to generate trait impls automatically for a bunch of generated structs (see below, not linking to a playground since this requires quite a bit of unrelated infrastructure).
Functionally, this works fine, but is unfortunately significantly slower (as in 2x) compared to an implementation where I build an array of unsafe fn
pointers -- one per field -- that take a *const mut()
and decode a T
there (as unsafe as can be, but surprisingly faster than unrolled code from a dedicated proc macro).
Flamegraphs show a pyramid of <HCons<T,U> as ParseToGeneric>::parse_to_generic
calls (with literal T
and U
, though there are tons of functions like this in the binary, with different hashes appended) that I hoped #[inline(always)]
would flatten into a single function.
The structs have various numbers of fields, from 0 to 30-odd. I'm running benchmarks on one of the larger ones.
This is just about the hottest path I have, so I do care about performance. Is there a way to force the compiler to build one fully inlined function per <T,U>
? The function call overhead really hurts here.
trait ParseToGeneric<'a>: Sized {
fn parse_to_generic(
params: impl Iterator<Item = Result<&'a [u8], FromBytesError>>,
) -> Result<Self, PayloadFromBytesError>;
}
impl<'a, T, U> ParseToGeneric<'a> for HCons<T, U>
where
T: FromBytes<'a>,
U: ParseToGeneric<'a>,
{
#[inline(always)]
fn parse_to_generic(
mut params: impl Iterator<Item = Result<&'a [u8], FromBytesError>>,
) -> Result<Self, PayloadFromBytesError> {
let val = T::from_bytes(todo!())?;
Ok(HCons {
head: val,
tail: U::parse_to_generic(params)?,
})
}
}
impl<'a> ParseToGeneric<'a> for HNil {
#[inline(always)]
fn parse_to_generic(
_params: impl Iterator<Item = Result<&'a [u8], FromBytesError>>,
) -> Result<Self, PayloadFromBytesError> {
Ok(Self)
}
}
impl<'a, T> FromRawEvent<'a> for T
where
T: Generic,
T::Repr: ParseToGeneric<'a>,
{
fn parse(raw_event: &RawEvent<'a>) -> Result<Self, PayloadFromBytesError> {
let mut params = todo!();
let hlist = T::Repr::parse_to_generic(&mut params)?;
Ok(frunk::from_generic(hlist))
}
}
1 post - 1 participant
🏷️ rust_feed