I've always had a "Transmute is never the answer" policy for as long as I've been using Rust. But in this example benchmark, recreating the type is more than 100x slower.
When others have encountered this kind of case, what alternative approaches have worked?
Thank you for any thoughts.
bench fastest │ slowest │ median │ mean │ samples │ iters
├─ fast_bench │ │ │ │ │
│ ╰─ 10 6.903 µs │ 95.51 µs │ 8.773 µs │ 9.255 µs │ 5000 │ 5000
╰─ slow_bench │ │ │ │ │
╰─ 10 964.4 µs │ 4.591 ms │ 1.106 ms │ 1.172 ms │ 5000 │ 5000
enum MaybeOwned<'a> {
Borrowed(&'a [u8]),
Owned(Vec<u8>)
}
impl<'a> MaybeOwned<'a> {
fn ensure_owned(&mut self) {
match self {
Self::Owned(_) => {},
Self::Borrowed(slice) => {
*self = Self::Owned(slice.to_vec())
}
}
}
}
struct BigStruct<'a> {
maybe: MaybeOwned<'a>,
lots_o_data: [u8; 1048576],
}
fn ensure_owned_fast(mut input: Box<BigStruct>) -> Box<BigStruct<'static>> {
input.maybe.ensure_owned();
unsafe{ core::mem::transmute(input) }
}
fn ensure_owned_slow(input: Box<BigStruct>) -> Box<BigStruct<'static>> {
let v = match input.maybe {
MaybeOwned::Owned(v) => v,
MaybeOwned::Borrowed(slice) => slice.to_vec()
};
Box::new(BigStruct {
maybe: MaybeOwned::Owned(v),
lots_o_data: input.lots_o_data
})
}
#[divan::bench(args = [10])]
fn slow_bench(bencher: Bencher, n: usize) {
let local_vec = b"12345".to_vec();
bencher
.with_inputs(|| {
let mut inputs: Vec<Box<BigStruct>> = Vec::with_capacity(n);
for _ in 0..n {
let maybe = MaybeOwned::Borrowed(&local_vec);
inputs.push(Box::new(BigStruct {
maybe, lots_o_data: [0u8; 1048576]
}));
}
inputs
})
.bench_values(|mut inputs| {
for _ in 0..n {
let mut dest: Option<Box<BigStruct<'static>>> = None;
*black_box(&mut dest) = Some(ensure_owned_slow(inputs.pop().unwrap()));
}
});
}